- 新增日食、月食、本地可见性、中心线、半影区域、SVG 图示与沙罗周期信息 - 新增行星冲合、留、方照、物理星历、视直径、相位、亮肢角、轨道节点等计算 - 新增木星伽利略卫星位置、现象与接触事件计算 - 新增恒星星表、星座判定、自行修正与观测辅助能力 - 新增 coord、formula、orbit、sundial、lite/sun、lite/moon 等扩展包 - 完善农历年号、月相英文别名、视差角、大气质量、折射、日晷与双星计算 - 增加 NASA、JPL Horizons、IMCCE 等回归测试数据与基线测试 - 重构基础算法文件组织,补充大量公开 API 注释和语义回归测试 - 更新中文和英文 README,补充示例、精度说明、SVG 配图
163 lines
4.5 KiB
Go
163 lines
4.5 KiB
Go
package basic
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
func TestStarDataCacheMatchesRawRows(t *testing.T) {
|
|
if err := LoadStarData(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(parsedStarData) != len(stardat) {
|
|
t.Fatalf("parsed cache length mismatch: got %d want %d", len(parsedStarData), len(stardat))
|
|
}
|
|
if len(cachedStarData) != len(stardat) {
|
|
t.Fatalf("full cache length mismatch: got %d want %d", len(cachedStarData), len(stardat))
|
|
}
|
|
for i, row := range stardat {
|
|
parsed, err := parseStarData(row)
|
|
if err != nil {
|
|
t.Fatalf("parse row %d failed: %v", i+1, err)
|
|
}
|
|
if parsed != parsedStarData[i] {
|
|
t.Fatalf("parsed cache mismatch at HR %d", i+1)
|
|
}
|
|
full := fullStarData(parsed)
|
|
if full != cachedStarData[i] {
|
|
t.Fatalf("full cache mismatch at HR %d", i+1)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStarDataRegressionSamples(t *testing.T) {
|
|
if err := LoadStarData(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tests := []struct {
|
|
hr int
|
|
hip uint32
|
|
name string
|
|
chineseName string
|
|
chineseAlias string
|
|
chineseBayerName string
|
|
commonName string
|
|
cst string
|
|
cstChinese string
|
|
ra float64
|
|
dec float64
|
|
mag float64
|
|
pmRA float64
|
|
pmDec float64
|
|
}{
|
|
{15, 677, "21Alp And", "壁宿二", "", "仙女座α", "Alpheratz", "Andromeda", "仙女座", 2.097083333333, 29.090555555556, 2.06, 0.136, -0.163},
|
|
{424, 11767, "1Alp UMi", "勾陈一", "北极星", "小熊座α", "Polaris", "UrsaMinor", "小熊座", 37.952916666667, 89.264166666667, 2.02, 0.038, -0.015},
|
|
{2491, 32349, "9Alp CMa", "天狼", "", "大犬座α", "Sirius", "CanisMajor", "大犬座", 101.287083333333, -16.716111111111, -1.46, -0.553, -1.205},
|
|
{7001, 91262, "3Alp Lyr", "织女一", "织女", "天琴座α", "Vega", "Lyra", "天琴座", 279.234583333333, 38.783611111111, 0.03, 0.202, 0.286},
|
|
{9100, 330, "9 Cas", "", "", "", "", "", "", 1.056666666667, 62.287777777778, 5.88, -0.004, 0.006},
|
|
}
|
|
for _, tc := range tests {
|
|
got, err := StarDataByHR(tc.hr)
|
|
if err != nil {
|
|
t.Fatalf("StarDataByHR(%d) failed: %v", tc.hr, err)
|
|
}
|
|
if got.HR != uint16(tc.hr) || got.HIP != tc.hip || got.Name != tc.name ||
|
|
got.ChineseName != tc.chineseName || got.ChineseAlias != tc.chineseAlias ||
|
|
got.ChineseBayerName != tc.chineseBayerName || got.CommonName != tc.commonName ||
|
|
got.Cst != tc.cst || got.CstChinese != tc.cstChinese {
|
|
t.Fatalf("unexpected metadata for HR %d: %+v", tc.hr, got)
|
|
}
|
|
assertNear(t, "Ra", tc.hr, got.Ra, tc.ra, 1e-12)
|
|
assertNear(t, "Dec", tc.hr, got.Dec, tc.dec, 1e-12)
|
|
assertNear(t, "Mag", tc.hr, got.Mag, tc.mag, 1e-12)
|
|
assertNear(t, "PmRA", tc.hr, got.PmRA, tc.pmRA, 1e-12)
|
|
assertNear(t, "PmDec", tc.hr, got.PmDec, tc.pmDec, 1e-12)
|
|
}
|
|
}
|
|
|
|
func TestStarDataByChineseAlias(t *testing.T) {
|
|
if err := LoadStarData(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
hr uint16
|
|
hip uint32
|
|
}{
|
|
{"天狼", 2491, 32349},
|
|
{"天狼星", 2491, 32349},
|
|
{"勾陈一", 424, 11767},
|
|
{"北极", 424, 11767},
|
|
{"北极星", 424, 11767},
|
|
{"织女", 7001, 91262},
|
|
}
|
|
for _, tc := range tests {
|
|
got, err := StarDataByChinese(tc.name)
|
|
if err != nil {
|
|
t.Fatalf("StarDataByChinese(%q) failed: %v", tc.name, err)
|
|
}
|
|
if got.HR != tc.hr || got.HIP != tc.hip {
|
|
t.Fatalf("StarDataByChinese(%q) got HR=%d HIP=%d", tc.name, got.HR, got.HIP)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStarDataByHRInvalid(t *testing.T) {
|
|
if err := LoadStarData(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, hr := range []int{0, -1, len(stardat) + 1} {
|
|
if _, err := StarDataByHR(hr); err == nil {
|
|
t.Fatalf("StarDataByHR(%d) expected error", hr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkInitStarData(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
stardat = nil
|
|
hr2detail = nil
|
|
hr2hip = nil
|
|
chnidx = nil
|
|
parsedStarData = nil
|
|
cachedStarData = nil
|
|
if err := initStarData(); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkStarDataByHR(b *testing.B) {
|
|
if err := LoadStarData(); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if _, err := StarDataByHR(2491); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkStarDataByChinese(b *testing.B) {
|
|
if err := LoadStarData(); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if _, err := StarDataByChinese("天狼星"); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertNear(t *testing.T, field string, hr int, got, want, tolerance float64) {
|
|
t.Helper()
|
|
if math.Abs(got-want) > tolerance {
|
|
t.Fatalf("HR %d %s mismatch: got %.15f want %.15f", hr, field, got, want)
|
|
}
|
|
}
|