2bf8478639
- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑 - 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口 - 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界 - 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名 - 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验 - 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口 - 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
136 lines
5.8 KiB
Go
136 lines
5.8 KiB
Go
package basic
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestSolarEclipseLocalEphemerisBoundedError(t *testing.T) {
|
|
events := []struct {
|
|
name string
|
|
seed float64
|
|
}{
|
|
{"2010-01-15", JDECalc(2010, 1, 15)},
|
|
{"2014-04-29", JDECalc(2014, 4, 29)},
|
|
{"2023-04-20", JDECalc(2023, 4, 20)},
|
|
{"2031-05-21", JDECalc(2031, 5, 21)},
|
|
{"2309-06-09", JDECalc(2309, 6, 9)},
|
|
}
|
|
for _, event := range events {
|
|
t.Run(event.name, func(t *testing.T) {
|
|
center := CalcMoonSHByJDE(event.seed, 0)
|
|
ephemeris := newSolarEclipseLocalEphemeris(center)
|
|
for offset := -4.5; offset <= 4.5; offset += 0.25 {
|
|
jd := center + offset/24
|
|
approxSun, approxMoon, ok := ephemeris.equatorialAt(jd)
|
|
if !ok {
|
|
t.Fatalf("interpolator rejected in-window time %.3fh", offset)
|
|
}
|
|
exactSun, exactMoon := solarEclipseSunMoonEquatorial(jd)
|
|
for name, pair := range map[string][2][3]float64{
|
|
"sun": {solarEclipseLLRToXYZ(approxSun[0], approxSun[1], approxSun[2]), solarEclipseLLRToXYZ(exactSun[0], exactSun[1], exactSun[2])},
|
|
"moon": {solarEclipseLLRToXYZ(approxMoon[0], approxMoon[1], approxMoon[2]), solarEclipseLLRToXYZ(exactMoon[0], exactMoon[1], exactMoon[2])},
|
|
} {
|
|
if errorKM := vectorDifferenceKM(pair[0], pair[1]); errorKM > 1 {
|
|
t.Fatalf("%s interpolation error at %.3fh = %.6f km", name, offset, errorKM)
|
|
}
|
|
}
|
|
}
|
|
if _, _, ok := ephemeris.equatorialAt(center + 6.1/24); ok {
|
|
t.Fatal("interpolator should reject times outside its bounded window")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOccultationLocalEphemerisBoundedError(t *testing.T) {
|
|
star := hr4799OccultationCoordinateForTest()
|
|
starTT := occultationTimeToTT(time.Date(2025, 6, 5, 20, 0, 0, 0, time.UTC))
|
|
starEphemeris := newStarOccultationLocalEphemeris(starTT, star)
|
|
for offset := -4.5; offset <= 4.5; offset += 0.25 {
|
|
jd := starTT + offset/24
|
|
got, ok := starEphemeris.stateAt(jd)
|
|
if !ok {
|
|
t.Fatalf("star interpolator rejected in-window time %.3fh", offset)
|
|
}
|
|
want := starOccultationEphemerisStateAt(jd, star)
|
|
if difference := angularSeparationDegrees(got.moonRA, got.moonDec, want.moonRA, want.moonDec); difference > 1e-5 {
|
|
t.Fatalf("moon direction interpolation error at %.3fh = %.9f deg", offset, difference)
|
|
}
|
|
if difference := angularSeparationDegrees(got.starRA, got.starDec, want.starRA, want.starDec); difference > 1e-5 {
|
|
t.Fatalf("star direction interpolation error at %.3fh = %.9f deg", offset, difference)
|
|
}
|
|
}
|
|
|
|
config, ok := planetOccultationConfigFor(OccultationSaturn)
|
|
if !ok {
|
|
t.Fatal("Saturn occultation config is unavailable")
|
|
}
|
|
planetTT := occultationTimeToTT(time.Date(2024, 8, 21, 2, 40, 0, 0, time.UTC))
|
|
planetEphemeris := newPlanetOccultationLocalEphemeris(planetTT, config)
|
|
for offset := -4.5; offset <= 4.5; offset += 0.25 {
|
|
jd := planetTT + offset/24
|
|
got, ok := planetEphemeris.stateAt(jd)
|
|
if !ok {
|
|
t.Fatalf("planet interpolator rejected in-window time %.3fh", offset)
|
|
}
|
|
want := planetOccultationEphemerisStateAt(jd, config)
|
|
if difference := angularSeparationDegrees(got.moonRA, got.moonDec, want.moonRA, want.moonDec); difference > 1e-5 {
|
|
t.Fatalf("moon direction interpolation error at %.3fh = %.9f deg", offset, difference)
|
|
}
|
|
if difference := angularSeparationDegrees(got.planetRA, got.planetDec, want.planetRA, want.planetDec); difference > 1e-5 {
|
|
t.Fatalf("planet direction interpolation error at %.3fh = %.9f deg", offset, difference)
|
|
}
|
|
if math.Abs(got.planetDistanceKM-want.planetDistanceKM) > 100 {
|
|
t.Fatalf("planet distance interpolation error at %.3fh = %.6f km", offset, math.Abs(got.planetDistanceKM-want.planetDistanceKM))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOccultationLocalEphemerisFullWindowBoundedError(t *testing.T) {
|
|
star := hr4799OccultationCoordinateForTest()
|
|
starTT := occultationTimeToTT(time.Date(2025, 6, 5, 20, 0, 0, 0, time.UTC))
|
|
starEphemeris := newStarOccultationLocalEphemeris(starTT, star)
|
|
config, ok := planetOccultationConfigFor(OccultationSaturn)
|
|
if !ok {
|
|
t.Fatal("Saturn occultation config is unavailable")
|
|
}
|
|
planetTT := occultationTimeToTT(time.Date(2024, 8, 21, 2, 40, 0, 0, time.UTC))
|
|
planetEphemeris := newPlanetOccultationLocalEphemeris(planetTT, config)
|
|
for _, offset := range []float64{-47.5, -40, -24, 24, 40, 47.5} {
|
|
starJD := starTT + offset/24
|
|
gotStar, starOK := starEphemeris.stateAt(starJD)
|
|
wantStar := starOccultationEphemerisStateAt(starJD, star)
|
|
if !starOK {
|
|
t.Fatalf("star interpolator rejected in-window time %.3fh", offset)
|
|
}
|
|
if difference := angularSeparationDegrees(gotStar.moonRA, gotStar.moonDec, wantStar.moonRA, wantStar.moonDec); difference > 1e-5 {
|
|
t.Fatalf("star-event moon direction error at %.3fh = %.9f deg", offset, difference)
|
|
}
|
|
if difference := angularSeparationDegrees(gotStar.starRA, gotStar.starDec, wantStar.starRA, wantStar.starDec); difference > 1e-5 {
|
|
t.Fatalf("star direction error at %.3fh = %.9f deg", offset, difference)
|
|
}
|
|
|
|
planetJD := planetTT + offset/24
|
|
gotPlanet, planetOK := planetEphemeris.stateAt(planetJD)
|
|
wantPlanet := planetOccultationEphemerisStateAt(planetJD, config)
|
|
if !planetOK {
|
|
t.Fatalf("planet interpolator rejected in-window time %.3fh", offset)
|
|
}
|
|
if difference := angularSeparationDegrees(gotPlanet.moonRA, gotPlanet.moonDec, wantPlanet.moonRA, wantPlanet.moonDec); difference > 1e-5 {
|
|
t.Fatalf("planet-event moon direction error at %.3fh = %.9f deg", offset, difference)
|
|
}
|
|
if difference := angularSeparationDegrees(gotPlanet.planetRA, gotPlanet.planetDec, wantPlanet.planetRA, wantPlanet.planetDec); difference > 1e-5 {
|
|
t.Fatalf("planet direction error at %.3fh = %.9f deg", offset, difference)
|
|
}
|
|
if difference := math.Abs(gotPlanet.planetDistanceKM - wantPlanet.planetDistanceKM); difference > 100 {
|
|
t.Fatalf("planet distance error at %.3fh = %.6f km", offset, difference)
|
|
}
|
|
}
|
|
}
|
|
|
|
func vectorDifferenceKM(a, b [3]float64) float64 {
|
|
return math.Sqrt((a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]) + (a[2]-b[2])*(a[2]-b[2]))
|
|
}
|