245 lines
6.9 KiB
Go
245 lines
6.9 KiB
Go
|
|
package catalog
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bufio"
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"math"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"b612.me/astro/basic"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ReadHipparcos2007 读取 Hipparcos 定宽星表 / read the Hipparcos fixed-width catalog.
|
||
|
|
func ReadHipparcos2007(r io.Reader, options ReadOptions) (*Catalog, error) {
|
||
|
|
catalog := newCatalog()
|
||
|
|
scanner := bufio.NewScanner(r)
|
||
|
|
scanner.Buffer(make([]byte, 64*1024), 2*1024*1024)
|
||
|
|
lineNumber := 0
|
||
|
|
for scanner.Scan() {
|
||
|
|
lineNumber++
|
||
|
|
line := scanner.Bytes()
|
||
|
|
if isIgnorableFixedLine(line) {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
entry, err := hipparcosEntry(line, lineNumber)
|
||
|
|
if err != nil {
|
||
|
|
if options.Strict {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
catalog.skipped++
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if err := addEntry(catalog, entry, lineNumber, options); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if err := scanner.Err(); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
catalog.finalize()
|
||
|
|
return catalog, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func hipparcosEntry(line []byte, lineNumber int) (Entry, error) {
|
||
|
|
hip, present, err := fixedInt(line, 0, 6)
|
||
|
|
if err != nil || !present || hip <= 0 {
|
||
|
|
return Entry{}, fixedError(lineNumber, "invalid HIP")
|
||
|
|
}
|
||
|
|
ra, present, err := fixedFloat(line, 15, 28)
|
||
|
|
if err != nil || !present {
|
||
|
|
return Entry{}, fixedError(lineNumber, "invalid RArad")
|
||
|
|
}
|
||
|
|
dec, present, err := fixedFloat(line, 29, 42)
|
||
|
|
if err != nil || !present {
|
||
|
|
return Entry{}, fixedError(lineNumber, "invalid DErad")
|
||
|
|
}
|
||
|
|
parallax, _, err := fixedFloat(line, 43, 50)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, fixedError(lineNumber, "invalid Plx")
|
||
|
|
}
|
||
|
|
pmRA, _, err := fixedFloat(line, 51, 59)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, fixedError(lineNumber, "invalid pmRA")
|
||
|
|
}
|
||
|
|
pmDec, _, err := fixedFloat(line, 60, 68)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, fixedError(lineNumber, "invalid pmDE")
|
||
|
|
}
|
||
|
|
magnitude, magnitudePresent, err := fixedFloat(line, 129, 136)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, fixedError(lineNumber, "invalid Hpmag")
|
||
|
|
}
|
||
|
|
bv, bvPresent, err := fixedFloat(line, 152, 158)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, fixedError(lineNumber, "invalid B-V")
|
||
|
|
}
|
||
|
|
id := fmt.Sprintf("HIP %d", hip)
|
||
|
|
// 非正视差按无视差处理;非有限值原样保留,交给 Validate 拒收。
|
||
|
|
if finite(parallax) && parallax <= 0 {
|
||
|
|
parallax = 0
|
||
|
|
}
|
||
|
|
if !magnitudePresent {
|
||
|
|
magnitude = math.NaN()
|
||
|
|
}
|
||
|
|
if !bvPresent {
|
||
|
|
bv = math.NaN()
|
||
|
|
}
|
||
|
|
return Entry{
|
||
|
|
ID: id,
|
||
|
|
Coordinate: parseCoordinate(id, angularFromRadians(ra), angularFromRadians(dec), pmRA, pmDec, parallax, 0,
|
||
|
|
julianYearTime(1991.25), basic.CoordinateFrameICRS),
|
||
|
|
Magnitude: magnitude,
|
||
|
|
Band: BandHp,
|
||
|
|
BPRP: math.NaN(),
|
||
|
|
BV: bv,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// ReadBSC5 读取 BSC5 定宽星表 / read the BSC5 fixed-width catalog.
|
||
|
|
func ReadBSC5(r io.Reader, options ReadOptions) (*Catalog, error) {
|
||
|
|
catalog := newCatalog()
|
||
|
|
scanner := bufio.NewScanner(r)
|
||
|
|
scanner.Buffer(make([]byte, 64*1024), 2*1024*1024)
|
||
|
|
lineNumber := 0
|
||
|
|
for scanner.Scan() {
|
||
|
|
lineNumber++
|
||
|
|
line := scanner.Bytes()
|
||
|
|
if isIgnorableFixedLine(line) {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
entry, present, err := bsc5Entry(line, lineNumber)
|
||
|
|
if err != nil {
|
||
|
|
if options.Strict {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
catalog.skipped++
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if !present {
|
||
|
|
catalog.skipped++
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if err := addEntry(catalog, entry, lineNumber, options); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if err := scanner.Err(); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
catalog.finalize()
|
||
|
|
return catalog, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func bsc5Entry(line []byte, lineNumber int) (Entry, bool, error) {
|
||
|
|
hr, present, err := fixedInt(line, 0, 4)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid HR")
|
||
|
|
}
|
||
|
|
if !present || hr <= 0 {
|
||
|
|
return Entry{}, false, nil
|
||
|
|
}
|
||
|
|
name, _ := fixedField(line, 4, 14)
|
||
|
|
hd, _, err := fixedInt(line, 25, 31)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid HD")
|
||
|
|
}
|
||
|
|
sao, _, err := fixedInt(line, 31, 37)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid SAO")
|
||
|
|
}
|
||
|
|
raHour, raHourPresent, err := fixedInt(line, 75, 77)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid RA hour")
|
||
|
|
}
|
||
|
|
raMinute, raMinutePresent, err := fixedInt(line, 77, 79)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid RA minute")
|
||
|
|
}
|
||
|
|
raSecond, raSecondPresent, err := fixedFloat(line, 79, 83)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid RA second")
|
||
|
|
}
|
||
|
|
decSign, decSignPresent := fixedField(line, 83, 84)
|
||
|
|
decDegree, decDegreePresent, err := fixedInt(line, 84, 86)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid Dec degree")
|
||
|
|
}
|
||
|
|
decMinute, decMinutePresent, err := fixedInt(line, 86, 88)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid Dec minute")
|
||
|
|
}
|
||
|
|
decSecond, decSecondPresent, err := fixedInt(line, 88, 90)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid Dec second")
|
||
|
|
}
|
||
|
|
if !raHourPresent || !raMinutePresent || !raSecondPresent || !decSignPresent || !decDegreePresent || !decMinutePresent || !decSecondPresent {
|
||
|
|
return Entry{}, false, nil
|
||
|
|
}
|
||
|
|
ra := (float64(raHour) + float64(raMinute)/60 + raSecond/3600) * 15
|
||
|
|
dec := float64(decDegree) + float64(decMinute)/60 + float64(decSecond)/3600
|
||
|
|
if strings.HasPrefix(decSign, "-") {
|
||
|
|
dec = -dec
|
||
|
|
}
|
||
|
|
vmag, vmPresent, err := fixedFloat(line, 102, 107)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid Vmag")
|
||
|
|
}
|
||
|
|
bv, bvPresent, err := fixedFloat(line, 109, 114)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid B-V")
|
||
|
|
}
|
||
|
|
pmRA, _, err := fixedFloat(line, 148, 154)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid pmRA")
|
||
|
|
}
|
||
|
|
pmDec, _, err := fixedFloat(line, 154, 160)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid pmDE")
|
||
|
|
}
|
||
|
|
parallax, _, err := fixedFloat(line, 161, 166)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid parallax")
|
||
|
|
}
|
||
|
|
radial, _, err := fixedInt(line, 166, 170)
|
||
|
|
if err != nil {
|
||
|
|
return Entry{}, false, fixedError(lineNumber, "invalid radial velocity")
|
||
|
|
}
|
||
|
|
id := fmt.Sprintf("HR %d", hr)
|
||
|
|
aliases := make([]string, 0, 3)
|
||
|
|
if name != "" {
|
||
|
|
aliases = append(aliases, strings.Join(strings.Fields(name), " "))
|
||
|
|
}
|
||
|
|
if hd > 0 {
|
||
|
|
aliases = append(aliases, fmt.Sprintf("HD %d", hd))
|
||
|
|
}
|
||
|
|
if sao > 0 {
|
||
|
|
aliases = append(aliases, fmt.Sprintf("SAO %d", sao))
|
||
|
|
}
|
||
|
|
if !vmPresent {
|
||
|
|
vmag = math.NaN()
|
||
|
|
}
|
||
|
|
if !bvPresent {
|
||
|
|
bv = math.NaN()
|
||
|
|
}
|
||
|
|
// 非正视差按无视差处理;非有限值原样保留,交给 Validate 拒收。
|
||
|
|
if finite(parallax) && parallax <= 0 {
|
||
|
|
parallax = 0
|
||
|
|
}
|
||
|
|
return Entry{
|
||
|
|
ID: id, Aliases: aliases,
|
||
|
|
Coordinate: parseCoordinate(id, ra, dec, pmRA*1000, pmDec*1000, parallax*1000, float64(radial), time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC), basic.CoordinateFrameJ2000),
|
||
|
|
Magnitude: vmag, Band: BandV, BPRP: math.NaN(), BV: bv,
|
||
|
|
}, true, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func isIgnorableFixedLine(line []byte) bool {
|
||
|
|
trimmed := strings.TrimSpace(string(line))
|
||
|
|
return trimmed == "" || strings.HasPrefix(trimmed, "#") || strings.HasPrefix(trimmed, "//")
|
||
|
|
}
|
||
|
|
|
||
|
|
func fixedError(line int, reason string) error {
|
||
|
|
return fmt.Errorf("record %d: %s", line, reason)
|
||
|
|
}
|