141 lines
5.2 KiB
Go
141 lines
5.2 KiB
Go
|
|
// 被测契约:通用天象搜索与各行星专用事件一致,并能找到大合与行星近星。
|
|||
|
|
package basic
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"math"
|
|||
|
|
"testing"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"b612.me/astro/internal/geodata"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func TestSkyAspectMatchesMarsOpposition(t *testing.T) {
|
|||
|
|
start := 2460000.5
|
|||
|
|
// 专用函数返回 UT。
|
|||
|
|
want := UTC2TT(NextMarsOppositionWithInstance(start, nil))
|
|||
|
|
got := SkyAspectCrossingsWithInstance(SkyMars, SkySun, 180, start, start+800, nil)
|
|||
|
|
if len(got) == 0 || math.Abs(got[0].JDE-want)*86400 > 1 {
|
|||
|
|
t.Fatalf("opposition %v, want %.6f", got, want)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 专用函数按赤经留且返回 UT,通用搜索按黄经留,二者相差数小时属口径不同;这里只核对黄经速度确实变号。
|
|||
|
|
func TestSkyStationsFlipLongitudeRate(t *testing.T) {
|
|||
|
|
got := SkyStationsWithInstance(SkyMars, 2460000.5, 2460800.5, nil)
|
|||
|
|
if len(got) != 2 {
|
|||
|
|
t.Fatalf("stations %v", got)
|
|||
|
|
}
|
|||
|
|
for _, e := range got {
|
|||
|
|
before, _ := SkyBodyApparentLoBoWithInstance(SkyMars, e.JDE-1, nil)
|
|||
|
|
at, _ := SkyBodyApparentLoBoWithInstance(SkyMars, e.JDE, nil)
|
|||
|
|
after, _ := SkyBodyApparentLoBoWithInstance(SkyMars, e.JDE+1, nil)
|
|||
|
|
if geodata.NormalizeLongitude(at-before)*geodata.NormalizeLongitude(after-at) >= 0 {
|
|||
|
|
t.Errorf("no rate flip at %.5f", e.JDE)
|
|||
|
|
}
|
|||
|
|
ut := UTC2TT(NextMarsProgradeToRetrogradeWithInstance(e.JDE-30, nil))
|
|||
|
|
if math.Abs(ut-e.JDE) > 5 && math.Abs(UTC2TT(NextMarsRetrogradeToProgradeWithInstance(e.JDE-30, nil))-e.JDE) > 5 {
|
|||
|
|
t.Errorf("station %.5f far from RA stations", e.JDE)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSkyGreatConjunction2020(t *testing.T) {
|
|||
|
|
got := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, 2459100.5, 2459300.5, nil)
|
|||
|
|
if len(got) != 1 {
|
|||
|
|
t.Fatalf("minima %v", got)
|
|||
|
|
}
|
|||
|
|
when := JD2Date(TT2UTC(got[0].JDE))
|
|||
|
|
if when.Year() != 2020 || when.Month() != time.December || when.Day() < 21 || when.Day() > 22 || got[0].Value > 0.11 || got[0].Value < 0.09 {
|
|||
|
|
t.Errorf("great conjunction at %v separation %.4f°", when, got[0].Value)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSkyStarApproachRegulus(t *testing.T) {
|
|||
|
|
regulus := StarCoordinate{ID: "Regulus", RA: 152.09296, Dec: 11.96721, Epoch: time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), Frame: CoordinateFrameJ2000}
|
|||
|
|
got := SkyStarSeparationMinimaWithInstance(SkyVenus, regulus, 1.5, 2460400.5, 2460600.5, nil)
|
|||
|
|
if len(got) == 0 {
|
|||
|
|
t.Fatal("no Venus–Regulus approach")
|
|||
|
|
}
|
|||
|
|
for _, e := range got {
|
|||
|
|
if e.Value > 1.5 || e.Value < 0 {
|
|||
|
|
t.Errorf("approach %.4f° at %.5f", e.Value, e.JDE)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSkySeparationMinimaFindsNarrowWindow(t *testing.T) {
|
|||
|
|
wide := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, 2459190, 2459220, nil)
|
|||
|
|
if len(wide) == 0 {
|
|||
|
|
t.Fatal("wide window returned no minimum")
|
|||
|
|
}
|
|||
|
|
center := wide[0].JDE
|
|||
|
|
for _, halfWidth := range []float64{0.1, 0.25, 0.49, 0.99, 1.5, 3} {
|
|||
|
|
got := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, center-halfWidth, center+halfWidth, nil)
|
|||
|
|
if len(got) == 0 {
|
|||
|
|
t.Errorf("halfWidth %v: no minimum", halfWidth)
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
if math.Abs(got[0].JDE-center) > 1e-4 {
|
|||
|
|
t.Errorf("halfWidth %v: JDE = %.6f, want %.6f", halfWidth, got[0].JDE, center)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSkySeparationMinimaFindsEdgeOfWindow(t *testing.T) {
|
|||
|
|
wide := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, 2459190, 2459220, nil)
|
|||
|
|
if len(wide) == 0 {
|
|||
|
|
t.Fatal("wide window returned no minimum")
|
|||
|
|
}
|
|||
|
|
center := wide[0].JDE
|
|||
|
|
windows := [][2]float64{
|
|||
|
|
{center - 1.0/1440, center + 3.0/24},
|
|||
|
|
{center - 3.0/24, center + 1.0/1440},
|
|||
|
|
{center - 1.0/24, center + 1.0/24},
|
|||
|
|
}
|
|||
|
|
for _, window := range windows {
|
|||
|
|
got := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, window[0], window[1], nil)
|
|||
|
|
if len(got) != 1 || math.Abs(got[0].JDE-center) > 1e-4 {
|
|||
|
|
t.Errorf("window [%+.4f, %+.4f]: got %+v, want one minimum at %.6f", window[0]-center, window[1]-center, got, center)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if got := SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, center+0.5, center+3, nil); len(got) != 0 {
|
|||
|
|
t.Errorf("window after the minimum: got %+v, want none", got)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSkyScansTerminateOnDegenerateWindows(t *testing.T) {
|
|||
|
|
start := 2459205.26539
|
|||
|
|
windows := [][2]float64{
|
|||
|
|
{start, math.Nextafter(start, math.Inf(1))},
|
|||
|
|
{start, start + 26e-6/86400},
|
|||
|
|
{start, math.Inf(1)},
|
|||
|
|
{math.Inf(-1), start},
|
|||
|
|
{math.NaN(), start},
|
|||
|
|
}
|
|||
|
|
for _, window := range windows {
|
|||
|
|
SkySeparationMinimaWithInstance(SkyJupiter, SkySaturn, 1, window[0], window[1], nil)
|
|||
|
|
SkyLongitudeCrossingsWithInstance(SkySun, 280, window[0], window[1], nil)
|
|||
|
|
SkyAspectCrossingsWithInstance(SkyJupiter, SkySaturn, 0, window[0], window[1], nil)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSkyGridRejectsLimitInclusive(t *testing.T) {
|
|||
|
|
if _, _, ok := skyGrid(0, float64(skyGridMaxIntervals), 1, 1); ok {
|
|||
|
|
t.Error("grid at limit accepted")
|
|||
|
|
}
|
|||
|
|
if count, _, ok := skyGrid(0, float64(skyGridMaxIntervals-1), 1, 1); !ok || count != skyGridMaxIntervals-1 {
|
|||
|
|
t.Errorf("grid below limit: count=%d ok=%v", count, ok)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func TestSkyLongitudeCrossingsIncludesClosedEndpoints(t *testing.T) {
|
|||
|
|
const jde = 2451545.0
|
|||
|
|
lo, _ := SkyBodyApparentLoBoWithInstance(SkySun, jde, nil)
|
|||
|
|
for _, window := range [][2]float64{{jde - 2, jde}, {jde, jde + 2}} {
|
|||
|
|
got := SkyLongitudeCrossingsWithInstance(SkySun, lo, window[0], window[1], nil)
|
|||
|
|
if len(got) != 1 || math.Abs(got[0].JDE-jde) > 1e-5 {
|
|||
|
|
t.Errorf("window [%v, %v]: got %+v, want one crossing at %v", window[0], window[1], got, jde)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|