feat: 引入可切换星历模型并扩展天象观测链路

- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算
- 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询
- 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出
- 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解
- 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤
- 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持
- 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理
- 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
This commit is contained in:
2026-10-15 23:54:15 +08:00
parent 16c62a97d5
commit 044f2f4381
674 changed files with 62983 additions and 8396 deletions
+27 -11
View File
@@ -11,7 +11,6 @@ import (
// 掩星边界连续性判定的几何门限 / geometric thresholds for occultation-boundary continuity checks.
const (
EarthRadiusKM = 6378.1366
BoundaryBranchJumpKM = 750.0
// BoundaryBranchSpeedKMPerSecond 是判定"物理上不可能的支路跳变"的地面速度门限:
// 掩星边界随月球影子移动,地面速度上限约 1.1 km/s,取 2 km/s 留约两倍余量。
@@ -287,7 +286,7 @@ func nearestPolygonVertex(
distance := math.Inf(1)
for _, polygon := range polygons {
for _, candidate := range polygon {
value := geoDistanceKM(point, candidate)
value := geodata.EquatorialDistanceKM(point, candidate)
if value < distance {
nearest, distance = candidate, value
}
@@ -327,7 +326,7 @@ func nearestPolygonBoundaryPoint(
))
}
candidate := interpolateOccultationGeoPoint(start, end, fraction)
value := geoDistanceKM(point, candidate)
value := geodata.EquatorialDistanceKM(point, candidate)
if value < distance {
nearest, distance = candidate, value
}
@@ -742,12 +741,21 @@ func visibleBandPolygonsFromAnalyticContours(
}
return footprintFill
}
var selectionFill [][]geodata.GeoPoint
var selectionFill, footprintSelection [][]geodata.GeoPoint
if pointSource || len(visibilityContours) == 0 {
// Geocentric limit strips can extend beyond the station-corrected
// stellar envelope or omit a finite-disk horizon extremum. Use visible
// footprints as witnesses; the analytic linework supplies every edge.
selectionFill = getFootprintFill()
if pointSource && len(visibilityContours) == 0 {
// 密集恒星时间轴含大量重叠环;连续界限用于选面,逐环仍用于覆盖见证;界限条带选面失败时退回逐环。
footprintSelection = selectionFill
if limit := occultationLimitVisibleFillPolygons(northern, southern); len(limit) > 0 {
selectionFill = limit
} else if union := footprintVisibleUnionPolygons(footprints); len(union) > 0 {
selectionFill = union
}
}
}
if len(selectionFill) == 0 {
selectionFill = occultationLimitVisibleFillPolygons(northern, southern)
@@ -779,13 +787,21 @@ func visibleBandPolygonsFromAnalyticContours(
if pointSource && len(visibilityContours) == 0 {
initialCoverage = phaseLines
}
polygons, err := polygonize(initialCoverage)
if err != nil && len(phaseLines) > 0 {
// A multi-branch rise/set network can contain several closed faces with
// identical physical junctions. If fill-only selection chooses the
// adjacent face, require every exported phase line as a coverage witness
// and retry without changing the boundary network or snap tolerance.
polygons, err = polygonize(phaseLines)
selectFaces := func() ([][]geodata.GeoPoint, error) {
polygons, err := polygonize(initialCoverage)
if err != nil && len(phaseLines) > 0 {
// A multi-branch rise/set network can contain several closed faces with
// identical physical junctions. If fill-only selection chooses the
// adjacent face, require every exported phase line as a coverage witness
// and retry without changing the boundary network or snap tolerance.
polygons, err = polygonize(phaseLines)
}
return polygons, err
}
polygons, err := selectFaces()
if err != nil && len(footprintSelection) > 0 {
selectionFill = footprintSelection
polygons, err = selectFaces()
}
if err != nil {
if fallback, ok := fallbackVisible(); ok {