94 lines
3.8 KiB
Go
94 lines
3.8 KiB
Go
|
|
package basic
|
||
|
|
|
||
|
|
import (
|
||
|
|
"reflect"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// 折点补根的判据只能统计查询窗口内部的未成对端点:窗口边界上的裁剪端点没有任何折点
|
||
|
|
// 可以配对,把它们计入会让“未成对端点数严格下降”永远不成立。
|
||
|
|
func TestOccultationRiseSetFoldRecoveryCountsInteriorEndpoints(t *testing.T) {
|
||
|
|
zone := time.FixedZone("UTC+8", 8*3600)
|
||
|
|
start := time.Date(2025, time.July, 29, 0, 0, 0, 0, zone)
|
||
|
|
options := OccultationPathOptions{
|
||
|
|
Step: 20 * time.Minute, TargetSpacingKM: 900, RiseSetStep: time.Minute,
|
||
|
|
DisableFootprints: true,
|
||
|
|
}
|
||
|
|
paths, err := FindPlanetOccultationPaths(start, start.Add(24*time.Hour), OccultationMars, options)
|
||
|
|
if err != nil || len(paths) != 1 || !paths[0].HasTotalBand {
|
||
|
|
t.Fatalf("paths=%d err=%v, want one total-band event", len(paths), err)
|
||
|
|
}
|
||
|
|
path := paths[0]
|
||
|
|
config, _ := planetOccultationConfigFor(OccultationMars)
|
||
|
|
cache := newPlanetOccultationEventCache(config)
|
||
|
|
greatestTT := occultationTimeToTT(path.Greatest.Time)
|
||
|
|
cache.preparePathEphemeris(greatestTT, OccultationPathAlgorithmOptimized)
|
||
|
|
firstTT := occultationTimeToTT(path.TotalStart.Time)
|
||
|
|
lastTT := occultationTimeToTT(path.TotalEnd.Time)
|
||
|
|
curves, base, recovered := occultationRiseSetCurvesWithRecoveryReport(
|
||
|
|
firstTT, lastTT, greatestTT, options, start.Location(), cache.totalRiseSetCache,
|
||
|
|
)
|
||
|
|
if recovered == nil {
|
||
|
|
t.Fatal("fold recovery was never attempted on a graph with interior unpaired endpoints")
|
||
|
|
}
|
||
|
|
baseInterior := occultationRiseSetUnclosedEndpointCount(base, true, firstTT, lastTT)
|
||
|
|
recoveredInterior := occultationRiseSetUnclosedEndpointCount(recovered, true, firstTT, lastTT)
|
||
|
|
if baseInterior == 0 || recoveredInterior >= baseInterior {
|
||
|
|
t.Fatalf("interior unpaired endpoints base=%d recovered=%d, want a strict decrease",
|
||
|
|
baseInterior, recoveredInterior)
|
||
|
|
}
|
||
|
|
// 全局计数被窗口裁剪端点抬平:按它判定会丢弃这次重建。
|
||
|
|
baseGlobal := occultationRiseSetUnclosedEndpointCount(base, false, firstTT, lastTT)
|
||
|
|
recoveredGlobal := occultationRiseSetUnclosedEndpointCount(recovered, false, firstTT, lastTT)
|
||
|
|
if recoveredGlobal < baseGlobal {
|
||
|
|
t.Fatalf("global unpaired endpoints base=%d recovered=%d, sample no longer shows a window-truncated endpoint",
|
||
|
|
baseGlobal, recoveredGlobal)
|
||
|
|
}
|
||
|
|
if !reflect.DeepEqual(curves, recovered) {
|
||
|
|
t.Fatal("interior-endpoint test did not select the rebuilt phase graph")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestOccultationRiseSetWindowClippedEndpointIgnoredByInteriorCount(t *testing.T) {
|
||
|
|
firstTT := 2460000.0
|
||
|
|
lastTT := firstTT + 0.25
|
||
|
|
point := func(tt float64) OccultationPathPoint {
|
||
|
|
return OccultationPathPoint{Time: occultationTTToLocation(tt, time.UTC)}
|
||
|
|
}
|
||
|
|
curves := []OccultationRiseSetCurve{
|
||
|
|
{
|
||
|
|
Phase: RiseSetPhaseStart, Direction: RiseSetDirectionRise,
|
||
|
|
Segments: [][]OccultationPathPoint{
|
||
|
|
{point(lastTT), point(lastTT + 0.01)},
|
||
|
|
{point(firstTT + 0.02), point(firstTT + 0.03)},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
if got := occultationRiseSetUnclosedEndpointCount(curves, false, firstTT, lastTT); got != 4 {
|
||
|
|
t.Fatalf("global unpaired endpoints=%d, want 4", got)
|
||
|
|
}
|
||
|
|
if got := occultationRiseSetUnclosedEndpointCount(curves, true, firstTT, lastTT); got != 2 {
|
||
|
|
t.Fatalf("interior unpaired endpoints=%d, want 2", got)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// stateAt 的分配预算:同一上下文内换站点只复用已分配的站点表,不再额外分配。
|
||
|
|
func BenchmarkOccultationRiseSetStateAt(b *testing.B) {
|
||
|
|
context := newOccultationRiseSetContext(
|
||
|
|
occultationTimeToTT(time.Date(2025, time.July, 29, 12, 0, 0, 0, time.UTC)),
|
||
|
|
100, 10, 384400, 200, -15, 149597870.7*2, 3389.5,
|
||
|
|
)
|
||
|
|
coordinates := make([][2]float64, 0, 64)
|
||
|
|
for index := 0; index < 64; index++ {
|
||
|
|
coordinates = append(coordinates, [2]float64{float64(index) * 1.7, float64(index%30) - 15})
|
||
|
|
}
|
||
|
|
b.ReportAllocs()
|
||
|
|
b.ResetTimer()
|
||
|
|
for index := 0; index < b.N; index++ {
|
||
|
|
for _, coordinate := range coordinates {
|
||
|
|
_ = context.stateAt(coordinate[0], coordinate[1])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|