feat: 引入可切换星历模型并扩展天象观测链路
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
package semianalytic
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"b612.me/astro/internal/ltp"
|
||||
"b612.me/astro/planet/internal/semianalytic/lunar"
|
||||
"b612.me/astro/planet/internal/semianalytic/planets"
|
||||
"b612.me/astro/tools"
|
||||
)
|
||||
|
||||
const (
|
||||
auKM = 149597870.7
|
||||
earthSlot = 0
|
||||
moonSlot = 8
|
||||
fastBase = 9
|
||||
)
|
||||
|
||||
var sinEps0, cosEps0 = math.Sincos(eps0)
|
||||
|
||||
// xt 1..7 对应的表内天体号。
|
||||
var bodyID = [8]int{0, planets.Mercury, planets.Venus, planets.Mars, planets.Jupiter, planets.Saturn, planets.Uranus, planets.Neptune}
|
||||
|
||||
type lbr struct{ jd, l, b, r float64 }
|
||||
|
||||
type lbrSlot struct {
|
||||
v lbr
|
||||
set bool
|
||||
}
|
||||
|
||||
// L、B、R 通常在同一时刻连续取用,每个天体每档缓存最近一次的结果;0..8 全表,9..17 快速档。
|
||||
var (
|
||||
cacheMu sync.RWMutex
|
||||
cache [18]lbrSlot
|
||||
)
|
||||
|
||||
// tier n < 0 用全表,n ≥ 0 用快速档;新模型的快速档不随 n 细分。
|
||||
func tier(n int) (*planets.Model, *lunar.Model, int) {
|
||||
if n < 0 {
|
||||
return planets.Default(), lunar.Default(), 0
|
||||
}
|
||||
return planets.Fast(), lunar.Fast(), fastBase
|
||||
}
|
||||
|
||||
func cached(slot int, jd float64) (lbr, bool) {
|
||||
cacheMu.RLock()
|
||||
c := cache[slot]
|
||||
cacheMu.RUnlock()
|
||||
if c.set && c.v.jd == jd {
|
||||
return c.v, true
|
||||
}
|
||||
return lbr{}, false
|
||||
}
|
||||
|
||||
func store(slot int, jd float64, v [3]float64) lbr {
|
||||
l, b, r := ofDate(v, jd)
|
||||
c := lbr{jd, l, b, r}
|
||||
cacheMu.Lock()
|
||||
cache[slot] = lbrSlot{v: c, set: true}
|
||||
cacheMu.Unlock()
|
||||
return c
|
||||
}
|
||||
|
||||
type moonVec struct {
|
||||
jd float64
|
||||
p [3]float64
|
||||
set bool
|
||||
}
|
||||
|
||||
// 地球合成与月球本身在同一时刻都要月球向量,按档缓存最近一次。
|
||||
var (
|
||||
moonVecMu sync.RWMutex
|
||||
moonVecCache [2]moonVec
|
||||
)
|
||||
|
||||
func moonJ2000(lm *lunar.Model, slot int, jde float64) [3]float64 {
|
||||
moonVecMu.RLock()
|
||||
c := moonVecCache[slot]
|
||||
moonVecMu.RUnlock()
|
||||
if c.set && c.jd == jde {
|
||||
return c.p
|
||||
}
|
||||
p := lm.PositionJ2000(jde)
|
||||
moonVecMu.Lock()
|
||||
moonVecCache[slot] = moonVec{jd: jde, p: p, set: true}
|
||||
moonVecMu.Unlock()
|
||||
return p
|
||||
}
|
||||
|
||||
// ofDate J2000 平黄道向量 → 历元平黄道经纬(弧度,经度在 [0, 2π))与模长。
|
||||
func ofDate(v [3]float64, jde float64) (l, b, r float64) {
|
||||
q0, q1, q2 := v[0], v[1]*cosEps0-v[2]*sinEps0, v[1]*sinEps0+v[2]*cosEps0
|
||||
x, y, z := ltp.EclipticBasis(2000 + (jde-2451545)/365.25)
|
||||
ex := x[0]*q0 + x[1]*q1 + x[2]*q2
|
||||
ey := y[0]*q0 + y[1]*q1 + y[2]*q2
|
||||
ez := z[0]*q0 + z[1]*q1 + z[2]*q2
|
||||
l = math.Atan2(ey, ex)
|
||||
if l < 0 {
|
||||
l += 2 * math.Pi
|
||||
}
|
||||
return l, math.Atan2(ez, math.Hypot(ex, ey)), math.Sqrt(ex*ex + ey*ey + ez*ez)
|
||||
}
|
||||
|
||||
// Planet 与 planet.WherePlanetN 同语义(xt 取 -1..7,zn 取 0..2),调用方负责越界检查 / same contract as planet.WherePlanetN.
|
||||
func Planet(xt, zn int, jde float64, n int) float64 {
|
||||
pm, lm, base := tier(n)
|
||||
slot := base + earthSlot
|
||||
if xt > 0 {
|
||||
slot = base + xt
|
||||
}
|
||||
c, ok := cached(slot, jde)
|
||||
if !ok {
|
||||
var p [3]float64
|
||||
if xt <= 0 {
|
||||
emb, _ := pm.Heliocentric(planets.EMB, jde)
|
||||
p = planets.EarthFromEMB(emb, moonJ2000(lm, base/fastBase, jde))
|
||||
} else {
|
||||
p, _ = pm.Heliocentric(bodyID[xt], jde)
|
||||
}
|
||||
c = store(slot, jde, p)
|
||||
}
|
||||
switch zn {
|
||||
case 0:
|
||||
l := c.l * 180 / math.Pi
|
||||
if xt == 0 {
|
||||
l += 180
|
||||
}
|
||||
return tools.Limit360(l)
|
||||
case 1:
|
||||
if xt == 0 {
|
||||
return -c.b * 180 / math.Pi
|
||||
}
|
||||
return c.b * 180 / math.Pi
|
||||
}
|
||||
return c.r / auKM
|
||||
}
|
||||
|
||||
// Moon 地心月球历元平黄道:coord 0 黄经、1 黄纬(弧度)、2 距离(km),n ≥ 0 用快速档,调用方负责越界检查 / geocentric Moon of date.
|
||||
func Moon(coord int, jde float64, n int) float64 {
|
||||
_, lm, base := tier(n)
|
||||
slot := base + moonSlot
|
||||
c, ok := cached(slot, jde)
|
||||
if !ok {
|
||||
c = store(slot, jde, moonJ2000(lm, base/fastBase, jde))
|
||||
}
|
||||
switch coord {
|
||||
case 0:
|
||||
return c.l
|
||||
case 1:
|
||||
return c.b
|
||||
}
|
||||
return c.r
|
||||
}
|
||||
Reference in New Issue
Block a user