feat: 引入可切换星历模型并扩展天象观测链路
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
This commit is contained in:
@@ -72,28 +72,26 @@ type planetOccultationDiagramTime struct {
|
||||
labels []string
|
||||
}
|
||||
|
||||
// PlanetOccultationDiagram 为已求解的固定地点行星月掩计算以月心为原点的切平面轨迹。事件数据无效或不完整时,结果不含帧。
|
||||
// PlanetOccultationDiagram computes a Moon-centered tangent-plane track for an already solved fixed-site planetary occultation. Invalid or incomplete event data produces a result without frames.
|
||||
func PlanetOccultationDiagram(
|
||||
// PlanetOccultationDiagramWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
|
||||
func PlanetOccultationDiagramWithInstance(
|
||||
info PlanetOccultationInfo,
|
||||
options PlanetOccultationDiagramOptions,
|
||||
) PlanetOccultationDiagramResult {
|
||||
options PlanetOccultationDiagramOptions, inst *Instance) PlanetOccultationDiagramResult {
|
||||
options = normalizePlanetOccultationDiagramOptions(options)
|
||||
result := PlanetOccultationDiagramResult{Occultation: info, StepDays: options.StepDays}
|
||||
if !planetOccultationDiagramInputValid(info) {
|
||||
return result
|
||||
}
|
||||
|
||||
config, ok := planetOccultationConfigFor(info.Planet)
|
||||
config, ok := planetOccultationConfigFor(info.Planet, inst)
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
startTT := occultationTimeToTT(info.ExternalImmersion)
|
||||
greatestTT := occultationTimeToTT(info.Greatest)
|
||||
endTT := occultationTimeToTT(info.ExternalEmersion)
|
||||
externalImmersionFrame, externalImmersionOK := planetOccultationDiagramFrameAt(startTT, config, info.Observer)
|
||||
greatestFrame, greatestOK := planetOccultationDiagramFrameAt(greatestTT, config, info.Observer)
|
||||
externalEmersionFrame, externalEmersionOK := planetOccultationDiagramFrameAt(endTT, config, info.Observer)
|
||||
externalImmersionFrame, externalImmersionOK := planetOccultationDiagramFrameAt(startTT, config, info.Observer, inst)
|
||||
greatestFrame, greatestOK := planetOccultationDiagramFrameAt(greatestTT, config, info.Observer, inst)
|
||||
externalEmersionFrame, externalEmersionOK := planetOccultationDiagramFrameAt(endTT, config, info.Observer, inst)
|
||||
if !externalImmersionOK || !greatestOK || !externalEmersionOK {
|
||||
return result
|
||||
}
|
||||
@@ -103,10 +101,10 @@ func PlanetOccultationDiagram(
|
||||
)
|
||||
if info.HasInternalContacts {
|
||||
internalImmersionFrame, internalImmersionOK = planetOccultationDiagramFrameAt(
|
||||
occultationTimeToTT(info.InternalImmersion), config, info.Observer,
|
||||
occultationTimeToTT(info.InternalImmersion), config, info.Observer, inst,
|
||||
)
|
||||
internalEmersionFrame, internalEmersionOK = planetOccultationDiagramFrameAt(
|
||||
occultationTimeToTT(info.InternalEmersion), config, info.Observer,
|
||||
occultationTimeToTT(info.InternalEmersion), config, info.Observer, inst,
|
||||
)
|
||||
}
|
||||
if (info.HasInternalContacts && (!internalImmersionOK || !internalEmersionOK)) ||
|
||||
@@ -120,7 +118,7 @@ func PlanetOccultationDiagram(
|
||||
result.StepDays = stepDays
|
||||
result.Frames = make([]PlanetOccultationDiagramFrame, 0, len(times))
|
||||
for _, item := range times {
|
||||
frame, frameOK := planetOccultationDiagramFrameAt(item.jde, config, info.Observer)
|
||||
frame, frameOK := planetOccultationDiagramFrameAt(item.jde, config, info.Observer, inst)
|
||||
if !frameOK {
|
||||
return PlanetOccultationDiagramResult{Occultation: info, StepDays: stepDays}
|
||||
}
|
||||
@@ -142,8 +140,10 @@ func planetOccultationDiagramMatchesInfo(
|
||||
!finite(info.PlanetSemidiameterArcsec) || info.PlanetSemidiameterArcsec <= 0 {
|
||||
return false
|
||||
}
|
||||
if math.Abs(externalImmersion.SeparationArcsec-externalImmersion.MoonRadiusArcsec-externalImmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec ||
|
||||
math.Abs(externalEmersion.SeparationArcsec-externalEmersion.MoonRadiusArcsec-externalEmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec {
|
||||
// 已按月缘剖面收敛的接触不在圆月面上,圆月面接触判据只适用于未修正的结果。
|
||||
checkContacts := !info.LunarLimbApplied
|
||||
if checkContacts && (math.Abs(externalImmersion.SeparationArcsec-externalImmersion.MoonRadiusArcsec-externalImmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec ||
|
||||
math.Abs(externalEmersion.SeparationArcsec-externalEmersion.MoonRadiusArcsec-externalEmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec) {
|
||||
return false
|
||||
}
|
||||
if math.Abs(greatest.SeparationArcsec-info.MinimumSeparationArcsec) > planetOccultationDiagramGeometryArcsec ||
|
||||
@@ -153,6 +153,10 @@ func planetOccultationDiagramMatchesInfo(
|
||||
return false
|
||||
}
|
||||
|
||||
// 月缘结果的食型按剖面判定,圆月面掩甚间隙不再适用。
|
||||
if info.LunarLimbApplied {
|
||||
return info.Type == OccultationPartial || info.Type == OccultationTotal || info.Type == OccultationGrazing
|
||||
}
|
||||
externalMetric := greatest.SeparationArcsec - greatest.MoonRadiusArcsec - greatest.PlanetRadiusArcsec
|
||||
internalMetric := greatest.SeparationArcsec - greatest.MoonRadiusArcsec + greatest.PlanetRadiusArcsec
|
||||
switch info.Type {
|
||||
@@ -164,8 +168,8 @@ func planetOccultationDiagramMatchesInfo(
|
||||
math.Abs(startTT-greatestTT) <= planetOccultationDiagramContactTimeDays &&
|
||||
math.Abs(endTT-greatestTT) <= planetOccultationDiagramContactTimeDays
|
||||
case OccultationTotal:
|
||||
if math.Abs(internalImmersion.SeparationArcsec-internalImmersion.MoonRadiusArcsec+internalImmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec ||
|
||||
math.Abs(internalEmersion.SeparationArcsec-internalEmersion.MoonRadiusArcsec+internalEmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec {
|
||||
if checkContacts && (math.Abs(internalImmersion.SeparationArcsec-internalImmersion.MoonRadiusArcsec+internalImmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec ||
|
||||
math.Abs(internalEmersion.SeparationArcsec-internalEmersion.MoonRadiusArcsec+internalEmersion.PlanetRadiusArcsec) > planetOccultationDiagramGeometryArcsec) {
|
||||
return false
|
||||
}
|
||||
return externalMetric < -planetOccultationGrazingToleranceArcsec &&
|
||||
@@ -291,9 +295,10 @@ func planetOccultationDiagramLabelPriority(labels []string) int {
|
||||
func planetOccultationDiagramFrameAt(
|
||||
tt float64,
|
||||
config planetOccultationConfig,
|
||||
observer Observer,
|
||||
) (PlanetOccultationDiagramFrame, bool) {
|
||||
state := planetOccultationStateAt(tt, config, &observer, -1)
|
||||
observer Observer, instances ...*Instance) (PlanetOccultationDiagramFrame, bool) {
|
||||
inst := instanceValue(instances)
|
||||
|
||||
state := planetOccultationStateAt(tt, config, &observer, -1, inst)
|
||||
if !state.valid {
|
||||
return PlanetOccultationDiagramFrame{}, false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user