044f2f4381
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
130 lines
5.1 KiB
Go
130 lines
5.1 KiB
Go
// 被测契约:标准气象下与原升落一致;高压低温让日出提前、日落推迟;非法气象报错。
|
|
package basic
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
func TestRiseSetWithAtmosphere(t *testing.T) {
|
|
jd, lon, lat, tz := 2460676.5, 116.4, 39.9, 8.0
|
|
cases := []struct {
|
|
name string
|
|
standard func() (float64, error)
|
|
weather func(p, c float64) (float64, error)
|
|
rise bool
|
|
}{
|
|
{"sunrise", func() (float64, error) { return GetSunRiseTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) },
|
|
func(p, c float64) (float64, error) {
|
|
return GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, p, c, nil)
|
|
}, true},
|
|
{"sunset", func() (float64, error) { return GetSunSetTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) },
|
|
func(p, c float64) (float64, error) {
|
|
return GetSunSetTimeWithAtmosphere(jd, lon, lat, tz, 0, p, c, nil)
|
|
}, false},
|
|
{"moonrise", func() (float64, error) { return GetMoonRiseTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) },
|
|
func(p, c float64) (float64, error) {
|
|
return GetMoonRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, p, c, nil)
|
|
}, true},
|
|
{"moonset", func() (float64, error) { return GetMoonSetTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) },
|
|
func(p, c float64) (float64, error) {
|
|
return GetMoonSetTimeWithAtmosphere(jd, lon, lat, tz, 0, p, c, nil)
|
|
}, false},
|
|
}
|
|
for _, c := range cases {
|
|
base, err := c.standard()
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", c.name, err)
|
|
}
|
|
same, err := c.weather(refractionStandardPressureHPa, refractionStandardTemperatureC)
|
|
if err != nil || math.Abs(same-base)*86400 > 0.01 {
|
|
t.Errorf("%s: standard weather %.6f vs %.6f (%v)", c.name, same, base, err)
|
|
}
|
|
dense, err := c.weather(1040, -30)
|
|
if err != nil {
|
|
t.Fatalf("%s dense: %v", c.name, err)
|
|
}
|
|
shift := (dense - base) * 86400
|
|
t.Logf("%s: 1040 hPa / -30 °C shifts %+.1f s", c.name, shift)
|
|
if c.rise && shift >= 0 || !c.rise && shift <= 0 || math.Abs(shift) > 120 {
|
|
t.Errorf("%s: implausible shift %.1f s", c.name, shift)
|
|
}
|
|
}
|
|
if _, err := GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, -1, 10, nil); err != ErrInvalidObservationInput {
|
|
t.Errorf("negative pressure: %v", err)
|
|
}
|
|
if _, err := GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, 1010, -300, nil); err != ErrInvalidObservationInput {
|
|
t.Errorf("below absolute zero: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPointRiseSetWithAtmosphere(t *testing.T) {
|
|
jd, lon, lat, tz := 2460676.5, 116.4, 39.9, 8.0
|
|
type pair struct {
|
|
name string
|
|
plain, atmos func(p, temp float64) (float64, error)
|
|
}
|
|
cases := []pair{
|
|
{"MarsRise", func(float64, float64) (float64, error) { return MarsRiseTimeWithInstance(jd, lon, lat, tz, 1, 0, nil) },
|
|
func(p, temp float64) (float64, error) {
|
|
return MarsRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, p, temp, nil)
|
|
}},
|
|
{"JupiterSet", func(float64, float64) (float64, error) {
|
|
return JupiterSetTimeWithInstance(jd, lon, lat, tz, 1, 0, nil)
|
|
},
|
|
func(p, temp float64) (float64, error) {
|
|
return JupiterSetTimeWithAtmosphere(jd, lon, lat, tz, 0, p, temp, nil)
|
|
}},
|
|
{"SiriusRise", func(float64, float64) (float64, error) {
|
|
return StarRiseTime(jd, 101.29, -16.72, lon, lat, 0, tz, true)
|
|
},
|
|
func(p, temp float64) (float64, error) {
|
|
return StarRiseTimeWithAtmosphere(jd, 101.29, -16.72, lon, lat, tz, 0, p, temp)
|
|
}},
|
|
}
|
|
for _, c := range cases {
|
|
plain, err := c.plain(0, 0)
|
|
if err != nil {
|
|
t.Fatalf("%s plain: %v", c.name, err)
|
|
}
|
|
std, err := c.atmos(refractionStandardPressureHPa, refractionStandardTemperatureC)
|
|
if err != nil {
|
|
t.Fatalf("%s standard: %v", c.name, err)
|
|
}
|
|
// 原接口用固定 34′ 地平折射,这里按公式在实际高度求折射,标准气象下差约 2–3 s。
|
|
if d := math.Abs(std-plain) * 86400; d > 5 {
|
|
t.Errorf("%s: standard weather differs from plain by %.2f s", c.name, d)
|
|
}
|
|
dense, err := c.atmos(1040, -30)
|
|
if err != nil {
|
|
t.Fatalf("%s dense: %v", c.name, err)
|
|
}
|
|
shift := (dense - std) * 86400
|
|
t.Logf("%s: 1040 hPa / -30 °C shifts %+.1f s", c.name, shift)
|
|
rising := c.name != "JupiterSet"
|
|
if (rising && shift >= 0) || (!rising && shift <= 0) || math.Abs(shift) > 300 {
|
|
t.Errorf("%s: implausible shift %.1f s", c.name, shift)
|
|
}
|
|
}
|
|
if _, err := MarsRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, -1, 10, nil); err == nil {
|
|
t.Error("invalid pressure must fail")
|
|
}
|
|
}
|
|
|
|
func TestRiseSetWeatherRejectsOutOfRange(t *testing.T) {
|
|
jd, lon, lat, tz := 2460831.0, 116.4, 39.9, 8.0
|
|
for _, c := range [][2]float64{{1.01e9, 10}, {1e6, 10}, {1201, 10}, {1010, -273.14}, {1010, -101}, {1010, 81}, {math.NaN(), 10}, {1010, math.Inf(1)}} {
|
|
if _, err := GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, c[0], c[1], nil); err != ErrInvalidObservationInput {
|
|
t.Errorf("sun p=%g T=%g: err=%v", c[0], c[1], err)
|
|
}
|
|
if _, err := MercuryRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, c[0], c[1], nil); err != ErrInvalidObservationInput {
|
|
t.Errorf("mercury p=%g T=%g: err=%v", c[0], c[1], err)
|
|
}
|
|
}
|
|
for _, c := range [][2]float64{{1200, 10}, {1010, -100}, {1010, 80}, {300, 10}} {
|
|
if _, err := GetSunRiseTimeWithAtmosphere(jd, lon, lat, tz, 0, c[0], c[1], nil); err != nil {
|
|
t.Errorf("sun p=%g T=%g must be accepted: %v", c[0], c[1], err)
|
|
}
|
|
}
|
|
}
|