feat: 完善日月食与月掩几何链路并扩展历法接口

- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑
- 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口
- 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界
- 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名
- 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验
- 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口
- 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
This commit is contained in:
2026-09-17 12:27:40 +08:00
parent 9ee2163cc7
commit 2bf8478639
428 changed files with 85981 additions and 7998 deletions
+75
View File
@@ -0,0 +1,75 @@
package basic
import (
"math"
"sync"
"sync/atomic"
)
// Nutation2000B 是 jd 的纯函数,但升落、星历与掩星路径会对同一批瞬时反复求值:一条计算链里
// 地球自转、状态上下文与几何装配各自求一次同一个瞬时。这里用有界直接映射表记住结果:
// 槽位固定、无分配、RWMutex 保证 c-shared 宿主多线程安全。章动只依赖传入的 jd,与 ΔT 世代无关。
// Nutation2000B is a pure function of jd, yet the rise/set, ephemeris and occultation chains evaluate
// it repeatedly for the same instants from independent code paths. This bounded direct-mapped memo
// removes that redundancy with fixed slots, no allocation and an RWMutex for the c-shared host.
// Nutation depends only on its argument, so no ΔT generation stamp is needed.
const nutationMemoBits = 13
const nutationMemoSize = 1 << nutationMemoBits
type nutationMemoEntry struct {
// key 是 math.Float64bits(jd)+1,0 表示空槽(避免 jd=0 与空槽同码)。
key uint64
dpsi, deps float64
}
var (
nutationMemoMu sync.RWMutex
nutationMemoTable [nutationMemoSize]nutationMemoEntry
nutationMemoHits uint64
nutationMemoMiss uint64
)
func nutationMemoIndex(jd float64) uint64 {
bits := math.Float64bits(jd)
return (bits ^ (bits >> 31)) & (nutationMemoSize - 1)
}
func nutationMemoLoad(jd float64) (float64, float64, bool) {
key := math.Float64bits(jd) + 1
entry := &nutationMemoTable[nutationMemoIndex(jd)]
nutationMemoMu.RLock()
entryKey, dpsi, deps := entry.key, entry.dpsi, entry.deps
nutationMemoMu.RUnlock()
if key != 0 && entryKey == key {
atomic.AddUint64(&nutationMemoHits, 1)
return dpsi, deps, true
}
atomic.AddUint64(&nutationMemoMiss, 1)
return 0, 0, false
}
func nutationMemoStore(jd, dpsi, deps float64) {
key := math.Float64bits(jd) + 1
if key == 0 {
return
}
nutationMemoMu.Lock()
nutationMemoTable[nutationMemoIndex(jd)] = nutationMemoEntry{key: key, dpsi: dpsi, deps: deps}
nutationMemoMu.Unlock()
}
// nutationMemoStats 返回命中/未命中计数,供测试守护命中率。
func nutationMemoStats() (uint64, uint64) {
return atomic.LoadUint64(&nutationMemoHits), atomic.LoadUint64(&nutationMemoMiss)
}
func resetNutationMemo() {
nutationMemoMu.Lock()
for i := range nutationMemoTable {
nutationMemoTable[i] = nutationMemoEntry{}
}
nutationMemoMu.Unlock()
atomic.StoreUint64(&nutationMemoHits, 0)
atomic.StoreUint64(&nutationMemoMiss, 0)
}