044f2f4381
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
225 lines
5.5 KiB
Go
225 lines
5.5 KiB
Go
package basic
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
_ "embed"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math"
|
|
)
|
|
|
|
// starCatalogMagic star_catalog.dat 为 gzip 压缩、按列存储的 uvarint 整数格式。
|
|
const starCatalogMagic = "STRCAT02"
|
|
|
|
const starCatalogVersion = 2
|
|
|
|
const starCatalogDetailFields = 6
|
|
|
|
//go:embed star_catalog.dat
|
|
var starCatalogCompressed []byte
|
|
|
|
// supplementalStarDetails 载荷中无命名条目的星表元数据,字段依次为中文名、别名、拜耳命名、通用名、中文星座、IAU 星座。
|
|
var supplementalStarDetails = map[uint16][]string{
|
|
4799: {"进贤增九", "", "室女座25", "", "室女座", "Virgo"},
|
|
}
|
|
|
|
func initStarCatalogData() []InnerStarData {
|
|
reader, err := gzip.NewReader(bytes.NewReader(starCatalogCompressed))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer reader.Close()
|
|
payload, err := ioutil.ReadAll(reader)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
stars, detail, hip, err := decodeStarCatalogPayload(payload)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
for hr, record := range supplementalStarDetails {
|
|
detail[hr] = record
|
|
}
|
|
hr2detail = detail
|
|
hr2hip = hip
|
|
return stars
|
|
}
|
|
|
|
// starCatalogReader 顺序读取 uvarint 载荷。
|
|
type starCatalogReader struct {
|
|
data []byte
|
|
pos int
|
|
err error
|
|
}
|
|
|
|
func (r *starCatalogReader) uvarint() uint64 {
|
|
if r.err != nil {
|
|
return 0
|
|
}
|
|
value, n := binary.Uvarint(r.data[r.pos:])
|
|
if n <= 0 {
|
|
r.err = errors.New("truncated star catalog")
|
|
return 0
|
|
}
|
|
r.pos += n
|
|
return value
|
|
}
|
|
|
|
func (r *starCatalogReader) bytes(n uint64) []byte {
|
|
if r.err != nil {
|
|
return nil
|
|
}
|
|
if n > uint64(len(r.data)-r.pos) {
|
|
r.err = errors.New("truncated star catalog")
|
|
return nil
|
|
}
|
|
value := r.data[r.pos : r.pos+int(n)]
|
|
r.pos += int(n)
|
|
return value
|
|
}
|
|
|
|
// 解码公式须与编码端一致,运算顺序不可调换,否则结果不再逐位相同。
|
|
|
|
// starCatalogScaled 解码符号-幅值定点数,保留 -0。
|
|
func starCatalogScaled(v uint64, scale float64) float64 {
|
|
value := float64(v>>1) / scale
|
|
if v&1 != 0 {
|
|
return math.Copysign(value, -1)
|
|
}
|
|
return value
|
|
}
|
|
|
|
func starCatalogRa(v uint64) float64 {
|
|
ra := float64(v / 36000)
|
|
minute := (v / 600) % 60
|
|
sec := float64(v%600) / 10
|
|
ra += float64(minute)/60 + sec/3600
|
|
return ra * 15
|
|
}
|
|
|
|
func starCatalogDec(v uint64) float64 {
|
|
total := v >> 1
|
|
dec := float64(total / 3600)
|
|
dec += float64((total/60)%60)/60 + float64(total%60)/3600
|
|
if v&1 != 0 {
|
|
dec = -dec
|
|
}
|
|
return dec
|
|
}
|
|
|
|
func starCatalogMag(v uint64) float64 {
|
|
if v == 0 {
|
|
return 9999.9999
|
|
}
|
|
return starCatalogScaled(v-1, 100)
|
|
}
|
|
|
|
func starCatalogPc(v uint64) float64 {
|
|
if rc := starCatalogScaled(v, 1000); rc != 0 {
|
|
return 1 / rc
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func starCatalogZigzag(v uint64) int64 {
|
|
return int64(v>>1) ^ -int64(v&1)
|
|
}
|
|
|
|
func decodeStarCatalogPayload(payload []byte) ([]InnerStarData, map[uint16][]string, map[uint16]uint32, error) {
|
|
if len(payload) < len(starCatalogMagic)+1 || string(payload[:len(starCatalogMagic)]) != starCatalogMagic {
|
|
return nil, nil, nil, errors.New("invalid star catalog magic")
|
|
}
|
|
if version := payload[len(starCatalogMagic)]; version != starCatalogVersion {
|
|
return nil, nil, nil, fmt.Errorf("unsupported star catalog version %d", version)
|
|
}
|
|
r := &starCatalogReader{data: payload, pos: len(starCatalogMagic) + 1}
|
|
count := r.uvarint()
|
|
if r.err == nil && count > math.MaxUint16 {
|
|
return nil, nil, nil, fmt.Errorf("star catalog count %d out of range", count)
|
|
}
|
|
stars := make([]InnerStarData, count)
|
|
for i := range stars {
|
|
stars[i].HR = uint16(i + 1)
|
|
stars[i].Name = string(r.bytes(r.uvarint()))
|
|
}
|
|
hd := int64(0)
|
|
for i := range stars {
|
|
hd += starCatalogZigzag(r.uvarint())
|
|
stars[i].HD = uint32(hd)
|
|
}
|
|
for i := range stars {
|
|
stars[i].Ra = starCatalogRa(r.uvarint())
|
|
}
|
|
for i := range stars {
|
|
stars[i].Dec = starCatalogDec(r.uvarint())
|
|
}
|
|
for i := range stars {
|
|
stars[i].Mag = starCatalogMag(r.uvarint())
|
|
}
|
|
for i := range stars {
|
|
stars[i].PmRA = starCatalogScaled(r.uvarint(), 1000)
|
|
}
|
|
for i := range stars {
|
|
stars[i].PmDec = starCatalogScaled(r.uvarint(), 1000)
|
|
}
|
|
for i := range stars {
|
|
stars[i].RadVel = starCatalogScaled(r.uvarint(), 1)
|
|
}
|
|
for i := range stars {
|
|
stars[i].RotVel = starCatalogScaled(r.uvarint(), 1)
|
|
}
|
|
for i := range stars {
|
|
stars[i].Pc = starCatalogPc(r.uvarint())
|
|
}
|
|
|
|
stringCount := r.uvarint()
|
|
if r.err == nil && stringCount > uint64(len(payload)) {
|
|
return nil, nil, nil, fmt.Errorf("star catalog string count %d out of range", stringCount)
|
|
}
|
|
stringsTable := make([]string, stringCount+1)
|
|
for i := 1; i < len(stringsTable); i++ {
|
|
stringsTable[i] = string(r.bytes(r.uvarint()))
|
|
}
|
|
indexes := make([][starCatalogDetailFields]uint64, count)
|
|
for k := 0; k < starCatalogDetailFields; k++ {
|
|
for i := range indexes {
|
|
indexes[i][k] = r.uvarint()
|
|
}
|
|
}
|
|
detail := make(map[uint16][]string)
|
|
for i, record := range indexes {
|
|
hasValue := false
|
|
values := make([]string, starCatalogDetailFields)
|
|
for k, index := range record {
|
|
if index >= uint64(len(stringsTable)) {
|
|
return nil, nil, nil, fmt.Errorf("detail string index out of range: hr=%d index=%d", i+1, index)
|
|
}
|
|
values[k] = stringsTable[index]
|
|
hasValue = hasValue || index != 0
|
|
}
|
|
if hasValue {
|
|
detail[uint16(i+1)] = values
|
|
}
|
|
}
|
|
hip := make(map[uint16]uint32)
|
|
prevHIP := int64(0)
|
|
for i := range stars {
|
|
value := r.uvarint()
|
|
if value == 0 {
|
|
continue
|
|
}
|
|
prevHIP += starCatalogZigzag(value - 1)
|
|
hip[uint16(i+1)] = uint32(prevHIP)
|
|
}
|
|
if r.err != nil {
|
|
return nil, nil, nil, r.err
|
|
}
|
|
if r.pos != len(payload) {
|
|
return nil, nil, nil, fmt.Errorf("%d trailing bytes in star catalog", len(payload)-r.pos)
|
|
}
|
|
return stars, detail, hip, nil
|
|
}
|