astro/basic/star_catalog.go

264 lines
6.2 KiB
Go
Raw Normal View History

package basic
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
"sync"
"time"
)
// this file contains bright 9100 stars
// 9100颗亮星列表
type InnerStarData struct {
HR uint16 //Bright Star Number;[1/9110]+ Harvard Revised Number;亮星编号
Name string //Name, generally Bayer(如天狼星Alpha CMA) and/or Flamsteed(如天狼星9 CMA) name
HD uint32 //Henry Draper Catalog Number;HD星表编号
Ra float64 //Ra J2000;J2000历元赤经
Dec float64 //De J2000;J2000历元赤纬
Mag float64 //视星等
PmRA float64 //赤经年自行
PmDec float64 //赤纬年自行
RadVel float64 //径向速度 km/s
RotVel float64 //自行速度 km/s
Pc float64 //秒差距
HIP uint32 //HIP星表编号
}
type StarData struct {
InnerStarData
ChineseName string
ChineseAlias string
ChineseBayerName string
CommonName string
CommonAliasName string
Cst string
CstChinese string
}
func parseStarData(star []byte) (InnerStarData, error) {
var err error
var stardata InnerStarData
if len(star) < 160 {
return stardata, errors.New("invalid stardat")
}
for i := 0; i < 4; i++ {
if star[i] == ' ' {
continue
}
stardata.HR = stardata.HR*10 + uint16(star[i]-48)
}
stardata.Name = string(bytes.TrimSpace(star[4:14]))
for i := 25; i < 31; i++ {
if star[i] == ' ' {
continue
}
stardata.HD = stardata.HD*10 + uint32(star[i]-48)
}
stardata.Ra, err = parseRa(star)
if err != nil {
return stardata, fmt.Errorf("parse ra failed:%v", err)
}
stardata.Dec, err = parseDec(star)
if err != nil {
return stardata, fmt.Errorf("parse dec failed:%v", err)
}
magOri := string(bytes.TrimSpace(star[102:107]))
if magOri != "" {
stardata.Mag, err = strconv.ParseFloat(magOri, 64)
if err != nil {
return stardata, fmt.Errorf("parse mag failed:%v", err)
}
} else {
stardata.Mag = 9999.9999
}
stardata.PmRA, _ = strconv.ParseFloat(string(bytes.TrimSpace(star[148:154])), 64)
stardata.PmDec, _ = strconv.ParseFloat(string(bytes.TrimSpace(star[154:160])), 64)
if len(star) >= 170 {
stardata.RadVel, _ = strconv.ParseFloat(string(bytes.TrimSpace(star[166:170])), 64)
}
if len(star) >= 179 {
stardata.RotVel, _ = strconv.ParseFloat(string(bytes.TrimSpace(star[176:179])), 64)
}
if len(star) > 161 {
rc, _ := strconv.ParseFloat(string(bytes.TrimSpace(star[162:166])), 64)
if rc != 0 {
stardata.Pc = 1 / rc
}
}
return stardata, nil
}
func parseRa(star []byte) (float64, error) {
var sec float64
var err error
ra := float64(0)
for i := 75; i < 77; i++ {
if star[i] == ' ' {
continue
}
ra = ra*10 + float64(star[i]-48)
}
minute := uint8(0)
for i := 77; i < 79; i++ {
if star[i] == ' ' {
continue
}
minute = minute*10 + (star[i] - 48)
}
ori := string(bytes.TrimSpace(star[79:83]))
if ori != "" {
sec, err = strconv.ParseFloat(ori, 64)
if err != nil {
return ra, err
}
}
ra += float64(minute)/60 + sec/3600
return ra * 15, nil
}
func parseDec(star []byte) (float64, error) {
var sec float64
var err error
underZero := false
if star[83] == '-' {
underZero = true
}
dec := float64(0)
for i := 84; i < 86; i++ {
if star[i] == ' ' {
continue
}
dec = dec*10 + float64(star[i]-48)
}
minute := uint8(0)
for i := 86; i < 88; i++ {
if star[i] == ' ' {
continue
}
minute = minute*10 + (star[i] - 48)
}
ori := string(bytes.TrimSpace(star[88:90]))
if ori != "" {
sec, err = strconv.ParseFloat(ori, 64)
if err != nil {
return dec, err
}
}
dec += float64(minute)/60 + sec/3600
if underZero {
dec = -dec
}
return dec, nil
}
var stardat [][]byte
var starhdindex map[string]int
var hr2detail map[uint16][]string
var hr2hip map[uint16]uint32
var chnidx map[string]uint16
var parsedStarData []InnerStarData
var cachedStarData []StarData
var starDataOnce sync.Once
var starDataErr error
func LoadStarData() error {
starDataOnce.Do(func() {
starDataErr = initStarData()
})
return starDataErr
}
func initStarData() error {
data := initStarCatalogData()
stardat = bytes.Split(data, []byte("\n"))
parsedStarData = make([]InnerStarData, len(stardat))
cachedStarData = make([]StarData, len(stardat))
for i, row := range stardat {
parsed, err := parseStarData(row)
if err != nil {
return fmt.Errorf("parse star %d failed: %w", i+1, err)
}
parsedStarData[i] = parsed
cachedStarData[i] = fullStarData(parsed)
}
chnidx = make(map[string]uint16, len(hr2detail)*2)
for hr := 1; hr <= len(cachedStarData); hr++ {
info, ok := hr2detail[uint16(hr)]
if !ok {
continue
}
registerStarChineseIndex(uint16(hr), info[0])
registerStarChineseIndex(uint16(hr), info[1])
}
return nil
}
func registerStarChineseIndex(hr uint16, name string) {
if name == "" {
return
}
chnidx[name] = hr
if strings.HasSuffix(name, "星") {
trimmed := strings.TrimSuffix(name, "星")
if trimmed != "" {
chnidx[trimmed] = hr
}
}
}
func fullStarData(star InnerStarData) StarData {
star.HIP = hr2hip[star.HR]
if info, ok := hr2detail[star.HR]; ok {
return StarData{
InnerStarData: star,
ChineseName: info[0],
ChineseAlias: info[1],
ChineseBayerName: info[2],
CommonName: info[3],
CommonAliasName: "",
Cst: info[5],
CstChinese: info[4],
}
}
return StarData{InnerStarData: star}
}
func StarDataByChinese(name string) (StarData, error) {
if err := LoadStarData(); err != nil {
return StarData{}, err
}
if strings.HasSuffix(name, "星") {
name = strings.TrimSuffix(name, "星")
}
hr, ok := chnidx[name]
if !ok || hr == 0 || int(hr) > len(cachedStarData) {
return StarData{}, errors.New("no such star")
}
return cachedStarData[hr-1], nil
}
func StarDataByHR(hr int) (StarData, error) {
if err := LoadStarData(); err != nil {
return StarData{}, err
}
if hr <= 0 || hr > len(cachedStarData) {
return StarData{}, errors.New("no such star")
}
return cachedStarData[hr-1], nil
}
func (s InnerStarData) RaDecByJde(jde float64) (float64, float64) {
//计算自行
year := ((jde - 2451545.0) / 365.2422)
return Precess(s.Ra+(year*s.PmRA/3600), s.Dec+(year*s.PmDec/3600), 2451545.0, jde)
}
func (s StarData) RaDecByDate(date time.Time) (float64, float64) {
jde := Date2JDE(date.UTC())
return s.RaDecByJde(jde)
}