feat: 引入可切换星历模型并扩展天象观测链路
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
This commit is contained in:
+64
-10
@@ -1,6 +1,8 @@
|
||||
package basic
|
||||
|
||||
import "math"
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// localEphemerisVectorNode is a pair of Cartesian ephemeris samples at one
|
||||
// TT. Cartesian interpolation avoids right-ascension wraparound at 0/360.
|
||||
@@ -82,12 +84,14 @@ type solarEclipseLocalEphemeris struct {
|
||||
nodes []localEphemerisVectorNode
|
||||
}
|
||||
|
||||
func newSolarEclipseLocalEphemeris(center float64) *solarEclipseLocalEphemeris {
|
||||
func newSolarEclipseLocalEphemeris(center float64, instances ...*Instance) *solarEclipseLocalEphemeris {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
half := solarLocalEphemerisNodeCount / 2
|
||||
nodes := make([]localEphemerisVectorNode, solarLocalEphemerisNodeCount)
|
||||
for index := range nodes {
|
||||
tt := center + float64(index-half)*solarLocalEphemerisStepDays
|
||||
sun, moon := solarEclipseSunMoonEquatorial(tt)
|
||||
sun, moon := solarEclipseSunMoonEquatorial(tt, inst)
|
||||
nodes[index] = localEphemerisVectorNode{
|
||||
tt: tt,
|
||||
first: solarEclipseLLRToXYZ(sun[0], sun[1], sun[2]),
|
||||
@@ -97,6 +101,50 @@ func newSolarEclipseLocalEphemeris(center float64) *solarEclipseLocalEphemeris {
|
||||
return &solarEclipseLocalEphemeris{nodes: nodes}
|
||||
}
|
||||
|
||||
// solarEclipseLimbAxes 与日月局部星历同一时间网格的月球本体坐标架插值节点。
|
||||
type solarEclipseLimbAxes struct {
|
||||
base lunarLimbFrame
|
||||
nodes []localEphemerisVectorNode
|
||||
}
|
||||
|
||||
func newSolarEclipseLimbAxes(ephemeris *solarEclipseLocalEphemeris, limb LunarLimb, inst *Instance) *solarEclipseLimbAxes {
|
||||
if ephemeris == nil || limb == nil {
|
||||
return nil
|
||||
}
|
||||
axes := &solarEclipseLimbAxes{nodes: make([]localEphemerisVectorNode, len(ephemeris.nodes))}
|
||||
for index, node := range ephemeris.nodes {
|
||||
frame := newLunarLimbFrame(limb, node.tt, node.next, inst)
|
||||
if !frame.valid {
|
||||
return nil
|
||||
}
|
||||
if index == len(ephemeris.nodes)/2 {
|
||||
axes.base = frame
|
||||
}
|
||||
axes.nodes[index] = localEphemerisVectorNode{tt: node.tt, first: frame.axisX, next: frame.axisZ}
|
||||
}
|
||||
return axes
|
||||
}
|
||||
|
||||
func (axes *solarEclipseLimbAxes) frameAt(tt float64) (lunarLimbFrame, bool) {
|
||||
if axes == nil {
|
||||
return lunarLimbFrame{}, false
|
||||
}
|
||||
axisX, axisZ, ok := interpolateLocalEphemerisVectors(axes.nodes, tt)
|
||||
if !ok {
|
||||
return lunarLimbFrame{}, false
|
||||
}
|
||||
frame := axes.base
|
||||
if frame.axisZ, ok = lunarLimbNormalize(axisZ); !ok {
|
||||
return lunarLimbFrame{}, false
|
||||
}
|
||||
// 两轴分别插值后不再严格正交,按 z 轴重新正交化 x 轴。
|
||||
if frame.axisX, ok = lunarLimbNormalize(lunarLimbReject(axisX, frame.axisZ)); !ok {
|
||||
return lunarLimbFrame{}, false
|
||||
}
|
||||
frame.axisY = lunarLimbCross(frame.axisZ, frame.axisX)
|
||||
return frame, true
|
||||
}
|
||||
|
||||
func (ephemeris *solarEclipseLocalEphemeris) equatorialAt(tt float64) ([3]float64, [3]float64, bool) {
|
||||
var empty [3]float64
|
||||
if ephemeris == nil {
|
||||
@@ -119,12 +167,14 @@ type starOccultationLocalEphemeris struct {
|
||||
hasDistance bool
|
||||
}
|
||||
|
||||
func newStarOccultationLocalEphemeris(center float64, star StarCoordinate) *starOccultationLocalEphemeris {
|
||||
func newStarOccultationLocalEphemeris(center float64, star StarCoordinate, instances ...*Instance) *starOccultationLocalEphemeris {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
half := occultationLocalEphemerisNodeCount / 2
|
||||
nodes := make([]localEphemerisVectorNode, occultationLocalEphemerisNodeCount)
|
||||
for index := range nodes {
|
||||
tt := center + float64(index-half)*occultationLocalEphemerisStepDays
|
||||
state := starOccultationEphemerisStateAt(tt, star)
|
||||
state := starOccultationEphemerisStateAt(tt, star, inst)
|
||||
moon := occultationPathRaDecVector(state.moonRA, state.moonDec, state.moonDistanceKM)
|
||||
targetDistance := state.starDistanceKM
|
||||
if targetDistance <= 0 {
|
||||
@@ -138,12 +188,14 @@ func newStarOccultationLocalEphemeris(center float64, star StarCoordinate) *star
|
||||
next: [3]float64{target.x, target.y, target.z},
|
||||
}
|
||||
}
|
||||
return newStarOccultationLocalEphemerisFromNodes(center, star, nodes, false)
|
||||
return newStarOccultationLocalEphemerisFromNodes(center, star, nodes, false, inst)
|
||||
}
|
||||
|
||||
// newStarOccultationLocalEphemerisFromNodes 装配局部星历:距离必须与节点同源,密集分支同样要带上。
|
||||
func newStarOccultationLocalEphemerisFromNodes(center float64, star StarCoordinate, nodes []localEphemerisVectorNode, dense bool) *starOccultationLocalEphemeris {
|
||||
_, _, distanceAU := starApparentRaDecDistanceGeocentric(center, star)
|
||||
func newStarOccultationLocalEphemerisFromNodes(center float64, star StarCoordinate, nodes []localEphemerisVectorNode, dense bool, instances ...*Instance) *starOccultationLocalEphemeris {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
_, _, distanceAU := starApparentRaDecDistanceGeocentric(center, star, inst)
|
||||
return &starOccultationLocalEphemeris{
|
||||
star: star, nodes: nodes, dense: dense,
|
||||
distanceKM: distanceAU * occultationPathAstronomicalUnitKM, hasDistance: distanceAU > 0,
|
||||
@@ -195,12 +247,14 @@ type planetOccultationLocalEphemeris struct {
|
||||
dense bool
|
||||
}
|
||||
|
||||
func newPlanetOccultationLocalEphemeris(center float64, config planetOccultationConfig) *planetOccultationLocalEphemeris {
|
||||
func newPlanetOccultationLocalEphemeris(center float64, config planetOccultationConfig, instances ...*Instance) *planetOccultationLocalEphemeris {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
half := occultationLocalEphemerisNodeCount / 2
|
||||
nodes := make([]localEphemerisVectorNode, occultationLocalEphemerisNodeCount)
|
||||
for index := range nodes {
|
||||
tt := center + float64(index-half)*occultationLocalEphemerisStepDays
|
||||
state := planetOccultationEphemerisStateAt(tt, config)
|
||||
state := planetOccultationEphemerisStateAt(tt, config, inst)
|
||||
moon := occultationPathRaDecVector(state.moonRA, state.moonDec, state.moonDistanceKM)
|
||||
target := occultationPathRaDecVector(state.planetRA, state.planetDec, state.planetDistanceKM)
|
||||
nodes[index] = localEphemerisVectorNode{
|
||||
|
||||
Reference in New Issue
Block a user