astro/low_precision_regression_test.go
starainrt 3ffdbe0034
feat: 扩展天文计算能力
- 新增日食、月食、本地可见性、中心线、半影区域、SVG 图示与沙罗周期信息
- 新增行星冲合、留、方照、物理星历、视直径、相位、亮肢角、轨道节点等计算
- 新增木星伽利略卫星位置、现象与接触事件计算
- 新增恒星星表、星座判定、自行修正与观测辅助能力
- 新增 coord、formula、orbit、sundial、lite/sun、lite/moon 等扩展包
- 完善农历年号、月相英文别名、视差角、大气质量、折射、日晷与双星计算
- 增加 NASA、JPL Horizons、IMCCE 等回归测试数据与基线测试
- 重构基础算法文件组织,补充大量公开 API 注释和语义回归测试
- 更新中文和英文 README,补充示例、精度说明、SVG 配图
2026-05-01 22:38:44 +08:00

176 lines
8.5 KiB
Go

package astro_test
import (
"encoding/json"
"math"
"os"
"testing"
"b612.me/astro/basic"
"b612.me/astro/planet"
)
type lowPrecisionSunSnapshot struct {
LoBits uint64 `json:"lo_bits"`
MBits uint64 `json:"m_bits"`
EccBits uint64 `json:"ecc_bits"`
PeriBits uint64 `json:"peri_bits"`
MidBits uint64 `json:"mid_bits"`
TrueLoBits uint64 `json:"true_lo_bits"`
ApparentLoBits uint64 `json:"apparent_lo_bits"`
ApparentRaBits uint64 `json:"apparent_ra_bits"`
ApparentDecBits uint64 `json:"apparent_dec_bits"`
TrueRaBits uint64 `json:"true_ra_bits"`
TrueDecBits uint64 `json:"true_dec_bits"`
DistanceBits uint64 `json:"distance_bits"`
}
type lowPrecisionMoonSnapshot struct {
LoBits uint64 `json:"lo_bits"`
SunAngleBits uint64 `json:"sun_angle_bits"`
MBits uint64 `json:"m_bits"`
LonXBits uint64 `json:"lonx_bits"`
IBits uint64 `json:"i_bits"`
RBits uint64 `json:"r_bits"`
BBits uint64 `json:"b_bits"`
TrueLoBits uint64 `json:"true_lo_bits"`
TrueBoBits uint64 `json:"true_bo_bits"`
AwayBits uint64 `json:"away_bits"`
ApparentLoBits uint64 `json:"apparent_lo_bits"`
TrueRaBits uint64 `json:"true_ra_bits"`
TrueDecBits uint64 `json:"true_dec_bits"`
}
type lowPrecisionSample struct {
UTC string `json:"utc"`
TTJD float64 `json:"tt_jd"`
Sun lowPrecisionSunSnapshot `json:"sun"`
Moon lowPrecisionMoonSnapshot `json:"moon"`
}
func loadLowPrecisionSamples(t *testing.T) []lowPrecisionSample {
t.Helper()
data, err := os.ReadFile("testdata/low_precision_sun_moon_baseline.json")
if err != nil {
t.Fatal(err)
}
var samples []lowPrecisionSample
if err := json.Unmarshal(data, &samples); err != nil {
t.Fatal(err)
}
if len(samples) == 0 {
t.Fatal("empty low precision baseline samples")
}
return samples
}
func TestLowPrecisionSunMoonBaselineRegression(t *testing.T) {
samples := loadLowPrecisionSamples(t)
assertBits := func(t *testing.T, name, utc string, got float64, want uint64) {
t.Helper()
if math.Float64bits(got) != want {
t.Fatalf("%s regression at %s", name, utc)
}
}
for _, sample := range samples {
jd := sample.TTJD
assertBits(t, "planet.SunLo", sample.UTC, planet.SunLo(jd), sample.Sun.LoBits)
assertBits(t, "basic.SunLo", sample.UTC, basic.SunLo(jd), sample.Sun.LoBits)
assertBits(t, "planet.SunM", sample.UTC, planet.SunM(jd), sample.Sun.MBits)
assertBits(t, "basic.SunM", sample.UTC, basic.SunM(jd), sample.Sun.MBits)
assertBits(t, "planet.Earthe", sample.UTC, planet.Earthe(jd), sample.Sun.EccBits)
assertBits(t, "basic.Earthe", sample.UTC, basic.Earthe(jd), sample.Sun.EccBits)
assertBits(t, "planet.EarthPI", sample.UTC, planet.EarthPI(jd), sample.Sun.PeriBits)
assertBits(t, "basic.EarthPI", sample.UTC, basic.EarthPI(jd), sample.Sun.PeriBits)
assertBits(t, "planet.SunMidFun", sample.UTC, planet.SunMidFun(jd), sample.Sun.MidBits)
assertBits(t, "basic.SunMidFun", sample.UTC, basic.SunMidFun(jd), sample.Sun.MidBits)
assertBits(t, "planet.SunTrueLo", sample.UTC, planet.SunTrueLo(jd), sample.Sun.TrueLoBits)
assertBits(t, "basic.SunTrueLo", sample.UTC, basic.SunTrueLo(jd), sample.Sun.TrueLoBits)
assertBits(t, "planet.SunApparentLo", sample.UTC, planet.SunApparentLo(jd), sample.Sun.ApparentLoBits)
assertBits(t, "basic.SunApparentLo", sample.UTC, basic.SunApparentLo(jd), sample.Sun.ApparentLoBits)
assertBits(t, "basic.SunApparentRa", sample.UTC, basic.SunApparentRa(jd), sample.Sun.ApparentRaBits)
assertBits(t, "basic.SunApparentDec", sample.UTC, basic.SunApparentDec(jd), sample.Sun.ApparentDecBits)
assertBits(t, "basic.SunTrueRa", sample.UTC, basic.SunTrueRa(jd), sample.Sun.TrueRaBits)
assertBits(t, "basic.SunTrueDec", sample.UTC, basic.SunTrueDec(jd), sample.Sun.TrueDecBits)
assertBits(t, "planet.Distance", sample.UTC, planet.Distance(jd), sample.Sun.DistanceBits)
assertBits(t, "basic.Distance", sample.UTC, basic.Distance(jd), sample.Sun.DistanceBits)
assertBits(t, "planet.MoonLo", sample.UTC, planet.MoonLo(jd), sample.Moon.LoBits)
assertBits(t, "basic.MoonLo", sample.UTC, basic.MoonLo(jd), sample.Moon.LoBits)
assertBits(t, "planet.SunMoonAngle", sample.UTC, planet.SunMoonAngle(jd), sample.Moon.SunAngleBits)
assertBits(t, "basic.SunMoonAngle", sample.UTC, basic.SunMoonAngle(jd), sample.Moon.SunAngleBits)
assertBits(t, "planet.MoonM", sample.UTC, planet.MoonM(jd), sample.Moon.MBits)
assertBits(t, "basic.MoonM", sample.UTC, basic.MoonM(jd), sample.Moon.MBits)
assertBits(t, "planet.MoonLonX", sample.UTC, planet.MoonLonX(jd), sample.Moon.LonXBits)
assertBits(t, "basic.MoonLonX", sample.UTC, basic.MoonLonX(jd), sample.Moon.LonXBits)
assertBits(t, "planet.MoonI", sample.UTC, planet.MoonI(jd), sample.Moon.IBits)
assertBits(t, "basic.MoonI", sample.UTC, basic.MoonI(jd), sample.Moon.IBits)
assertBits(t, "planet.MoonR", sample.UTC, planet.MoonR(jd), sample.Moon.RBits)
assertBits(t, "basic.MoonR", sample.UTC, basic.MoonR(jd), sample.Moon.RBits)
assertBits(t, "planet.MoonB", sample.UTC, planet.MoonB(jd), sample.Moon.BBits)
assertBits(t, "basic.MoonB", sample.UTC, basic.MoonB(jd), sample.Moon.BBits)
assertBits(t, "planet.MoonTrueLo", sample.UTC, planet.MoonTrueLo(jd), sample.Moon.TrueLoBits)
assertBits(t, "basic.MoonTrueLo", sample.UTC, basic.MoonTrueLo(jd), sample.Moon.TrueLoBits)
assertBits(t, "planet.MoonTrueBo", sample.UTC, planet.MoonTrueBo(jd), sample.Moon.TrueBoBits)
assertBits(t, "basic.MoonTrueBo", sample.UTC, basic.MoonTrueBo(jd), sample.Moon.TrueBoBits)
assertBits(t, "planet.MoonAway", sample.UTC, planet.MoonAway(jd), sample.Moon.AwayBits)
assertBits(t, "basic.MoonAway", sample.UTC, basic.MoonAway(jd), sample.Moon.AwayBits)
assertBits(t, "basic.MoonApparentLo", sample.UTC, basic.MoonApparentLo(jd), sample.Moon.ApparentLoBits)
assertBits(t, "basic.MoonTrueRa", sample.UTC, basic.MoonTrueRa(jd), sample.Moon.TrueRaBits)
assertBits(t, "basic.MoonTrueDec", sample.UTC, basic.MoonTrueDec(jd), sample.Moon.TrueDecBits)
}
}
func TestDerivedHighPrecisionTruncationFullMatchesDefault(t *testing.T) {
jd := 2469808.7654321
lon := 116.391
lat := 39.907
tz := 8.0
assertSame := func(name string, got, want float64) {
t.Helper()
if math.Float64bits(got) != math.Float64bits(want) {
t.Fatalf("%s full-n mismatch", name)
}
}
assertSamePair := func(name string, got1, got2, want1, want2 float64) {
t.Helper()
assertSame(name+".1", got1, want1)
assertSame(name+".2", got2, want2)
}
assertSame("HSunTrueLo", basic.HSunTrueLo(jd), basic.HSunTrueLoN(jd, -1))
assertSame("HSunTrueBo", basic.HSunTrueBo(jd), basic.HSunTrueBoN(jd, -1))
assertSame("HSunApparentLo", basic.HSunApparentLo(jd), basic.HSunApparentLoN(jd, -1))
assertSame("SunLoGXC", basic.SunLoGXC(jd), basic.SunLoGXCN(jd, -1))
assertSame("EarthAway", basic.EarthAway(jd), basic.EarthAwayN(jd, -1))
assertSame("HSunApparentRa", basic.HSunApparentRa(jd), basic.HSunApparentRaN(jd, -1))
assertSame("HSunTrueRa", basic.HSunTrueRa(jd), basic.HSunTrueRaN(jd, -1))
assertSame("HSunApparentDec", basic.HSunApparentDec(jd), basic.HSunApparentDecN(jd, -1))
assertSame("HSunTrueDec", basic.HSunTrueDec(jd), basic.HSunTrueDecN(jd, -1))
ra1, dec1 := basic.HSunApparentRaDec(jd)
ra2, dec2 := basic.HSunApparentRaDecN(jd, -1)
assertSamePair("HSunApparentRaDec", ra1, dec1, ra2, dec2)
assertSame("HMoonTrueLo", basic.HMoonTrueLo(jd), basic.HMoonTrueLoN(jd, -1))
assertSame("HMoonTrueBo", basic.HMoonTrueBo(jd), basic.HMoonTrueBoN(jd, -1))
assertSame("HMoonAway", basic.HMoonAway(jd), basic.HMoonAwayN(jd, -1))
assertSame("HMoonApparentLo", basic.HMoonApparentLo(jd), basic.HMoonApparentLoN(jd, -1))
assertSame("HMoonTrueRa", basic.HMoonTrueRa(jd), basic.HMoonTrueRaN(jd, -1))
assertSame("HMoonTrueDec", basic.HMoonTrueDec(jd), basic.HMoonTrueDecN(jd, -1))
ra1, dec1 = basic.HMoonTrueRaDec(jd)
ra2, dec2 = basic.HMoonTrueRaDecN(jd, -1)
assertSamePair("HMoonTrueRaDec", ra1, dec1, ra2, dec2)
ra1, dec1 = basic.HMoonApparentRaDec(jd, lon, lat, tz)
ra2, dec2 = basic.HMoonApparentRaDecN(jd, lon, lat, tz, -1)
assertSamePair("HMoonApparentRaDec", ra1, dec1, ra2, dec2)
assertSame("HMoonApparentRa", basic.HMoonApparentRa(jd, lon, lat, tz), basic.HMoonApparentRaN(jd, lon, lat, tz, -1))
assertSame("HMoonApparentDec", basic.HMoonApparentDec(jd, lon, lat, tz), basic.HMoonApparentDecN(jd, lon, lat, tz, -1))
assertSame("HMoonAzimuth", basic.HMoonAzimuth(jd, lon, lat, tz), basic.HMoonAzimuthN(jd, lon, lat, tz, -1))
assertSame("HMoonHeight", basic.HMoonHeight(jd, lon, lat, tz), basic.HMoonHeightN(jd, lon, lat, tz, -1))
}