Files
astro/doc/manual/en/star.md
T
b612 16c62a97d5 feat: 完善时标与天象几何计算并扩展输出接口
- 新增时标、ΔT 模型、质心时间与 UT1 支持
- 改进日月食、月掩、行星事件及路径边界计算
- 完善恒星三维自行与动态距离传播
- 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具
- 整理中英文手册、示例资源及回归测试
2026-09-23 18:55:12 +08:00

17 KiB
Raw Blame History

Stars

中文 | Back to README

Full examples in this manual run from the repository root.

The library ships a catalog of 9100 stars (BSC / HR numbers 1-9110, apparent magnitudes -1.46 to 7.96) and propagates proper motion automatically. The catalog stores J2000 right ascension and declination (InnerStarData.Ra/Dec); to get the position at a given instant you must apply proper motion, precession and nutation through StarData.RaDecByDate(date). Rise/set, topocentric quantities and constellation lookup all expect the corrected coordinates.

Contents

Finding Sirius and its rise time

package main

import (
	"fmt"
	"log"
	"time"

	"b612.me/astro/star"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2020, 1, 1, 8, 8, 8, 0, cst)
	lon, lat, height := 115.0, 40.0, 0.0
	sirius, err := star.StarDataByName("天狼")
	if err != nil {
		log.Fatal(err)
	}
	ra, dec := sirius.RaDecByDate(date)
	rise, err := star.RiseTime(date, ra, dec, lon, lat, height, true)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(star.Constellation(ra, dec, date))
	fmt.Printf("RA=%.6f Dec=%.6f deg\n", ra, dec)
	fmt.Println(rise.Format(time.RFC3339))
}

Catalog coordinates have epoch J2000. RaDecByDate applies proper motion, precession and nutation before date-specific rise/set, altitude or constellation calculations.

Proper motion is propagated in Julian years (365.25 days), and RaDecByDate converts the civil instant to TT before taking the epoch difference. Whenever a record carries a distance (Pc > 0), RaDecByJde advances a full three-dimensional space motion: proper motion supplies the tangential velocity and RadVel the line-of-sight component, the position vector is extrapolated linearly and its direction is taken. Records without a distance fall back to two dimensions, advancing only the two angular components, which is equivalent to treating the star as infinitely distant. The two differ by second-order terms only — the curvature of the great-circle path and the change in angular scale as radial motion alters the distance; even the fastest bright star moves less than about 0.09 arcseconds from it within 26 years.

API Reference

Group Entry points Purpose Units and convention
Constellation Constellation / ConstellationEN / ConstellationCode Chinese name / English name / IAU three-letter code Corrected RA/Dec in degrees plus the instant
Star catalog InitStarDatabase / StarDataByHR / StarDataByName / TopBrightStars Initialise the embedded catalog, look up by HR number or Chinese name, fetch the bright-star sample HR 1-9110; Mag is apparent magnitude
Coordinate correction StarData.RaDecByDate (RaDecByJde on the basic side) Correct a J2000 position to the given instant Returns RA/Dec in degrees
Rise, set, culmination RiseTime / SetTime / CulminationTime (DownTime is a deprecated alias of SetTime) Rise, set and culmination instants Civil instants; polar cases return sentinel errors
Topocentric quantities Altitude / ApparentAltitude / Azimuth / Zenith / ApparentZenith (Apparent) altitude, azimuth, (apparent) zenith distance Degrees
Hour angle and parallactic angle HourAngle / ParallacticAngle Stellar hour angle, zenith-direction parallactic angle Degrees
Sidereal time MeanSiderealTime / ApparentSiderealTime Mean and apparent sidereal time Hours

The star package has no ...N truncated entry points; truncated analytical series live on the matching functions of sun, moon, the planets and coord, where n < 0 uses every built-in term and n >= 0 truncates the series (see those manuals).

The snippets below omit shared preamble variables: date (observation instant, civil time scale), lon/lat (observer longitude/latitude, east/north positive, degrees), height (observer height, ellipsoidal, metres), aero (whether refraction and apparent-radius corrections are applied).

Constellation lookup

sirius, _ := star.StarDataByName("天狼")
ra, dec := sirius.RaDecByDate(date)
fmt.Println(star.Constellation(ra, dec, date))     // 大犬座
fmt.Println(star.ConstellationEN(ra, dec, date))   // Canis Major
fmt.Println(star.ConstellationCode(ra, dec, date)) // CMA

All three share one boundary table and differ only in output convention. The lookup uses the apparent position of the day, so pass the result of RaDecByDate, never the J2000 coordinates from the catalog.

Star catalog

_ = star.InitStarDatabase()
s, _ := star.StarDataByHR(2491)
fmt.Println(s.HR, s.ChineseName, s.CommonName, s.Mag) // 2491 天狼 Sirius -1.46
bright, _ := star.TopBrightStars()
fmt.Println(len(bright), bright[0].HR) // size of the bright-star sample and the leading HR number

The catalog loads lazily behind sync.Once; InitStarDatabase() only warms it up. It is idempotent and surfaces the load error explicitly, and skipping it still works because the first lookup loads the catalog automatically.

TopBrightStars() returns 169 built-in entries around magnitude 3 or brighter, roughly ordered from brighter to dimmer.

basic.StarData adds naming fields on top of InnerStarData, whose conventions are:

Field Meaning
HR / HD / HIP Bright Star number (1-9110) / Henry Draper number / Hipparcos number
Ra / Dec J2000 right ascension and declination in degrees (run RaDecByDate for the apparent position)
Mag Apparent magnitude
PmRA / PmDec Annual proper motion cos(dec)*dRA/dt and in declination, arcseconds per year
RadVel / RotVel Radial and proper-motion velocity, km/s
Pc Distance in parsecs; > 0 propagates proper motion as a 3D space motion
ChineseName / ChineseAlias / ChineseBayerName Chinese name, alias and Bayer designation
CommonName / CommonAliasName Common English name and alias
Cst / CstChinese Constellation name in English and Chinese

Lookup by name matches the catalog's Chinese names only (天狼 for Sirius, 织女一 for Vega); English names are not matched. Use StarDataByHR to look up by number.

Rise, set and culmination

ra, dec := 101.28715533, -16.71611586 // Sirius-like sample coordinates near J2000
rise, _ := star.RiseTime(date, ra, dec, lon, lat, height, true)
set, _ := star.SetTime(date, ra, dec, lon, lat, height, true)
fmt.Println(rise, set)
fmt.Println(star.CulminationTime(date, ra, lon))

With aero true the rise/set solution uses the horizon corrected by refraction and apparent radius; false uses the geometric horizon. CulminationTime needs only right ascension and longitude.

Under a polar night or midnight sun, RiseTime/SetTime return sentinel errors instead of an instant, see below.

Topocentric quantities

fmt.Println(star.Altitude(date, ra, dec, lon, lat))
fmt.Println(star.ApparentAltitude(date, ra, dec, lon, lat, 1010, 10))
fmt.Println(star.Azimuth(date, ra, dec, lon, lat), star.Zenith(date, ra, dec, lon, lat))
fmt.Println(star.HourAngle(date, ra, lon), star.ParallacticAngle(date, ra, dec, lon, lat))

ApparentAltitude/ApparentZenith take two extra arguments, pressure (hPa) and temperature (C), for the refraction correction. ParallacticAngle is the usual input for rotating a camera or a spectrograph slit.

Sidereal time

fmt.Println(star.MeanSiderealTime(date), star.ApparentSiderealTime(date))

Both return hours; the relation between sidereal time, hour angle and horizontal transforms is documented in the coord manual.

Calculating observing quantities for bright stars

bright, _ := star.TopBrightStars()
for _, s := range bright[:3] {
	ra, dec := s.RaDecByDate(date)
	alt := star.ApparentAltitude(date, ra, dec, lon, lat, 1010, 10)
	fmt.Printf("%-6s %-10s mag=%.2f alt=%.3f\n", s.ChineseName, star.ConstellationEN(ra, dec, date), s.Mag, alt)
}

Run at date = 2020-01-01 08:08:08 CST from 115 E, 40 N with pressure 1010 hPa and temperature 10 C, this prints:

天狼     Canis Major    mag=-1.46 alt=-30.180
老人     Carina         mag=-0.72 alt=-48.661
大角     Bootes         mag=-0.04 alt=68.926

Complete example

package main

import (
	"fmt"
	"time"

	"b612.me/astro/star"
	"b612.me/astro/tools"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	// Observation instant.
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)

	// Initialise the star catalog.
	_ = star.InitStarDatabase()
	sirius, _ := star.StarDataByName("天狼")
	ra, dec := sirius.RaDecByDate(date)
	// Sirius rising.
	riseDate, _ := star.RiseTime(date, ra, dec, 115, 40, 0, true)
	fmt.Println(riseDate)
	// Sirius setting.
	setDate, _ := star.SetTime(date, ra, dec, 115, 40, 0, true)
	fmt.Println(setDate)
	fmt.Println(star.Constellation(ra, dec, date))

	// Vega.
	vega, _ := star.StarDataByName("织女一")
	ra, dec = vega.RaDecByDate(time.Date(13600, 1, 1, 0, 0, 0, 0, time.Local))
	// Vega right ascension in 13600 CE.
	fmt.Println(tools.Format(ra/15, 1))
	// Vega declination in 13600 CE.
	fmt.Println(tools.Format(dec, 0))

	bright, _ := star.TopBrightStars()
	fmt.Println(bright[0].ChineseName, bright[0].CommonName, bright[0].Mag)
}

Output:

2019-12-31 19:22:56.144202053 +0800 CST // Sirius rising
2020-01-01 05:30:39.802506566 +0800 CST // Sirius setting
大犬座 // constellation of Sirius
6h3m46.61s // Vega right ascension in 13600 CE
84°18′27.15″ // Vega declination in 13600 CE
天狼 Sirius -1.46 // first bright-star entry: Chinese name, common name, magnitude

The printed constellation name is the Chinese one because Constellation is the Chinese-name entry point; use ConstellationEN for Canis Major and ConstellationCode for CMA.

Bulk lookups and caching

for _, hr := range []int{2491, 2326, 5340} {
	s, err := star.StarDataByHR(hr)
	if err != nil {
		continue
	}
	ra, dec := s.RaDecByDate(date)
	fmt.Println(s.ChineseName, star.ConstellationCode(ra, dec, date), s.Mag)
}

The catalog loads once on first access and is then a read-only cache, so share it across calls and goroutine-free request paths instead of caching it yourself. Unknown numbers or names return an error; in bulk code it is enough to skip on err != nil without classifying the error.

Usage examples

Can I see this star tonight?

ra, dec := sirius.RaDecByDate(date)
fmt.Println(star.RiseTime(date, ra, dec, lon, lat, height, true)) // rise
fmt.Println(star.SetTime(date, ra, dec, lon, lat, height, true))  // set
fmt.Println(star.CulminationTime(date, ra, lon))                  // culmination
fmt.Println(star.ApparentAltitude(date, ra, dec, lon, lat, 1010, 10), star.Azimuth(date, ra, dec, lon, lat))

aero = true solves rise/set against the horizon corrected for refraction and apparent radius, which sits closer to the visual "just above the horizon" moment; false uses the geometric horizon, and the two differ on the order of 2-3 minutes.

Use the apparent altitude to decide whether the star is visible now, with 1010 hPa / 10 C as the usual default weather values. height is ellipsoidal height in metres; when you only have orthometric height, add the geoid undulation first, see Observer height conventions in the README.

Using a J2000 position at a given instant

ra, dec := sirius.RaDecByDate(date)                        // J2000 -> instant (proper motion + precession + nutation)
fmt.Println(star.ApparentSiderealTime(date))               // apparent sidereal time, hours
fmt.Println(star.HourAngle(date, ra, lon))                 // hour angle, degrees

The catalog stores J2000 positions, so skipping RaDecByDate puts rise/set and constellation lookups off by degrees. Sidereal time, horizontal transforms, precession and nutation are covered in Coordinate tools; every time argument is a civil instant (a UTC label, equal to UT1 before 1972-01-01), see Time Scale Declaration.

Constellation lookup and the bright-star catalog

bright, _ := star.TopBrightStars()
for _, s := range bright[:3] {
	ra, dec := s.RaDecByDate(date)
	fmt.Println(s.ChineseName, star.ConstellationCode(ra, dec, date), s.Mag, s.CommonName)
}
天狼 CMA -1.46 Sirius
老人 CAR -0.72 Canopus
大角 BOO -0.04 Arcturus

All three constellation entry points share one boundary table and differ only in output: Constellation gives the Chinese name, ConstellationEN the English name and ConstellationCode the IAU three-letter code; the lookup uses the apparent position of the day. Catalog fields (HR/HD/HIP, J2000 RA/Dec, magnitude, proper motion, parallax distance and the naming fields) are listed as a table under API Reference.

Polar edges and catalog conventions

_, err := star.RiseTime(date, ra, dec, 0, 89, 0, true)
if errors.Is(err, star.ERR_STAR_NEVER_RISE) || errors.Is(err, star.ERR_STAR_NEVER_SET) {
	fmt.Println("该日无升落:", err)
}
  • Polar sentinel errors: RiseTime returns star.ERR_STAR_NEVER_RISE during a polar night (below the horizon all day) and SetTime returns star.ERR_STAR_NEVER_SET during a midnight sun (above it all day); branch with errors.Is.

    ERR_STAR_NEVER_DOWN is a deprecated alias of ERR_STAR_NEVER_SET, and DownTime is a deprecated alias of SetTime.

  • Refraction range: the Saemundsson approximation only applies for true altitudes inside (-5, 90) degrees and contributes zero outside, so well below the horizon the apparent altitude equals the geometric one.

  • Catalog conventions: BSC / HR 1-9110, apparent magnitudes -1.46 to 7.96; name lookup matches the built-in Chinese names only (天狼, 织女一), never English names.

  • Cross-checking against external catalogs: positions here are J2000 mean places with proper motion plus precession and nutation; when comparing term by term, bring both sides to the same instant with RaDecByDate first.

Parameter and result conventions

  • Units: right ascension, declination, altitude, zenith distance, azimuth, hour angle, parallactic angle are in degrees; sidereal time is in hours; tools.Format renders them as degrees-minutes-seconds or hours-minutes-seconds.

    In the catalog PmRA/PmDec are arcseconds per year, RadVel/RotVel are km/s and Pc is parsecs.

  • Time scale: every public time argument and return value is a civil instant (a UTC label, equal to UT1 before 1972-01-01); see Time Scale Declaration.

  • Coordinate convention: the catalog is J2000; RaDecByDate adds proper motion, precession and nutation. Skipping it puts rise/set and constellation lookups off by a large margin (a century of precession is a degree-level shift).

  • Polar sentinel errors: RiseTime returns star.ERR_STAR_NEVER_RISE during a polar night (the star stays below the horizon all day) and SetTime returns star.ERR_STAR_NEVER_SET during a midnight sun (it stays above it); test with errors.Is. ERR_STAR_NEVER_DOWN is a deprecated alias of ERR_STAR_NEVER_SET.

  • Observer height: height is ellipsoidal height in metres; when you only have orthometric height, add the geoid undulation first, as described under "Observer Height Convention" in the README.

  • Refraction range: the Saemundsson approximation behind ApparentAltitude/ApparentZenith only applies for true altitudes inside (-5, 90) degrees and contributes zero outside that range, so well below the horizon the apparent altitude equals the geometric one (the alt=-30.180 above is the same value Altitude returns).

    Pressure must be positive and temperature above absolute zero, otherwise the result is NaN.

  • aero semantics: a true value solves rise/set against the horizon corrected by standard refraction, a false value uses the geometric horizon; the difference is about 2-3 minutes at low latitudes and grows noticeably at high latitudes.

  • Catalog range: HR 1-9110, apparent magnitudes -1.46 to 7.96; name lookup matches the built-in Chinese names only.