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,193 @@
|
||||
package planets
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// bitReader 按位读取,字节内高位在前。
|
||||
type bitReader struct {
|
||||
b []byte
|
||||
p int // 位位置
|
||||
err error
|
||||
}
|
||||
|
||||
// window 返回从当前位开始的 64 位窗口(高位对齐,越界补零),有效位至少 57 位。
|
||||
func (br *bitReader) window() uint64 {
|
||||
i := br.p >> 3
|
||||
var w uint64
|
||||
if i+8 <= len(br.b) {
|
||||
w = binary.BigEndian.Uint64(br.b[i:])
|
||||
} else {
|
||||
for k := 0; k < 8; k++ {
|
||||
w <<= 8
|
||||
if i+k < len(br.b) {
|
||||
w |= uint64(br.b[i+k])
|
||||
}
|
||||
}
|
||||
}
|
||||
return w << uint(br.p&7)
|
||||
}
|
||||
|
||||
func (br *bitReader) skip(n int) {
|
||||
br.p += n
|
||||
if br.p > 8*len(br.b) {
|
||||
br.err = errors.New("planets: truncated bit stream")
|
||||
}
|
||||
}
|
||||
|
||||
func (br *bitReader) get(n int) uint64 {
|
||||
if n == 0 || br.err != nil {
|
||||
return 0
|
||||
}
|
||||
if n > 56 {
|
||||
hi := br.get(n - 32)
|
||||
return hi<<32 | br.get(32)
|
||||
}
|
||||
v := br.window() >> uint(64-n)
|
||||
br.skip(n)
|
||||
return v
|
||||
}
|
||||
|
||||
// gamma Elias-gamma,编码值为 n+1。
|
||||
func (br *bitReader) gamma() int {
|
||||
if br.err != nil {
|
||||
return 0
|
||||
}
|
||||
z := bits.LeadingZeros64(br.window())
|
||||
if z > 28 {
|
||||
br.err = errors.New("planets: bad gamma code")
|
||||
return 0
|
||||
}
|
||||
v := br.get(2*z + 1)
|
||||
return int(v) - 1
|
||||
}
|
||||
|
||||
func unzigzag(z int) int {
|
||||
if z&1 == 0 {
|
||||
return z >> 1
|
||||
}
|
||||
return -(z >> 1) - 1
|
||||
}
|
||||
|
||||
// readElement3 读取 PLN3 元件位流:包络游程、幅角号(定长或同阶升序间隔)、成对共享位宽的量化系数。
|
||||
func readElement3(r *reader, e *element, secDeg, nk int, sortedArgs bool) error {
|
||||
if secDeg > maxDeg {
|
||||
return errors.New("planets: secular degree too high")
|
||||
}
|
||||
n64 := r.uvar()
|
||||
if n64 > uint64(nk) {
|
||||
return errors.New("planets: too many terms")
|
||||
}
|
||||
nint := int(n64)
|
||||
e.secular = make([]float64, secDeg+1)
|
||||
for i := range e.secular {
|
||||
e.secular[i] = r.f64()
|
||||
}
|
||||
step := r.f64()
|
||||
size := r.uvar()
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
if size > uint64(len(r.b)-r.p) {
|
||||
return errors.New("planets: truncated table")
|
||||
}
|
||||
br := &bitReader{b: r.take(int(size))}
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
env := make([]int, 0, nint)
|
||||
for runs := br.gamma(); runs > 0 && br.err == nil; runs-- {
|
||||
d := int(br.get(4))
|
||||
cnt := br.gamma() + 1
|
||||
if d > maxDeg || len(env)+cnt > nint {
|
||||
return errors.New("planets: bad envelope runs")
|
||||
}
|
||||
for ; cnt > 0; cnt-- {
|
||||
env = append(env, d)
|
||||
}
|
||||
}
|
||||
if br.err != nil {
|
||||
return br.err
|
||||
}
|
||||
if len(env) != nint {
|
||||
return errors.New("planets: envelope count mismatch")
|
||||
}
|
||||
args := make([]int32, nint)
|
||||
nbits := bits.Len(uint(nk - 1))
|
||||
if nbits == 0 {
|
||||
nbits = 1
|
||||
}
|
||||
prev, cur := -1, -1
|
||||
for j := 0; j < nint; j++ {
|
||||
a := 0
|
||||
if sortedArgs {
|
||||
if env[j] != cur {
|
||||
prev, cur = -1, env[j]
|
||||
}
|
||||
a = prev + 1 + br.gamma()
|
||||
prev = a
|
||||
} else {
|
||||
a = int(br.get(nbits))
|
||||
}
|
||||
if a >= nk {
|
||||
return errors.New("planets: argument index out of range")
|
||||
}
|
||||
args[j] = int32(a)
|
||||
}
|
||||
top := 0
|
||||
for _, d := range env {
|
||||
if d > top {
|
||||
top = d
|
||||
}
|
||||
}
|
||||
// 按包络阶降序的稳定排序:pos[j] 为位流第 j 项的新位置
|
||||
pos := make([]int, nint)
|
||||
e.arg = make([]int32, nint)
|
||||
k := 0
|
||||
for d := top; d >= 0; d-- {
|
||||
for j := 0; j < nint; j++ {
|
||||
if env[j] == d {
|
||||
pos[j], e.arg[k] = k, args[j]
|
||||
k++
|
||||
}
|
||||
}
|
||||
}
|
||||
if nint == 0 {
|
||||
return br.err
|
||||
}
|
||||
e.cnt = make([]int, top+1)
|
||||
e.c, e.s = make([][]float64, top+1), make([][]float64, top+1)
|
||||
for d := 0; d <= top; d++ {
|
||||
for j := 0; j < nint; j++ {
|
||||
if env[j] >= d {
|
||||
e.cnt[d]++
|
||||
}
|
||||
}
|
||||
e.c[d], e.s[d] = make([]float64, e.cnt[d]), make([]float64, e.cnt[d])
|
||||
}
|
||||
width := 0
|
||||
coef := func() float64 {
|
||||
neg := br.get(1) != 0
|
||||
v := float64(br.get(width))
|
||||
if neg {
|
||||
v = -v
|
||||
}
|
||||
return v * step
|
||||
}
|
||||
for d := 0; d <= top; d++ {
|
||||
for j := 0; j < nint; j++ {
|
||||
if env[j] < d {
|
||||
continue
|
||||
}
|
||||
width += unzigzag(br.gamma())
|
||||
if width < 0 || width > 63 {
|
||||
return errors.New("planets: bad coefficient width")
|
||||
}
|
||||
e.c[d][pos[j]] = coef()
|
||||
e.s[d][pos[j]] = coef()
|
||||
}
|
||||
}
|
||||
return br.err
|
||||
}
|
||||
Reference in New Issue
Block a user