044f2f4381
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
165 lines
4.4 KiB
Go
165 lines
4.4 KiB
Go
package catalog
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"b612.me/astro/basic"
|
|
)
|
|
|
|
// GaiaDR3Columns 返回 Gaia DR3 归档导出的列映射 / column map for Gaia DR3 archive exports.
|
|
func GaiaDR3Columns() ColumnMap {
|
|
return ColumnMap{
|
|
ID: "source_id", RA: "ra", Dec: "dec", PMRA: "pmra", PMDec: "pmdec",
|
|
Parallax: "parallax", RadialVelocity: "radial_velocity", Magnitude: "phot_g_mean_mag",
|
|
BPRP: "bp_rp", Band: BandG, Epoch: julianYearTime(2016.0), Frame: basic.CoordinateFrameICRS,
|
|
IDPrefix: "Gaia DR3 ", PMRAIncludesCosDec: true,
|
|
}
|
|
}
|
|
|
|
// Hipparcos2007Columns 返回 VizieR 导出的 Hipparcos 2007 新归算 CSV 列映射,角度已换算为度;原始 hip2.dat 用 ReadHipparcos2007 /
|
|
// column map for VizieR CSV exports of Hipparcos 2007 in degrees; use ReadHipparcos2007 for raw hip2.dat.
|
|
func Hipparcos2007Columns() ColumnMap {
|
|
return ColumnMap{
|
|
ID: "HIP", RA: "RArad", Dec: "DErad", PMRA: "pmRA", PMDec: "pmDE",
|
|
Parallax: "Plx", Magnitude: "Hpmag", BV: "B-V", Band: BandHp,
|
|
Epoch: julianYearTime(1991.25), Frame: basic.CoordinateFrameICRS, IDPrefix: "HIP ",
|
|
PMRAIncludesCosDec: true, AnglesInRadians: false,
|
|
}
|
|
}
|
|
|
|
func julianYearTime(year float64) time.Time {
|
|
return basic.JD2DateByZone(2451545.0+(year-2000.0)*365.25, time.UTC, false)
|
|
}
|
|
|
|
func parseMissing(value string) bool {
|
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
|
case "", "nan", "--", "null":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func parseFloat(value string) (float64, bool, error) {
|
|
if parseMissing(value) {
|
|
return 0, false, nil
|
|
}
|
|
parsed, err := strconv.ParseFloat(strings.TrimSpace(value), 64)
|
|
if err != nil {
|
|
return 0, true, err
|
|
}
|
|
return parsed, true, nil
|
|
}
|
|
|
|
func parseInt(value string) (int64, bool, error) {
|
|
if parseMissing(value) {
|
|
return 0, false, nil
|
|
}
|
|
parsed, err := strconv.ParseInt(strings.TrimSpace(value), 10, 64)
|
|
if err != nil {
|
|
return 0, true, err
|
|
}
|
|
return parsed, true, nil
|
|
}
|
|
|
|
func parseUint(value string) (uint64, bool, error) {
|
|
if parseMissing(value) {
|
|
return 0, false, nil
|
|
}
|
|
parsed, err := strconv.ParseUint(strings.TrimSpace(value), 10, 64)
|
|
if err != nil {
|
|
return 0, true, err
|
|
}
|
|
return parsed, true, nil
|
|
}
|
|
|
|
func normalizedFrame(frame basic.CoordinateFrame) basic.CoordinateFrame {
|
|
if frame == "" {
|
|
return basic.CoordinateFrameICRS
|
|
}
|
|
return frame
|
|
}
|
|
|
|
func addEntry(c *Catalog, entry Entry, record int, options ReadOptions) error {
|
|
if entry.ID == "" {
|
|
return recordFailure(c, options, record, "missing ID")
|
|
}
|
|
if err := entry.Coordinate.Validate(); err != nil {
|
|
return recordFailure(c, options, record, err.Error())
|
|
}
|
|
if options.Filter != nil && !options.Filter(entry) {
|
|
return nil
|
|
}
|
|
c.add(entry)
|
|
return nil
|
|
}
|
|
|
|
func recordFailure(c *Catalog, options ReadOptions, record int, reason interface{}) error {
|
|
if options.Strict {
|
|
return fmt.Errorf("record %d: %v", record, reason)
|
|
}
|
|
c.skipped++
|
|
return nil
|
|
}
|
|
|
|
func parseCoordinate(id string, ra, dec, pmRA, pmDec, parallax, radial float64, epoch time.Time, frame basic.CoordinateFrame) basic.StarCoordinate {
|
|
if ra == 360 || ra == -0 {
|
|
ra = 0
|
|
} else {
|
|
ra = normalizeRA(ra)
|
|
}
|
|
// 负视差是测量噪声,按无视差处理;非有限值原样保留,交给 Validate 拒收。
|
|
if finite(parallax) && parallax < 0 {
|
|
parallax = 0
|
|
}
|
|
return basic.StarCoordinate{
|
|
ID: id,
|
|
RA: ra,
|
|
Dec: dec,
|
|
Epoch: epoch,
|
|
Frame: normalizedFrame(frame),
|
|
ProperMotionRACosDecMasPerYear: pmRA,
|
|
ProperMotionDecMasPerYear: pmDec,
|
|
ParallaxMas: parallax,
|
|
RadialVelocityKmPerSecond: radial,
|
|
}
|
|
}
|
|
|
|
func fixedFloat(line []byte, start, end int) (float64, bool, error) {
|
|
value, ok := fixedField(line, start, end)
|
|
if !ok {
|
|
return 0, false, nil
|
|
}
|
|
return parseFloat(value)
|
|
}
|
|
|
|
func fixedInt(line []byte, start, end int) (int64, bool, error) {
|
|
value, ok := fixedField(line, start, end)
|
|
if !ok {
|
|
return 0, false, nil
|
|
}
|
|
return parseInt(value)
|
|
}
|
|
|
|
func fixedField(line []byte, start, end int) (string, bool) {
|
|
if start < 0 || end < start || start >= len(line) || end > len(line) {
|
|
return "", false
|
|
}
|
|
return strings.TrimSpace(string(line[start:end])), true
|
|
}
|
|
|
|
func checkFinite(value float64) error {
|
|
if !finite(value) {
|
|
return fmt.Errorf("non-finite value %v", value)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func angularFromRadians(value float64) float64 {
|
|
return value * 180 / math.Pi
|
|
}
|