feat: 新增月掩与日月食地理绘图并提升观测计算精度
- 新增月掩恒星和行星:支持搜索、掩甚点、全球掩带及固定地点轨迹计算 - 支持恒星星表坐标转换、有限盘面行星接触事件和月掩 SVG 输出 - 新增日月食及月掩全球投影图、时间标记和 GeoJSON 地理数据接口 - 扩展日食中心线、南北界及偏食足迹采样,支持极区投影 - 修正站心时角、月出月落、月球视半径、折射和恒星自行计算 - 优化内外行星事件搜索、边界选择、极端输入处理和计算稳定性
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPlanetOccultationDiagramSaturnIncludesFiveDynamicDiskStages(t *testing.T) {
|
||||
location := time.FixedZone("UTC+8", 8*3600)
|
||||
start := time.Date(2025, time.February, 1, 0, 0, 0, 0, location)
|
||||
end := start.Add(24 * time.Hour)
|
||||
events, err := FindPlanetOccultations(
|
||||
start, end, OccultationSaturn, 104.52219613, 55.25401991, 0, OccultationSearchOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("FindPlanetOccultations() error = %v", err)
|
||||
}
|
||||
if len(events) != 1 {
|
||||
t.Fatalf("FindPlanetOccultations() returned %d events, want 1", len(events))
|
||||
}
|
||||
diagram := PlanetOccultationDiagram(events[0], PlanetOccultationDiagramOptions{})
|
||||
if len(diagram.Frames) < 5 {
|
||||
t.Fatalf("PlanetOccultationDiagram() returned %d frames, want at least 5", len(diagram.Frames))
|
||||
}
|
||||
|
||||
frames := make(map[string]PlanetOccultationDiagramFrame)
|
||||
for _, frame := range diagram.Frames {
|
||||
for _, label := range frame.Labels {
|
||||
frames[label] = frame
|
||||
}
|
||||
}
|
||||
for _, label := range []string{"C1", "C2", "Greatest", "C3", "C4"} {
|
||||
if _, ok := frames[label]; !ok {
|
||||
t.Fatalf("diagram is missing %s", label)
|
||||
}
|
||||
}
|
||||
for _, label := range []string{"C1", "C4"} {
|
||||
frame := frames[label]
|
||||
residual := frame.SeparationArcsec - frame.MoonRadiusArcsec - frame.PlanetRadiusArcsec
|
||||
if math.Abs(residual) > 0.1 {
|
||||
t.Errorf("%s outer-contact residual = %.6f arcsec", label, residual)
|
||||
}
|
||||
}
|
||||
for _, label := range []string{"C2", "C3"} {
|
||||
frame := frames[label]
|
||||
residual := frame.SeparationArcsec - frame.MoonRadiusArcsec + frame.PlanetRadiusArcsec
|
||||
if math.Abs(residual) > 0.1 {
|
||||
t.Errorf("%s inner-contact residual = %.6f arcsec", label, residual)
|
||||
}
|
||||
}
|
||||
if !frames["Greatest"].FullyOcculted {
|
||||
t.Fatal("greatest frame does not report a fully occulted Saturn disk")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanetOccultationDiagramRejectsIncompleteEvent(t *testing.T) {
|
||||
result := PlanetOccultationDiagram(PlanetOccultationInfo{Planet: OccultationSaturn}, PlanetOccultationDiagramOptions{})
|
||||
if len(result.Frames) != 0 {
|
||||
t.Fatalf("invalid event returned %d frames, want none", len(result.Frames))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanetOccultationDiagramRejectsTargetGeometryMismatch(t *testing.T) {
|
||||
location := time.FixedZone("UTC+8", 8*3600)
|
||||
events, err := FindPlanetOccultations(
|
||||
time.Date(2025, time.February, 1, 0, 0, 0, 0, location),
|
||||
time.Date(2025, time.February, 2, 0, 0, 0, 0, location),
|
||||
OccultationSaturn, 104.52219613, 55.25401991, 0, OccultationSearchOptions{},
|
||||
)
|
||||
if err != nil || len(events) != 1 {
|
||||
t.Fatalf("FindPlanetOccultations() = %d events, %v; want one", len(events), err)
|
||||
}
|
||||
// TargetID 只是显示元数据,但改变建模行星必须 / TargetID is display metadata, but changing the modeled planet must
|
||||
// 使事件几何失效,而不是静默绘制另一个天体 / invalidate the event geometry rather than silently drawing another body.
|
||||
event := events[0]
|
||||
event.Planet = OccultationVenus
|
||||
event.TargetID = "Venus"
|
||||
result := PlanetOccultationDiagram(event, PlanetOccultationDiagramOptions{})
|
||||
if len(result.Frames) != 0 {
|
||||
t.Fatalf("diagram accepted mismatched planet and returned %d frames", len(result.Frames))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanetOccultationDiagramPartialEventHasOnlyExternalStages(t *testing.T) {
|
||||
start := time.Date(2024, time.August, 21, 1, 30, 0, 0, time.UTC)
|
||||
end := time.Date(2024, time.August, 21, 4, 0, 0, 0, time.UTC)
|
||||
events, err := FindPlanetOccultations(
|
||||
start, end, OccultationSaturn, -30.072, -6.5, 0, OccultationSearchOptions{},
|
||||
)
|
||||
if err != nil || len(events) != 1 {
|
||||
t.Fatalf("FindPlanetOccultations() = %d events, %v; want one", len(events), err)
|
||||
}
|
||||
if events[0].Type != OccultationPartial {
|
||||
t.Fatalf("event type = %q, want partial", events[0].Type)
|
||||
}
|
||||
diagram := PlanetOccultationDiagram(events[0], PlanetOccultationDiagramOptions{})
|
||||
labels := make(map[string]bool)
|
||||
for _, frame := range diagram.Frames {
|
||||
for _, label := range frame.Labels {
|
||||
labels[label] = true
|
||||
}
|
||||
}
|
||||
for _, label := range []string{"C1", "Greatest", "C4"} {
|
||||
if !labels[label] {
|
||||
t.Fatalf("partial diagram is missing %s", label)
|
||||
}
|
||||
}
|
||||
if labels["C2"] || labels["C3"] {
|
||||
t.Fatalf("partial diagram contains internal contacts: %+v", labels)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user