208 lines
6.9 KiB
Go
208 lines
6.9 KiB
Go
|
|
package basic
|
||
|
|
|
||
|
|
import (
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
type stationEvent struct {
|
||
|
|
when time.Time
|
||
|
|
kind string
|
||
|
|
}
|
||
|
|
|
||
|
|
type stationTruthCase struct {
|
||
|
|
name string
|
||
|
|
events []stationEvent
|
||
|
|
lastR2P func(float64) float64
|
||
|
|
nextR2P func(float64) float64
|
||
|
|
lastP2R func(float64) float64
|
||
|
|
nextP2R func(float64) float64
|
||
|
|
}
|
||
|
|
|
||
|
|
func mustJST(value string) time.Time {
|
||
|
|
loc := time.FixedZone("JST", 9*3600)
|
||
|
|
t, err := time.ParseInLocation("2006-01-02 15:04", value, loc)
|
||
|
|
if err != nil {
|
||
|
|
panic(err)
|
||
|
|
}
|
||
|
|
return t
|
||
|
|
}
|
||
|
|
|
||
|
|
func toUTJD(t time.Time) float64 {
|
||
|
|
return TD2UT(Date2JDE(t.UTC()), true)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestStationTruthAgainstNAOJ(t *testing.T) {
|
||
|
|
cases := []stationTruthCase{
|
||
|
|
{
|
||
|
|
name: "Mars",
|
||
|
|
events: []stationEvent{
|
||
|
|
{when: mustJST("2024-12-08 05:59"), kind: "P2R"},
|
||
|
|
{when: mustJST("2025-01-16 11:39"), kind: "OPP"},
|
||
|
|
{when: mustJST("2025-02-24 18:35"), kind: "R2P"},
|
||
|
|
{when: mustJST("2027-01-12 01:10"), kind: "P2R"},
|
||
|
|
{when: mustJST("2027-02-20 00:51"), kind: "OPP"},
|
||
|
|
{when: mustJST("2027-04-03 02:33"), kind: "R2P"},
|
||
|
|
},
|
||
|
|
lastR2P: LastMarsRetrogradeToPrograde,
|
||
|
|
nextR2P: NextMarsRetrogradeToPrograde,
|
||
|
|
lastP2R: LastMarsProgradeToRetrograde,
|
||
|
|
nextP2R: NextMarsProgradeToRetrograde,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "Jupiter",
|
||
|
|
events: []stationEvent{
|
||
|
|
{when: mustJST("2024-10-09 16:13"), kind: "P2R"},
|
||
|
|
{when: mustJST("2024-12-08 05:58"), kind: "OPP"},
|
||
|
|
{when: mustJST("2025-02-04 22:07"), kind: "R2P"},
|
||
|
|
{when: mustJST("2025-11-12 04:54"), kind: "P2R"},
|
||
|
|
{when: mustJST("2026-01-10 17:42"), kind: "OPP"},
|
||
|
|
{when: mustJST("2026-03-11 11:44"), kind: "R2P"},
|
||
|
|
{when: mustJST("2026-12-13 21:03"), kind: "P2R"},
|
||
|
|
{when: mustJST("2027-02-11 09:29"), kind: "OPP"},
|
||
|
|
{when: mustJST("2027-04-13 15:17"), kind: "R2P"},
|
||
|
|
},
|
||
|
|
lastR2P: LastJupiterRetrogradeToPrograde,
|
||
|
|
nextR2P: NextJupiterRetrogradeToPrograde,
|
||
|
|
lastP2R: LastJupiterProgradeToRetrograde,
|
||
|
|
nextP2R: NextJupiterProgradeToRetrograde,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "Saturn",
|
||
|
|
events: []stationEvent{
|
||
|
|
{when: mustJST("2024-07-01 06:15"), kind: "P2R"},
|
||
|
|
{when: mustJST("2024-09-08 13:35"), kind: "OPP"},
|
||
|
|
{when: mustJST("2024-11-16 14:57"), kind: "R2P"},
|
||
|
|
{when: mustJST("2025-07-14 16:57"), kind: "P2R"},
|
||
|
|
{when: mustJST("2025-09-21 14:46"), kind: "OPP"},
|
||
|
|
{when: mustJST("2025-11-29 09:35"), kind: "R2P"},
|
||
|
|
{when: mustJST("2026-07-28 08:09"), kind: "P2R"},
|
||
|
|
{when: mustJST("2026-10-04 21:29"), kind: "OPP"},
|
||
|
|
{when: mustJST("2026-12-12 08:21"), kind: "R2P"},
|
||
|
|
{when: mustJST("2027-08-11 02:53"), kind: "P2R"},
|
||
|
|
{when: mustJST("2027-10-18 09:36"), kind: "OPP"},
|
||
|
|
{when: mustJST("2027-12-25 12:05"), kind: "R2P"},
|
||
|
|
},
|
||
|
|
lastR2P: LastSaturnRetrogradeToPrograde,
|
||
|
|
nextR2P: NextSaturnRetrogradeToPrograde,
|
||
|
|
lastP2R: LastSaturnProgradeToRetrograde,
|
||
|
|
nextP2R: NextSaturnProgradeToRetrograde,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "Uranus",
|
||
|
|
events: []stationEvent{
|
||
|
|
{when: mustJST("2024-01-27 19:50"), kind: "R2P"},
|
||
|
|
{when: mustJST("2024-09-02 00:44"), kind: "P2R"},
|
||
|
|
{when: mustJST("2024-11-17 11:45"), kind: "OPP"},
|
||
|
|
{when: mustJST("2025-01-31 04:04"), kind: "R2P"},
|
||
|
|
{when: mustJST("2025-09-06 13:55"), kind: "P2R"},
|
||
|
|
{when: mustJST("2025-11-21 21:25"), kind: "OPP"},
|
||
|
|
{when: mustJST("2026-02-04 13:37"), kind: "R2P"},
|
||
|
|
{when: mustJST("2026-09-11 03:19"), kind: "P2R"},
|
||
|
|
{when: mustJST("2026-11-26 07:41"), kind: "OPP"},
|
||
|
|
{when: mustJST("2027-02-08 23:03"), kind: "R2P"},
|
||
|
|
{when: mustJST("2027-09-15 17:50"), kind: "P2R"},
|
||
|
|
{when: mustJST("2027-11-30 18:22"), kind: "OPP"},
|
||
|
|
},
|
||
|
|
lastR2P: LastUranusRetrogradeToPrograde,
|
||
|
|
nextR2P: NextUranusRetrogradeToPrograde,
|
||
|
|
lastP2R: LastUranusProgradeToRetrograde,
|
||
|
|
nextP2R: NextUranusProgradeToRetrograde,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: "Neptune",
|
||
|
|
events: []stationEvent{
|
||
|
|
{when: mustJST("2024-07-03 12:08"), kind: "P2R"},
|
||
|
|
{when: mustJST("2024-09-21 09:17"), kind: "OPP"},
|
||
|
|
{when: mustJST("2024-12-08 20:05"), kind: "R2P"},
|
||
|
|
{when: mustJST("2025-07-05 23:30"), kind: "P2R"},
|
||
|
|
{when: mustJST("2025-09-23 21:54"), kind: "OPP"},
|
||
|
|
{when: mustJST("2025-12-11 09:21"), kind: "R2P"},
|
||
|
|
{when: mustJST("2026-07-08 13:02"), kind: "P2R"},
|
||
|
|
{when: mustJST("2026-09-26 10:36"), kind: "OPP"},
|
||
|
|
{when: mustJST("2026-12-13 19:47"), kind: "R2P"},
|
||
|
|
{when: mustJST("2027-07-11 01:06"), kind: "P2R"},
|
||
|
|
{when: mustJST("2027-09-28 23:19"), kind: "OPP"},
|
||
|
|
{when: mustJST("2027-12-16 07:16"), kind: "R2P"},
|
||
|
|
},
|
||
|
|
lastR2P: LastNeptuneRetrogradeToPrograde,
|
||
|
|
nextR2P: NextNeptuneRetrogradeToPrograde,
|
||
|
|
lastP2R: LastNeptuneProgradeToRetrograde,
|
||
|
|
nextP2R: NextNeptuneProgradeToRetrograde,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, tc := range cases {
|
||
|
|
tc := tc
|
||
|
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
|
for i, event := range tc.events {
|
||
|
|
switch event.kind {
|
||
|
|
case "P2R":
|
||
|
|
before := event.when.Add(-24 * time.Hour)
|
||
|
|
after := event.when.Add(24 * time.Hour)
|
||
|
|
nextP2R := JDE2DateByZone(tc.nextP2R(toUTJD(before)), event.when.Location(), false)
|
||
|
|
lastP2R := JDE2DateByZone(tc.lastP2R(toUTJD(after)), event.when.Location(), false)
|
||
|
|
if !sameMinute(nextP2R, event.when) {
|
||
|
|
t.Fatalf("%s next P2R mismatch: got %s want %s", tc.name, nextP2R, event.when)
|
||
|
|
}
|
||
|
|
if !sameMinute(lastP2R, event.when) {
|
||
|
|
t.Fatalf("%s last P2R mismatch: got %s want %s", tc.name, lastP2R, event.when)
|
||
|
|
}
|
||
|
|
case "R2P":
|
||
|
|
before := event.when.Add(-24 * time.Hour)
|
||
|
|
after := event.when.Add(24 * time.Hour)
|
||
|
|
nextR2P := JDE2DateByZone(tc.nextR2P(toUTJD(before)), event.when.Location(), false)
|
||
|
|
lastR2P := JDE2DateByZone(tc.lastR2P(toUTJD(after)), event.when.Location(), false)
|
||
|
|
if !sameMinute(nextR2P, event.when) {
|
||
|
|
t.Fatalf("%s next R2P mismatch: got %s want %s", tc.name, nextR2P, event.when)
|
||
|
|
}
|
||
|
|
if !sameMinute(lastR2P, event.when) {
|
||
|
|
t.Fatalf("%s last R2P mismatch: got %s want %s", tc.name, lastR2P, event.when)
|
||
|
|
}
|
||
|
|
case "OPP":
|
||
|
|
prev := nearestOfKindBefore(tc.events, i, "P2R")
|
||
|
|
next := nearestOfKindAfter(tc.events, i, "R2P")
|
||
|
|
if prev.IsZero() || next.IsZero() {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
query := event.when
|
||
|
|
lastP2R := JDE2DateByZone(tc.lastP2R(toUTJD(query)), query.Location(), false)
|
||
|
|
nextR2P := JDE2DateByZone(tc.nextR2P(toUTJD(query)), query.Location(), false)
|
||
|
|
if !sameMinute(lastP2R, prev) {
|
||
|
|
t.Fatalf("%s opposition last P2R mismatch: got %s want %s", tc.name, lastP2R, prev)
|
||
|
|
}
|
||
|
|
if !sameMinute(nextR2P, next) {
|
||
|
|
t.Fatalf("%s opposition next R2P mismatch: got %s want %s", tc.name, nextR2P, next)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func nearestOfKindBefore(events []stationEvent, idx int, kind string) time.Time {
|
||
|
|
for i := idx - 1; i >= 0; i-- {
|
||
|
|
if events[i].kind == kind {
|
||
|
|
return events[i].when
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return time.Time{}
|
||
|
|
}
|
||
|
|
|
||
|
|
func nearestOfKindAfter(events []stationEvent, idx int, kind string) time.Time {
|
||
|
|
for i := idx + 1; i < len(events); i++ {
|
||
|
|
if events[i].kind == kind {
|
||
|
|
return events[i].when
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return time.Time{}
|
||
|
|
}
|
||
|
|
|
||
|
|
func sameMinute(got, want time.Time) bool {
|
||
|
|
diff := got.Sub(want)
|
||
|
|
if diff < 0 {
|
||
|
|
diff = -diff
|
||
|
|
}
|
||
|
|
return diff <= 2*time.Minute
|
||
|
|
}
|