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

326 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Stars
[中文](../star.md) | [Back to README](../../../README.en.md)
> 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](#finding-sirius-and-its-rise-time)
- [API Reference](#api-reference)
- [Constellation lookup](#constellation-lookup)
- [Star catalog](#star-catalog)
- [Rise, set and culmination](#rise-set-and-culmination)
- [Topocentric quantities](#topocentric-quantities)
- [Sidereal time](#sidereal-time)
- [Calculating observing quantities for bright stars](#calculating-observing-quantities-for-bright-stars)
- [Complete example](#complete-example)
- [Bulk lookups and caching](#bulk-lookups-and-caching)
- [Usage examples](#usage-examples)
- [Can I see this star tonight?](#can-i-see-this-star-tonight)
- [Using a J2000 position at a given instant](#using-a-j2000-position-at-a-given-instant)
- [Constellation lookup and the bright-star catalog](#constellation-lookup-and-the-bright-star-catalog)
- [Polar edges and catalog conventions](#polar-edges-and-catalog-conventions)
- [Parameter and result conventions](#parameter-and-result-conventions)
- [Related manuals](#related-manuals)
## Finding Sirius and its rise time
```go
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
```go
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
```go
_ = 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
```go
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
```go
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
```go
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
```go
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:
```text
天狼 Canis Major mag=-1.46 alt=-30.180
老人 Carina mag=-0.72 alt=-48.661
大角 Bootes mag=-0.04 alt=68.926
```
### Complete example
```go
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:
```text
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
```go
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?
```go
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](coord.md#observer-height) in the README.
### Using a J2000 position at a given instant
```go
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](coord.md); every time argument is a civil instant (a UTC label, equal to UT1 before 1972-01-01), see [Time Scale Declaration](map-geojson.md#time-scale-declaration).
### Constellation lookup and the bright-star catalog
```go
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)
}
```
```text
天狼 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](#star-catalog).
### Polar edges and catalog conventions
```go
_, 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](map-geojson.md#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.
## Related manuals
- Sidereal time, horizontal transforms, precession/nutation and parallactic angle: [Coordinate tools](coord.md)
- Rise/set semantics next to the Sun and Moon: [Sun and Moon](sun-moon.md)
- Time scale in figures: [Time Scale Declaration](map-geojson.md#time-scale-declaration)