044f2f4381
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
127 lines
3.3 KiB
Go
127 lines
3.3 KiB
Go
// Package semianalytic 内置半解析模型:VSOP2013/TOP2013 与 MPP02 结构、按 DE441 重拟的表,提供历元 L/B/R 与 ICRF 几何状态。
|
|
package semianalytic
|
|
|
|
import (
|
|
"math"
|
|
|
|
"b612.me/astro/internal/ephem"
|
|
"b612.me/astro/planet/internal/semianalytic/lunar"
|
|
"b612.me/astro/planet/internal/semianalytic/planets"
|
|
)
|
|
|
|
// 表以 TT 为自变量拟合;TDB−TT 小于 1.7 ms,直接当作 TDB 使用。
|
|
const (
|
|
coverStart = -1931455.0
|
|
coverEnd = 5373545.0
|
|
eps0 = 84381.448 / 3600 * math.Pi / 180
|
|
emrat = 81.3005682214972
|
|
)
|
|
|
|
// Ephemeris 内置表;越过覆盖范围仍外推求值,只在 Coverage 中报告范围 / built-in tables; extrapolates beyond coverage.
|
|
type Ephemeris struct{}
|
|
|
|
var _ ephem.Engine = Ephemeris{}
|
|
|
|
// Apparent 地心视方向(ICRF 单位矢量)、几何地心距 km、光行时日 / apparent geocentric direction.
|
|
func (Ephemeris) Apparent(target ephem.Body, jdTT float64) (dir [3]float64, distKM, tauDays float64, err error) {
|
|
return Apparent(target, jdTT)
|
|
}
|
|
|
|
func finite(x float64) bool { return !math.IsNaN(x) && !math.IsInf(x, 0) }
|
|
|
|
type state struct{ p, v [3]float64 }
|
|
|
|
func (a state) sub(b state) state {
|
|
for i := range a.p {
|
|
a.p[i] -= b.p[i]
|
|
a.v[i] -= b.v[i]
|
|
}
|
|
return a
|
|
}
|
|
|
|
// State 目标相对中心的 ICRF 几何状态 / geometric ICRF state of target relative to center.
|
|
func (Ephemeris) State(target, center ephem.Body, jdTDB float64) (pos, vel [3]float64, err error) {
|
|
if !finite(jdTDB) {
|
|
return pos, vel, ephem.ErrOutOfRange
|
|
}
|
|
t, err := helio(target, jdTDB)
|
|
if err != nil {
|
|
return pos, vel, err
|
|
}
|
|
c, err := helio(center, jdTDB)
|
|
if err != nil {
|
|
return pos, vel, err
|
|
}
|
|
d := t.sub(c)
|
|
return toICRF(d.p), toICRF(d.v), nil
|
|
}
|
|
|
|
// Coverage 可信区间 / trusted time span.
|
|
func (Ephemeris) Coverage(target ephem.Body) (start, end float64, ok bool) {
|
|
if _, err := helio(target, 2451545); err != nil {
|
|
return 0, 0, false
|
|
}
|
|
return coverStart, coverEnd, true
|
|
}
|
|
|
|
// helio 日心 J2000 平黄道状态(km、km/日)。
|
|
func helio(b ephem.Body, jd float64) (state, error) {
|
|
switch b {
|
|
case ephem.Sun:
|
|
return state{}, nil
|
|
case ephem.SSB:
|
|
return ssbHelio(jd), nil
|
|
case ephem.Earth, ephem.Moon:
|
|
emb, err := planetHelio(int(ephem.EMB), jd)
|
|
if err != nil {
|
|
return state{}, err
|
|
}
|
|
mp, mv := lunar.Default().PositionVelocityJ2000(jd)
|
|
k := 1.0 / (1 + emrat)
|
|
if b == ephem.Moon {
|
|
k -= 1
|
|
}
|
|
for i := range mp {
|
|
emb.p[i] -= mp[i] * k
|
|
emb.v[i] -= mv[i] * k
|
|
}
|
|
return emb, nil
|
|
}
|
|
if b >= ephem.Mercury && b <= ephem.Neptune {
|
|
return planetHelio(int(b), jd)
|
|
}
|
|
return state{}, ephem.ErrUnsupportedBody
|
|
}
|
|
|
|
func planetHelio(id int, jd float64) (state, error) {
|
|
p, v, err := planets.Default().HeliocentricState(id, jd)
|
|
return state{p, v}, err
|
|
}
|
|
|
|
// ssbHelio 太阳系质心的日心状态:Σ GMᵢ·rᵢ / (GM☉ + Σ GMᵢ),冥王星等小天体不计。
|
|
func ssbHelio(jd float64) state {
|
|
var s state
|
|
_, gmSun := planets.GM(1)
|
|
total := gmSun
|
|
for id := 1; id <= 8; id++ {
|
|
gm, _ := planets.GM(id)
|
|
h, _ := planetHelio(id, jd)
|
|
for i := range s.p {
|
|
s.p[i] += gm * h.p[i]
|
|
s.v[i] += gm * h.v[i]
|
|
}
|
|
total += gm
|
|
}
|
|
for i := range s.p {
|
|
s.p[i] /= total
|
|
s.v[i] /= total
|
|
}
|
|
return s
|
|
}
|
|
|
|
// toICRF J2000 平黄道 → ICRF 赤道(绕 x 轴转 −ε0)。
|
|
func toICRF(e [3]float64) [3]float64 {
|
|
s, c := math.Sincos(eps0)
|
|
return [3]float64{e[0], e[1]*c - e[2]*s, e[1]*s + e[2]*c}
|
|
}
|