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

56 KiB

Planets

中文 | Back to README

The seven major planets each have a package of their own: mercury, venus, mars, jupiter, saturn, uranus, and neptune.

They share the same API shape: a civil instant is passed as time.Time, angles and distances come back as float64, and event searches return time.Time or a struct.

The inner planets (Mercury and Venus) add superior/inferior conjunction, greatest elongation, and geocentric transit; the outer planets (Mars through Neptune) add opposition and quadrature; Jupiter alone has the Galilean satellites and Saturn alone has ring parameters. The low-level VSOP87 series and the solar/lunar analytic series live in the planet package, shared by these seven packages and by sun / moon.

  • The function names of the common capabilities are identical across the seven packages (for example all of them provide ApparentRa, ApparentDec, and ApparentRaDec); only the extra families differ.

    Always qualify a call with its package name instead of mixing packages.

  • Position functions return a geocentric apparent place.

    Topocentric quantities and horizontal coordinates are separate: Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle, with formulas under Coordinate Tools.

  • Event-search families come as Last... / Next... pairs (some also Closest...) and always return the nearest event at or before/after the input instant, endpoints included.

  • The planets have no dedicated SVG chart entry point; occultation-related charts (including Saturn-ring occultations) are documented in Lunar Occultations.

Contents

The position and rise time of Mars

package main

import (
	"fmt"
	"log"
	"time"

	"b612.me/astro/mars"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2020, 1, 1, 8, 8, 8, 0, cst)
	lon, lat, height := 108.93, 34.27, 0.0
	ra, dec := mars.ApparentRaDec(date)
	fmt.Printf("RA=%.6f Dec=%.6f deg\n", ra, dec)
	rise, err := mars.RiseTime(date, lon, lat, height, true)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(rise.Format(time.RFC3339))
}

ApparentRaDec returns geocentric apparent right ascension and declination in degrees. Rise/set also needs a site and ellipsoidal height, and returns an error when no rise event exists.

API Reference

Cross-planet comparison

Rows list capabilities and columns list planetary packages. Qualify each function with its package, for example mars.NextOpposition. A slash separates function names; — means the package has no corresponding API.

Capability mercury venus mars jupiter saturn uranus neptune
Apparent ecliptic longitude / latitude ApparentLo / ApparentBo ApparentLo / ApparentBo ApparentLo / ApparentBo ApparentLo / ApparentBo ApparentLo / ApparentBo ApparentLo / ApparentBo ApparentLo / ApparentBo
Apparent right ascension / declination ApparentRa / ApparentDec / ApparentRaDec ApparentRa / ApparentDec / ApparentRaDec ApparentRa / ApparentDec / ApparentRaDec ApparentRa / ApparentDec / ApparentRaDec ApparentRa / ApparentDec / ApparentRaDec ApparentRa / ApparentDec / ApparentRaDec ApparentRa / ApparentDec / ApparentRaDec
Apparent magnitude ApparentMagnitude ApparentMagnitude ApparentMagnitude ApparentMagnitude ApparentMagnitude ApparentMagnitude ApparentMagnitude
Geocentric / heliocentric distance EarthDistance / SunDistance EarthDistance / SunDistance EarthDistance / SunDistance EarthDistance / SunDistance EarthDistance / SunDistance EarthDistance / SunDistance EarthDistance / SunDistance
Orbital ascending / descending node AscendingNode / DescendingNode AscendingNode / DescendingNode AscendingNode / DescendingNode AscendingNode / DescendingNode AscendingNode / DescendingNode AscendingNode / DescendingNode AscendingNode / DescendingNode
Apparent diameter / semidiameter Diameter / Semidiameter Diameter / Semidiameter Diameter / Semidiameter Diameter / Semidiameter Diameter / Semidiameter Diameter / Semidiameter Diameter / Semidiameter
Phase angle / illuminated fraction / bright-limb position angle PhaseAngle / Phase / IlluminatedFraction / BrightLimbPositionAngle PhaseAngle / Phase / IlluminatedFraction / BrightLimbPositionAngle PhaseAngle / Phase / IlluminatedFraction / BrightLimbPositionAngle PhaseAngle / Phase / IlluminatedFraction / BrightLimbPositionAngle PhaseAngle / Phase / IlluminatedFraction / BrightLimbPositionAngle PhaseAngle / Phase / IlluminatedFraction / BrightLimbPositionAngle PhaseAngle / Phase / IlluminatedFraction / BrightLimbPositionAngle
Topocentric horizontal quantities Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle
Rise / set / culmination RiseTime / SetTime / DownTime / CulminationTime RiseTime / SetTime / DownTime / CulminationTime RiseTime / SetTime / DownTime / CulminationTime RiseTime / SetTime / DownTime / CulminationTime RiseTime / SetTime / DownTime / CulminationTime RiseTime / SetTime / DownTime / CulminationTime RiseTime / SetTime / DownTime / CulminationTime
Physical ephemeris Physical / PhysicalN Physical / PhysicalN Physical / PhysicalN Physical / PhysicalN / CentralMeridians / CentralMeridiansN Physical / PhysicalN / PhysicalSystemIII / Ring Physical / PhysicalN / PhysicalSystemIII Physical / PhysicalN
Conjunction LastConjunction / NextConjunction LastConjunction / NextConjunction LastConjunction / NextConjunction LastConjunction / NextConjunction LastConjunction / NextConjunction LastConjunction / NextConjunction LastConjunction / NextConjunction
Superior / inferior conjunction LastSuperiorConjunction / NextSuperiorConjunction / LastInferiorConjunction / NextInferiorConjunction LastSuperiorConjunction / NextSuperiorConjunction / LastInferiorConjunction / NextInferiorConjunction — — — — —
Station LastProgradeToRetrograde / NextProgradeToRetrograde / LastRetrogradeToPrograde / NextRetrogradeToPrograde, plus LastRetrograde / NextRetrograde LastProgradeToRetrograde / NextProgradeToRetrograde / LastRetrogradeToPrograde / NextRetrogradeToPrograde, plus LastRetrograde / NextRetrograde LastProgradeToRetrograde / NextProgradeToRetrograde / LastRetrogradeToPrograde / NextRetrogradeToPrograde LastProgradeToRetrograde / NextProgradeToRetrograde / LastRetrogradeToPrograde / NextRetrogradeToPrograde LastProgradeToRetrograde / NextProgradeToRetrograde / LastRetrogradeToPrograde / NextRetrogradeToPrograde LastProgradeToRetrograde / NextProgradeToRetrograde / LastRetrogradeToPrograde / NextRetrogradeToPrograde LastProgradeToRetrograde / NextProgradeToRetrograde / LastRetrogradeToPrograde / NextRetrogradeToPrograde
Opposition — — LastOpposition / NextOpposition LastOpposition / NextOpposition LastOpposition / NextOpposition LastOpposition / NextOpposition LastOpposition / NextOpposition
Quadrature — — LastEasternQuadrature / NextEasternQuadrature / LastWesternQuadrature / NextWesternQuadrature LastEasternQuadrature / NextEasternQuadrature / LastWesternQuadrature / NextWesternQuadrature LastEasternQuadrature / NextEasternQuadrature / LastWesternQuadrature / NextWesternQuadrature LastEasternQuadrature / NextEasternQuadrature / LastWesternQuadrature / NextWesternQuadrature LastEasternQuadrature / NextEasternQuadrature / LastWesternQuadrature / NextWesternQuadrature
Greatest elongation LastGreatestElongation / NextGreatestElongation / LastGreatestElongationEast / NextGreatestElongationEast / LastGreatestElongationWest / NextGreatestElongationWest LastGreatestElongation / NextGreatestElongation / LastGreatestElongationEast / NextGreatestElongationEast / LastGreatestElongationWest / NextGreatestElongationWest — — — — —
Geocentric transit LastTransit / NextTransit / ClosestTransit LastTransit / NextTransit / ClosestTransit — — — — —
Galilean satellites — — — Satellites / SatellitePhenomena / LastGalileanPhenomenonEvent / NextGalileanPhenomenonEvent / ClosestGalileanPhenomenonEvent / LastGalileanPhenomenonContactEvent / NextGalileanPhenomenonContactEvent / ClosestGalileanPhenomenonContactEvent — — —
Result types PhysicalInfo / TransitInfo PhysicalInfo / TransitInfo PhysicalInfo PhysicalInfo / CentralMeridianInfo / GalileanSatellitesInfo / GalileanPhenomenaInfo / GalileanSatellitePosition / GalileanSatellitePhenomenon / GalileanPhenomenonEvent / GalileanPhenomenonContactEvent PhysicalInfo / RingInfo PhysicalInfo PhysicalInfo

All seven packages spell the truncation family the same way: append N to any instantaneous evaluation function, where n < 0 uses the full built-in series and n >= 0 truncates it.

See the ...N truncation family for details. Event-search families (Last... / Next... / Closest...) have no N variants.

Common capabilities and units

Capability Purpose Unit and convention
ApparentLo / ApparentBo Geocentric apparent ecliptic longitude and latitude degrees; true equinox of date, including light time, aberration, and nutation
ApparentRa / ApparentDec / ApparentRaDec Geocentric apparent right ascension and declination degrees; true equator of date; ApparentRaDec returns both at once
ApparentMagnitude Apparent visual magnitude magnitudes (dimensionless)
Altitude / Azimuth / Zenith / HourAngle Topocentric horizontal coordinates degrees; azimuth runs from north toward east, and Zenith equals 90 - Altitude
RiseTime / SetTime / DownTime Rise and set within the local civil day time.Time keeping the input time zone; DownTime is a compatibility alias for SetTime
CulminationTime Upper culmination instant time.Time keeping the input time zone
ParallacticAngle Parallactic angle (zenith direction angle) degrees
Diameter / Semidiameter Geocentric apparent diameter and semidiameter arcseconds
PhaseAngle Sun-planet-Earth angle degrees
IlluminatedFraction / Phase Illuminated fraction 0-1; Phase is an alias for IlluminatedFraction
BrightLimbPositionAngle Position angle of the bright-limb center degrees
EarthDistance / SunDistance Earth distance and Sun distance AU
AscendingNode / DescendingNode Ecliptic longitude of the orbital plane's intersections with the ecliptic degrees; the two differ by about 180° at the same instant
Physical Disk orientation, sub-Earth/sub-Solar coordinates, north-pole position angle degrees; the positive longitude direction follows each body's IAU convention
planet.WherePlanet / planet.WherePlanetN VSOP87 longitude, latitude, and heliocentric distance degrees / AU; out-of-range input returns NaN instead of panicking

The ...N truncation family

Every instantaneous evaluation function has an ...N variant that trades accuracy for cost: n < 0 uses the full built-in series and is equivalent to the plain function, while n >= 0 keeps roughly n principal terms and scales the higher orders proportionally.

Event-search families (Last... / Next... / Closest...) have no N variant.

Functions with an N variant include ApparentLoN, ApparentBoN, ApparentRaN, ApparentDecN, ApparentRaDecN, ApparentMagnitudeN, EarthDistanceN, SunDistanceN, AltitudeN, AzimuthN, ZenithN, HourAngleN, CulminationTimeN, RiseTimeN, SetTimeN, DownTimeN, ParallacticAngleN, DiameterN, SemidiameterN, PhaseAngleN, PhaseN, IlluminatedFractionN, BrightLimbPositionAngleN, AscendingNodeN, DescendingNodeN, and PhysicalN, plus CentralMeridiansN for Jupiter, RingN and PhysicalSystemIIIN for Saturn, and PhysicalSystemIIIN for Uranus.

fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8))   // ecliptic longitude: full series / truncated to about 8 terms
fmt.Println(mars.SunDistance(date), mars.SunDistanceN(date, 8)) // heliocentric distance, AU

Usage examples

Which planet can I see tonight?

fmt.Println(venus.RiseTime(date, lon, lat, height, true))                // Venus rises today
fmt.Println(jupiter.CulminationTime(date, lon))                          // Jupiter's upper culmination
fmt.Println(mars.Altitude(date, lon, lat), mars.Azimuth(date, lon, lat)) // Mars's altitude and azimuth now
2020-01-01 10:02:34.350145161 +0800 CST <nil>
2020-01-01 12:32:17.585815787 +0800 CST
31.194578177219057 152.07031660415714

Above the horizon means Altitude greater than 0, and azimuth increases from due north towards the east; aero = true lowers the geometric horizon to about -0.5667° for standard refraction.

Per-parameter conventions and the polar sentinel errors are in Rise, set and culmination.

Oppositions, conjunctions, elongations, stations and retrogrades

fmt.Println(mars.NextOpposition(date))             // Mars's next opposition
fmt.Println(jupiter.NextConjunction(date))         // Jupiter's next conjunction
fmt.Println(saturn.NextProgradeToRetrograde(date)) // Saturn's next prograde-to-retrograde station
2020-10-14 07:25:50.441412627 +0800 CST
2021-01-29 09:39:33.697994649 +0800 CST
2020-05-11 17:26:53.961271941 +0800 CST

Event searches return the nearest event at or after the input instant and keep its time zone; the paired Last... functions and the direction-agnostic NextRetrograde are in Conjunctions, oppositions, stations and quadratures;

Mercury's and Venus's NextGreatestElongationEast / ...West are in Greatest elongation and geocentric transits.

Mercury and Venus transits

transit := mercury.NextTransit(date)                           // next geocentric Mercury transit after 2020
fmt.Println(transit.Valid, transit.Start, transit.Greatest)     // whether one was found, first contact, greatest transit
fmt.Println(transit.Duration, transit.MinimumSeparationArcsec)  // duration and minimum separation at greatest transit
true 2032-11-13 14:41:13.161198198 +0800 CST 2032-11-13 16:54:12.821315824 +0800 CST
4h26m2.695272267s 572.0643215495325

TransitInfo.Valid false means no transit inside the search window and every other field is the zero value; a transit is a geocentric geometry test and does not check whether the Sun is above the horizon at a given site.

The four contacts, partial transits and the internal-contact fields are in Greatest elongation and geocentric transits.

Phase, diameter, magnitude and nodes

fmt.Println(venus.PhaseAngle(date), venus.Phase(date))           // phase angle (degrees) and illuminated fraction
fmt.Println(venus.Diameter(date), venus.ApparentMagnitude(date)) // apparent diameter (arcseconds) and magnitude
fmt.Println(mars.AscendingNode(date), mars.DescendingNode(date)) // ascending/descending node longitudes (degrees)
49.98145049145023 0.8215177914415865
13.059409604614839 -4
49.71479005849112 229.71479005849113

Phase is an alias of IlluminatedFraction and runs from 0 to 1; the two nodes are about 180° apart at any instant.

Definitions, aliases and the truncated versions are in Nodes, phase, magnitude, diameter and parallactic angle.

Physical ephemerides and the Galilean satellites

j := jupiter.Physical(date)                                    // Jupiter's physical ephemeris
fmt.Println(j.DS, j.DE, j.CentralMeridianSystemIII)            // sub-solar/sub-Earth latitude and System III central meridian
sats := jupiter.Satellites(date)                               // instantaneous positions of the four Galilean satellites
fmt.Println(sats.Io.OffsetXJupiterR, sats.Io.InFrontOfJupiter) // Io's offset and whether it is in front of the disk
-56.55778470155335 -2.039966127259664 311.37430665615585
3.8223302102343975 false

Saturn's rings have their own saturn.Ring (EarthLatitude, MinorAxis, and so on; angles in degrees and axes in arcseconds). Disk orientation and the central meridians are covered in Physical ephemerides; the Galilean-satellite public entry points are in the jupiter package, while JupiterGalilean* in basic is the low-level entry that takes a Julian day, see Galilean satellites of Jupiter.

Comparison conventions

fmt.Println(mars.ApparentLo(date), mars.ApparentLoN(date, 8)) // apparent longitude with every term / truncated to about 8
_, err := mars.RiseTime(date, 0, 89, 0, true)                 // an observing site in the polar region
fmt.Println(errors.Is(err, mars.ERR_MARS_NEVER_RISE), errors.Is(err, mars.ERR_MARS_NEVER_SET))
238.38840227925655 238.39637464888327
true false

ApparentLo and friends are geocentric apparent places referred to the true equinox of date; bring external J2000 or mean places to the same convention with coord.Precess before comparing.

In the truncation family n < 0 uses every built-in term and n >= 0 truncates.

The convention gap between the Galilean contact events and JPL Horizons / IMCCE tables is documented under External baselines, and the overall boundaries under Parameter and result conventions.

Basic examples

The two examples below are the smallest runnable programs and use date = 2020-01-01 08:08:08 CST with the Xi'an coordinates.

Inner planets

package main

import (
	"fmt"
	"time"

	"b612.me/astro/mercury"
	"b612.me/astro/venus"
)

func main() {
	// Xi'an, China. Longitude east and latitude north are positive; elevation is 0 m.
	var lon, lat, height float64 = 108.93, 34.27, 0
	cst := time.FixedZone("CST", 8*3600)
	// Instant of observation.
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)

	// Previous inferior conjunction of Mercury.
	fmt.Println(mercury.LastInferiorConjunction(date))
	// Next superior conjunction of Venus.
	fmt.Println(venus.NextSuperiorConjunction(date))
	// Previous Mercury station from prograde to retrograde.
	fmt.Println(mercury.LastProgradeToRetrograde(date))
	// Next Venus station from retrograde to prograde.
	fmt.Println(venus.NextRetrogradeToPrograde(date))
	// Previous greatest eastern elongation of Mercury.
	fmt.Println(mercury.LastGreatestElongationEast(date))
	// Next greatest western elongation of Venus.
	fmt.Println(venus.NextGreatestElongationWest(date))
	// Venus rise and set times in Xi'an.
	fmt.Println(venus.RiseTime(date, lon, lat, height, true))
	fmt.Println(venus.SetTime(date, lon, lat, height, true))
	// Current apparent magnitude of Venus.
	fmt.Println(venus.ApparentMagnitude(date))
	// Venus phase angle, illuminated fraction, and bright-limb position angle.
	fmt.Println(venus.PhaseAngle(date))
	fmt.Println(venus.Phase(date))
	fmt.Println(venus.BrightLimbPositionAngle(date))
	// Earth-Venus distance.
	fmt.Println(venus.EarthDistance(date))
	// Sun-Venus distance.
	fmt.Println(venus.SunDistance(date))
}

Output:

2019-11-11 23:21:41.971051096 +0800 CST // previous inferior conjunction of Mercury
2021-03-26 14:57:42.052354216 +0800 CST // next superior conjunction of Venus
2019-11-01 04:31:49.749019145 +0800 CST // previous Mercury station from prograde to retrograde
2020-06-25 02:07:41.599749326 +0800 CST // next Venus station from retrograde to prograde
2019-10-20 12:01:37.740152478 +0800 CST // previous greatest eastern elongation of Mercury
2020-08-13 08:14:46.304587125 +0800 CST // next greatest western elongation of Venus
2020-01-01 10:02:34.172435402 +0800 CST <nil> // Venus rise time in Xi'an; no error
2020-01-01 20:25:37.36411482 +0800 CST <nil> // Venus set time in Xi'an; no error
-4 // Venus apparent magnitude
49.98145049145023 // Venus phase angle, degrees
0.8215177914415865 // illuminated fraction of Venus
255.63802053541346 // bright-limb position angle of Venus, degrees
1.2778819631550336 // Earth-Venus distance, AU
0.7262651056423838 // Sun-Venus distance, AU

Inner and outer planets also expose Diameter / Semidiameter and N variants, returning geocentric apparent diameter/semidiameter in arcseconds.

Planet apparent diameter and orbital nodes can be queried directly:

fmt.Println(mars.Diameter(date), mars.Semidiameter(date))                 // Martian apparent diameter and semidiameter, arcseconds
fmt.Println(venus.AscendingNode(date), venus.DescendingNode(date))         // Venus ascending-node and descending-node ecliptic longitudes, degrees

Ascending node / descending node here means the two intersections of the body's orbital plane with the ecliptic:

  • AscendingNode: ecliptic longitude where the body crosses from south of the ecliptic to north of it
  • DescendingNode: ecliptic longitude where the body crosses from north of the ecliptic to south of it
  • return values are degrees; for the same instant, descending node is usually about 180° from ascending node

For date := 2020-01-01 08:08:08 CST, the output is:

4.287299886569956 2.143649943284978 // Mars apparent diameter and semidiameter, arcseconds
76.86008484515058 256.8600848451506 // Venus ascending-node and descending-node longitudes, degrees

Mercury and Venus also expose NextTransit / LastTransit / ClosestTransit for geocentric planetary transits. "Geocentric" means the planet disk crosses the solar disk as seen from Earth's center; it does not test whether the Sun is above the horizon at a particular observing site. For observing plans, combine this with local solar altitude and weather.

package main

import (
	"fmt"
	"time"

	"b612.me/astro/mercury"
	"b612.me/astro/venus"
)

func main() {
	// Next geocentric Mercury transit after the beginning of 2019.
	mercuryTransit := mercury.NextTransit(time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC))
	fmt.Println(mercuryTransit.Valid)
	fmt.Println(mercuryTransit.Start)
	fmt.Println(mercuryTransit.InternalStart)
	fmt.Println(mercuryTransit.Greatest)
	fmt.Println(mercuryTransit.InternalEnd)
	fmt.Println(mercuryTransit.End)
	fmt.Println(mercuryTransit.Duration)
	fmt.Println(mercuryTransit.MinimumSeparationArcsec)
	fmt.Println(mercuryTransit.SunSemidiameterArcsec)
	fmt.Println(mercuryTransit.PlanetSemidiameterArcsec)

	// Next geocentric Venus transit after the beginning of 2012.
	venusTransit := venus.NextTransit(time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC))
	fmt.Println(venusTransit.Valid)
	fmt.Println(venusTransit.Start)
	fmt.Println(venusTransit.InternalStart)
	fmt.Println(venusTransit.Greatest)
	fmt.Println(venusTransit.InternalEnd)
	fmt.Println(venusTransit.End)
	fmt.Println(venusTransit.Duration)
}

Output:

true // a valid geocentric Mercury transit was found
2019-11-11 12:35:31.567597389 +0000 UTC // first contact: Mercury externally enters the solar disk
2019-11-11 12:37:12.817581295 +0000 UTC // second contact: Mercury is fully inside the solar disk
2019-11-11 15:19:48.36056292 +0000 UTC // greatest transit: Mercury center is closest to the Sun center
2019-11-11 18:02:29.176982045 +0000 UTC // third contact: Mercury starts leaving the solar disk
2019-11-11 18:04:10.637948513 +0000 UTC // fourth contact: Mercury externally leaves the solar disk
5h28m39.070351124s // geocentric transit duration from first to fourth contact
75.92400059923187 // minimum Mercury-Sun center separation at greatest transit, arcseconds
968.8881519533047 // solar semidiameter at greatest transit, arcseconds
4.978442871670873 // Mercury semidiameter at greatest transit, arcseconds
true // a valid geocentric Venus transit was found
2012-06-05 22:09:47.466886639 +0000 UTC // first contact: Venus externally enters the solar disk
2012-06-05 22:27:35.865356326 +0000 UTC // second contact: Venus is fully inside the solar disk
2012-06-06 01:29:35.572371482 +0000 UTC // greatest transit: Venus center is closest to the Sun center
2012-06-06 04:31:35.068444311 +0000 UTC // third contact: Venus starts leaving the solar disk
2012-06-06 04:49:23.25597167 +0000 UTC // fourth contact: Venus externally leaves the solar disk
6h39m35.789085031s // geocentric transit duration from first to fourth contact

Outer planets

package main

import (
	"fmt"
	"time"

	"b612.me/astro/jupiter"
	"b612.me/astro/mars"
	"b612.me/astro/neptune"
	"b612.me/astro/saturn"
	"b612.me/astro/uranus"
)

func main() {
	// Xi'an, China. Longitude east and latitude north are positive; elevation is 0 m.
	var lon, lat, height float64 = 108.93, 34.27, 0
	cst := time.FixedZone("CST", 8*3600)
	// Instant of observation.
	date := time.Date(2020, 1, 1, 8, 8, 8, 8, cst)

	// Next opposition of Mars.
	fmt.Println(mars.NextOpposition(date))
	// Next conjunction of Jupiter.
	fmt.Println(jupiter.NextConjunction(date))
	// Previous Saturn station from prograde to retrograde.
	fmt.Println(saturn.LastProgradeToRetrograde(date))
	// Saturn ring observing parameters.
	ring := saturn.Ring(date)
	fmt.Printf("saturn B=%.6f Bp=%.6f P=%.6f dU=%.6f major=%.6f minor=%.6f\n",
		ring.EarthLatitude,
		ring.SunLatitude,
		ring.PositionAngle,
		ring.DeltaU,
		ring.MajorAxis,
		ring.MinorAxis,
	)
	// Next Uranus station from retrograde to prograde.
	fmt.Println(uranus.NextRetrogradeToPrograde(date))
	// Previous eastern quadrature of Neptune.
	fmt.Println(neptune.LastEasternQuadrature(date))
	// Next western quadrature of Mars.
	fmt.Println(mars.NextWesternQuadrature(date))
	// Mars rise and set times in Xi'an.
	fmt.Println(mars.RiseTime(date, lon, lat, height, true))
	fmt.Println(mars.SetTime(date, lon, lat, height, true))
	// Current apparent magnitude of Mars.
	fmt.Println(mars.ApparentMagnitude(date))
	// Earth-Mars distance.
	fmt.Println(mars.EarthDistance(date))
	// Sun-Mars distance.
	fmt.Println(mars.SunDistance(date))
}

Output:

2020-10-14 07:25:50.441412627 +0800 CST // next opposition of Mars
2021-01-29 09:39:33.697994649 +0800 CST // next conjunction of Jupiter
2019-04-30 10:28:00.187439918 +0800 CST // previous Saturn station from prograde to retrograde
saturn B=23.577025 Bp=23.266930 P=6.629811 dU=1.171016 major=34.133852 minor=13.652911 // Saturn ring B, B', P, dU, major axis, minor axis
2020-01-11 15:23:23.360308706 +0800 CST // next Uranus station from retrograde to prograde
2019-12-08 17:00:15.517960488 +0800 CST // previous eastern quadrature of Neptune
2020-06-07 03:11:00.026179254 +0800 CST // next western quadrature of Mars
2020-01-01 04:41:29.621566236 +0800 CST <nil> // Mars rise time in Xi'an; no error
2020-01-01 14:55:32.963508367 +0800 CST <nil> // Mars set time in Xi'an; no error
1.57 // Mars apparent magnitude
2.1844284956325937 // Earth-Mars distance, AU
1.5897860004265403 // Sun-Mars distance, AU

saturn.Ring returns RingInfo: EarthLatitude is ring opening angle B, SunLatitude is B', PositionAngle is the position angle of the northern semiminor axis, DeltaU is the Saturnicentric longitude difference between the Sun and Earth in the ring plane, and MajorAxis / MinorAxis are the apparent outer major/minor axes in arcseconds.

Topic examples

The snippets below are grouped by topic and all build on the shared variables date, lon, lat, and height; complete runnable versions are in the basic examples above.

Position and coordinates

ApparentLo / ApparentBo / ApparentRa / ApparentDec / ApparentRaDec return a geocentric apparent place: the geometric geocentric position corrected for light time, aberration, and nutation, then converted to the true equator and equinox of date.

These packages do not provide J2000 or mean-place output; for a J2000 frame, use coord.Precess for precession and coord.Nutation2000B for nutation, and use coord.TopocentricEquatorial and coord.EquatorialToHorizontal for topocentric quantities. See Coordinate Tools.

// Apparent place of date: ecliptic and equatorial coordinates, degrees.
lo, bo := venus.ApparentLo(date), venus.ApparentBo(date)
ra, dec := venus.ApparentRaDec(date)
fmt.Println(lo, bo, venus.ApparentRa(date), venus.ApparentDec(date), ra, dec)

// Earth distance and Sun distance, AU.
fmt.Println(venus.EarthDistance(date), venus.SunDistance(date))

Rise, set and culmination

RiseTime / SetTime / DownTime work on the local civil day only: date selects the local date and the output time zone (when the local hour is greater than 12 it is first shifted back by 12 hours), height is the ellipsoidal height in meters, and aero == true adds standard atmospheric refraction (lowering the geometric horizon to about -0.5667°).

Polar day, polar night, or a day with no crossing returns a sentinel error instead of an instant. CulminationTime gives upper culmination; Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle form the topocentric family, with geometry under Coordinate Tools.

// Rise, set, and culmination on the local civil day in Xi'an; aero=true adds refraction.
rise, err := mars.RiseTime(date, lon, lat, height, true)
set, err := mars.SetTime(date, lon, lat, height, true)
fmt.Println(rise, set, err)
fmt.Println(mars.CulminationTime(date, lon))
// Topocentric horizontal quantities: east-positive longitude, north-positive latitude, degrees.
fmt.Println(mars.Altitude(date, lon, lat), mars.Azimuth(date, lon, lat))
fmt.Println(mars.Zenith(date, lon, lat), mars.HourAngle(date, lon))
fmt.Println(mars.ParallacticAngle(date, lon, lat))
// DownTime is a compatibility alias for SetTime; the N variant evaluates a truncated series.
set, err := mars.DownTime(date, lon, lat, height, true)
fmt.Println(set, err)
fmt.Println(mars.CulminationTimeN(date, lon, 12))

Conjunctions, oppositions, stations and quadratures

Event searches always return the nearest event at or before/after the input instant (endpoints included), in the input time zone. The inner planets use LastConjunction / NextConjunction plus the superior/inferior conjunction family; the outer planets use LastConjunction / NextConjunction, LastOpposition / NextOpposition, and the eastern/western quadrature family.

Stations come in two directions: LastProgradeToRetrograde / NextProgradeToRetrograde (prograde to retrograde) and LastRetrogradeToPrograde / NextRetrogradeToPrograde (retrograde to prograde); Mercury and Venus additionally have LastRetrograde / NextRetrograde, which ignore the direction, and the outer planets do not have those two names.

For the inner planets a conjunction is simply "on the same side as the Sun"; the outer planets additionally split opposition and quadrature: only the five planets outside Earth's orbit can reach opposition (Sun-Earth-planet in a line) or eastern/western quadrature (about 90° of ecliptic longitude from the Sun).

Mercury and Venus stay inside Earth's orbit, so those two families exist only in the outer-planet packages.

// Inner planets: superior/inferior conjunction, and direction-agnostic stations.
fmt.Println(mercury.LastSuperiorConjunction(date), mercury.NextInferiorConjunction(date))
fmt.Println(mercury.LastRetrograde(date), mercury.NextRetrograde(date))
// Outer planets: conjunction, opposition, and eastern/western quadrature.
fmt.Println(jupiter.NextConjunction(date), mars.NextOpposition(date))
fmt.Println(mars.NextEasternQuadrature(date), neptune.LastWesternQuadrature(date))
// Both station directions have Last/Next forms.
fmt.Println(saturn.LastProgradeToRetrograde(date), saturn.NextProgradeToRetrograde(date))
fmt.Println(saturn.LastRetrogradeToPrograde(date), saturn.NextRetrogradeToPrograde(date))

Greatest elongation and geocentric transits

Greatest elongation only makes sense for Mercury and Venus: LastGreatestElongation / NextGreatestElongation ignore the side, while LastGreatestElongationEast / NextGreatestElongationEast and the ...West forms distinguish it.

The public entry points for geocentric transits are likewise only in mercury / venus: LastTransit / NextTransit / ClosestTransit return a TransitInfo.

Valid == false means no transit inside the search window and every other field is a zero value; a partial transit has no internal contacts, so HasInternal is false and InternalStart / InternalEnd are zero.

A transit is a purely geocentric geometry test and does not check whether the Sun is above the horizon at a given site.

// Greatest elongation: use ...East / ...West to pick a side, or the plain form for either.
fmt.Println(mercury.NextGreatestElongationEast(date), venus.LastGreatestElongationWest(date))
fmt.Println(venus.NextGreatestElongation(date))
// Geocentric transit: when Valid is false every other field is a zero value.
transit := mercury.NextTransit(date)
if transit.Valid {
	fmt.Println(transit.Start, transit.InternalStart, transit.Greatest, transit.InternalEnd, transit.End)
	fmt.Println(transit.Duration, transit.MinimumSeparationArcsec, transit.SunSemidiameterArcsec)
}
// Remaining transit fields; InternalDuration is 0 when there are no internal contacts.
transit := venus.ClosestTransit(date)
fmt.Println(transit.HasInternal, transit.InternalDuration, transit.MinimumSeparationArcsec)
fmt.Println(transit.SunSemidiameterArcsec, transit.PlanetSemidiameterArcsec)

Nodes, phase, magnitude, diameter and parallactic angle

AscendingNode / DescendingNode are the ecliptic longitudes of the two intersections of the planet's orbital plane with the ecliptic, in degrees, about 180° apart at the same instant.

PhaseAngle is the Sun-planet-Earth angle in degrees; IlluminatedFraction (alias Phase) is the illuminated fraction from 0 to 1; BrightLimbPositionAngle is the position angle of the bright-limb center in degrees.

Diameter / Semidiameter return the geocentric apparent diameter and semidiameter in arcseconds. ParallacticAngle returns the topocentric parallactic angle (zenith direction angle) in degrees; topocentric coordinates are under Coordinate Tools.

fmt.Println(mars.AscendingNode(date), mars.DescendingNode(date))                     // node longitudes, degrees
fmt.Println(mars.PhaseAngle(date), mars.IlluminatedFraction(date), mars.Phase(date)) // phase angle in degrees; illuminated fraction
fmt.Println(mars.ApparentMagnitude(date), mars.BrightLimbPositionAngle(date))        // apparent magnitude; bright-limb position angle
fmt.Println(mars.Diameter(date), mars.Semidiameter(date)) // apparent diameter and semidiameter, arcseconds
fmt.Println(mars.ParallacticAngle(date, lon, lat))        // parallactic angle, degrees
// Nodes and apparent diameter also have truncated variants.
fmt.Println(mars.AscendingNodeN(date, 12), mars.DescendingNodeN(date, 12))
fmt.Println(mars.DiameterN(date, 12), mars.SemidiameterN(date, 12))

Division of labour with other manuals

  • Sidereal time, precession and nutation, and topocentric/horizontal conversion are in Coordinate Tools.
  • Sun and Moon positions, phases, rise/set, and syzygies are in the Sun and Moon manual.
  • Planetary occultations, occultation bands, and lunar-disk charts are in Lunar Occultations.
  • Asteroids, comets, and other bodies computed from orbital elements are in Small-body orbits.
  • Time-scale declarations, UT1 conventions, and GeoJSON output are in Event Maps and GeoJSON; the scale convention itself is in Time Scale Declaration.
  • The authoritative capability list, dependency matrix, and accuracy summary are in the root README.

Physical ephemerides

All seven major planets provide Physical / PhysicalN for disk orientation, sub-Earth/sub-Sun coordinates, and north-pole position angle. Jupiter additionally exposes System I/II/III central meridians, and Saturn exposes ring parameters.

package main

import (
	"fmt"
	"time"

	"b612.me/astro/jupiter"
	"b612.me/astro/saturn"
)

func main() {
	date := time.Date(2025, 11, 1, 0, 0, 0, 0, time.UTC)

	// Jupiter: DS and DE are planetocentric declinations of the Sun and Earth relative to Jupiter's equator.
	// CMI/CMII/CMIII are Jupiter System I/II/III central meridians, degrees.
	j := jupiter.Physical(date)
	fmt.Printf("jupiter DS=%.6f DE=%.6f CMI=%.6f CMII=%.6f CMIII=%.6f\n",
		j.DS,
		j.DE,
		j.CentralMeridianSystemI,
		j.CentralMeridianSystemII,
		j.CentralMeridianSystemIII,
	)

	// Saturn ring: B/B' are ring-plane latitudes seen from Earth and Sun; P is the position angle of the ring minor axis.
	ring := saturn.Ring(date)
	fmt.Printf("saturn B=%.6f Bp=%.6f P=%.6f major=%.6f minor=%.6f\n",
		ring.EarthLatitude,
		ring.SunLatitude,
		ring.PositionAngle,
		ring.MajorAxis,
		ring.MinorAxis,
	)
}

Output:

jupiter DS=54.342153 DE=1.436485 CMI=292.712909 CMII=276.309048 CMIII=147.241811 // Jupiter DS/DE and System I/II/III central meridians, degrees
saturn B=-0.608048 Bp=-2.675677 P=4.480276 major=42.709920 minor=0.453248 // Saturn ring B, B', minor-axis position angle, outer major/minor axes

If only Jupiter central meridians are needed:

cm := jupiter.CentralMeridians(date)
fmt.Printf("CMI=%.6f CMII=%.6f CMIII=%.6f\n", cm.SystemI, cm.SystemII, cm.SystemIII) // Jupiter System I/II/III central meridians

Saturn and Uranus also retain explicit System III semantic aliases:

sat3 := saturn.PhysicalSystemIII(date)
ura3 := uranus.PhysicalSystemIII(date)
fmt.Printf("saturn systemIII lon=%.6f lat=%.6f P=%.6f\n", sat3.SubEarthLongitude, sat3.SubEarthLatitude, sat3.NorthPolePositionAngle) // Saturn sub-Earth longitude/latitude and north-pole position angle
fmt.Printf("uranus systemIII lon=%.6f lat=%.6f P=%.6f\n", ura3.SubEarthLongitude, ura3.SubEarthLatitude, ura3.NorthPolePositionAngle) // Uranus sub-Earth longitude/latitude and north-pole position angle

All seven packages return their own PhysicalInfo, whose fields mirror basic.PlanetPhysicalInfo one for one.

The positive direction of SubEarthLongitude / SubSolarLongitude follows each body's current IAU/Horizons cartographic convention: Mercury, Mars, Jupiter, Saturn, and Neptune use west-positive longitudes, while Venus and Uranus use east-positive longitudes.

Saturn-ring parameters describe only the disk and band; they take no part in occultation contact geometry, and charts are in Lunar Occultations.

p := uranus.Physical(date) // Uranus sub-Earth/sub-Solar coordinates and north-pole position angle, degrees
fmt.Println(p.SubEarthLongitude, p.SubEarthLatitude, p.SubSolarLongitude, p.SubSolarLatitude, p.NorthPolePositionAngle)
// Truncated variants of the Saturn ring and the Jupiter central meridians.
fmt.Println(saturn.RingN(date, 12).MinorAxis, jupiter.CentralMeridiansN(date, 12).SystemIII)

Galilean satellites of Jupiter

The public entry points are in the jupiter package (jupiter.Satellites, jupiter.SatellitePhenomena, jupiter.NextGalileanPhenomenonEvent, and so on); they take a time.Time and return Jupiter's own types.

The JupiterGalilean* functions in basic (such as basic.JupiterGalileanSatelliteObservations and basic.NextJupiterGalileanPhenomenonEvent) are the low-level entries of the same implementation: they take a Julian day and return basic types.

Applications should use the jupiter layer.

// Entry point is in the jupiter package; satellite numbers use jupiter.GalileanSatelliteIo and friends.
sats := jupiter.Satellites(date)
fmt.Println(sats.Io.OffsetXJupiterR, sats.Io.OffsetYJupiterR, sats.Io.InFrontOfJupiter)

Satellite numbers and phenomenon types have named constants, so there is no need to write raw numbers or strings:

  • Satellite numbers: GalileanSatelliteIo, GalileanSatelliteEuropa, GalileanSatelliteGanymede, GalileanSatelliteCallisto
  • Phenomenon types (GalileanPhenomenonType): GalileanPhenomenonTransit, GalileanPhenomenonOccultation, GalileanPhenomenonEclipse, GalileanPhenomenonShadowTransit
  • Contact phases (GalileanPhenomenonContactPhase): GalileanPhenomenonContactDisappearance, GalileanPhenomenonContactReappearance
// Satellite numbers and phenomenon types are constants, so no raw numbers or strings are needed.
event := jupiter.NextGalileanPhenomenonEvent(date, jupiter.GalileanSatelliteCallisto, jupiter.GalileanPhenomenonShadowTransit)
fmt.Println(event.Type == jupiter.GalileanPhenomenonShadowTransit)

The jupiter package provides apparent positions, instantaneous phenomena, and event searches for the four Galilean satellites.

Common entry points:

  • Satellites: instantaneous apparent positions relative to Jupiter's disk
  • SatellitePhenomena: instantaneous transit, occultation, eclipse, and shadow-transit flags
  • LastGalileanPhenomenonEvent / NextGalileanPhenomenonEvent / ClosestGalileanPhenomenonEvent: search whole phenomenon intervals
  • LastGalileanPhenomenonContactEvent / NextGalileanPhenomenonContactEvent / ClosestGalileanPhenomenonContactEvent: search IMCCE-style D/F contact events

Two conventions matter:

  • GalileanPhenomenonEvent treats the satellite as a point and checks when its center enters or leaves Jupiter's disk. It is suitable for fast phenomenon search and internal state checks.
  • GalileanPhenomenonContactEvent includes the finite disk of the satellite and splits disappearance and reappearance contact windows. It is the better match for IMCCE tables such as TR.D/TR.F/OC.D/OC.F/EC.D/EC.F/SH.D/SH.F.

The two conventions may differ by up to about 7 minutes in duration. This is a definition difference, not a timing-accuracy failure. Use GalileanPhenomenonContactEvent for observing predictions and direct comparison with public almanacs.

Code example

package main

import (
	"fmt"
	"time"

	"b612.me/astro/jupiter"
)

func main() {
	date := time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC)

	// Instantaneous positions of the four satellites relative to Jupiter's center.
	sats := jupiter.Satellites(date)
	fmt.Printf("io x=%.6f y=%.6f front=%v\n", sats.Io.OffsetXJupiterR, sats.Io.OffsetYJupiterR, sats.Io.InFrontOfJupiter)
	fmt.Printf("europa ra=%.6f dec=%.6f\n", sats.Europa.ApparentRA, sats.Europa.ApparentDec)

	// Instantaneous phenomenon flags.
	ph := jupiter.SatellitePhenomena(date)
	fmt.Printf("io transit=%v occultation=%v eclipse=%v shadow=%v\n", ph.Io.Transit, ph.Io.Occultation, ph.Io.Eclipse, ph.Io.ShadowTransit)
	fmt.Printf("europa transit=%v occultation=%v eclipse=%v shadow=%v\n", ph.Europa.Transit, ph.Europa.Occultation, ph.Europa.Eclipse, ph.Europa.ShadowTransit)

	// Next full Io transit event.
	event := jupiter.NextGalileanPhenomenonEvent(date, jupiter.GalileanSatelliteIo, jupiter.GalileanPhenomenonTransit)
	fmt.Printf("event valid=%v sat=%d type=%s\n", event.Valid, event.Satellite, event.Type)
	fmt.Println(event.Start)
	fmt.Println(event.Greatest)
	fmt.Println(event.End)
	fmt.Println(event.Duration)

	// Next IMCCE-style contact window for a Europa occultation.
	contact := jupiter.NextGalileanPhenomenonContactEvent(date, jupiter.GalileanSatelliteEuropa, jupiter.GalileanPhenomenonOccultation)
	fmt.Printf("contact valid=%v sat=%d type=%s\n", contact.Valid, contact.Satellite, contact.Type)
	fmt.Println(contact.Disappearance.Start)
	fmt.Println(contact.Disappearance.ModelCrossing)
	fmt.Println(contact.Disappearance.End)
	fmt.Println(contact.Greatest)
	fmt.Println(contact.Reappearance.Start)
	fmt.Println(contact.Reappearance.ModelCrossing)
	fmt.Println(contact.Reappearance.End)
}

Output:

io x=-0.675026 y=-0.032798 front=true // Io X/Y offset from Jupiter center, in Jupiter radii; in front of Jupiter
europa ra=110.769133 dec=22.335828 // Europa apparent RA and Dec, degrees
io transit=true occultation=false eclipse=false shadow=true // Io is transiting, and its shadow is also transiting
europa transit=false occultation=false eclipse=false shadow=false // Europa has no transit, occultation, eclipse, or shadow transit at this instant
event valid=true sat=1 type=transit // next valid event is an Io transit
2026-01-16 16:32:47.552742362 +0000 UTC // Io transit begins
2026-01-16 17:40:44.189371168 +0000 UTC // midpoint of the Io transit
2026-01-16 18:48:40.287077128 +0000 UTC // Io transit ends
2h15m52.734334766s // Io transit duration
contact valid=true sat=2 type=occultation // next valid contact event is a Europa occultation
2026-01-17 01:00:34.99533087 +0000 UTC // Europa occultation disappearance starts
2026-01-17 01:02:31.714070141 +0000 UTC // model center crossing during disappearance
2026-01-17 01:04:28.432809412 +0000 UTC // disappearance ends
2026-01-17 02:27:37.807798683 +0000 UTC // deepest occultation
2026-01-17 03:50:48.120300471 +0000 UTC // reappearance starts
2026-01-17 03:52:43.901527225 +0000 UTC // model center crossing during reappearance
2026-01-17 03:54:39.68275398 +0000 UTC // reappearance ends

External baselines

The Galilean-satellite implementation was checked against two external baselines:

  • JPL Horizons: apparent positions of the four satellites relative to Jupiter's center, and shadow-center offsets from Jupiter's disk during shadow transits.
  • IMCCE 2026 tables: transits, occultations, Jupiter eclipses, shadow transits, and D/F contact windows.

Comparison results:

  • Satellites positions relative to Jupiter center: maximum sample difference against JPL Horizons about X=0.054", Y=0.048".
  • SatellitePhenomena shadow-transit shadow-center offsets: maximum sample difference against JPL Horizons about X=0.051", Y=0.016"; boolean phenomenon flags match in the samples.
  • GalileanPhenomenonContactEvent differs from IMCCE 2026 D/F contact times by at most about 79 s, and contact durations by about 17 s in the compared cases.
  • GalileanPhenomenonEvent uses a different definition from IMCCE D/F contacts, so start and end times can differ by about 7 min.

Shared types and constants from the planet package

The planet package is the low-level analytic-series entry point, shared by the seven planet packages and by sun / moon.

It exports functions only and no types, so what the planet packages really share is one set of numerical conventions and truncation semantics rather than shared types: each package's PhysicalInfo mirrors basic.PlanetPhysicalInfo and TransitInfo mirrors basic.PlanetTransitResult, field for field, while the types themselves still belong to their own packages.

Entry point Purpose Unit
planet.WherePlanet / planet.WherePlanetN VSOP87 result: xt is 1..7 for Mercury through Neptune and -1 or 0 for Earth, zn is 0 longitude, 1 latitude, 2 heliocentric distance; an out-of-range xt / zn returns NaN instead of panicking degrees / AU
planet.Distance Sun-Earth distance AU
planet.SunLo / planet.SunM / planet.SunMidFun / planet.SunTrueLo / planet.SunApparentLo Solar geometric longitude, mean anomaly, equation of center, true longitude, apparent longitude degrees
planet.Earthe / planet.EarthPI Earth's orbital eccentricity and longitude of perihelion dimensionless / degrees
planet.MoonLo / planet.MoonM / planet.MoonLonX / planet.SunMoonAngle Lunar mean longitude, mean anomaly, mean argument of latitude, and mean elongation degrees
planet.MoonI / planet.MoonB / planet.MoonR Periodic longitude, latitude, and distance terms (truncated ELP2000/82-style series) 10⁻⁶ degrees / 10⁻⁶ degrees / 10⁻³ km
planet.MoonTrueLo / planet.MoonTrueBo / planet.MoonAway Lunar true longitude, true latitude, and Earth distance degrees / degrees / km
// xt=1..7 is Mercury..Neptune; zn=0 longitude, 1 latitude, 2 heliocentric distance (AU).
fmt.Println(planet.WherePlanet(4, 2, 2460000.5))      // Jupiter heliocentric distance, AU
fmt.Println(planet.WherePlanetN(4, 2, 2460000.5, 12)) // truncated form, about 12 principal terms
fmt.Println(planet.WherePlanet(8, 0, 2460000.5))      // xt out of range: NaN
// Earth's heliocentric longitude (xt=-1) and a planet's can be compared on the same convention.
fmt.Println(planet.WherePlanet(-1, 0, 2460000.5), planet.WherePlanet(4, 0, 2460000.5))

Parameter and result conventions

The conventions below are shared by all seven packages; per-capability return units are summarised under Common capabilities and units.

Time scale and civil time

Every public API treats its time.Time argument as a civil instant (a UTC label): position and physical functions take date.UTC() and convert to TT internally before evaluating the ephemeris, while rise/set, culmination, and the topocentric horizontal family additionally read date.Zone() for local-time computation.

UTC had no leap seconds before 1972-01-01; the library treats that span as UT1, so a civil-time label there is equal to UT1.

From 1972 on the built-in leap-second table is used, and beyond the exact window the active UTC tracking policy applies. For explicit conversion use astro.UT1FromUTC, astro.TTFromUTC, and astro.DUT1 from the root package; time-scale declarations inside figures and captions and the UT1 convention are in Time Scale Declaration.

Units and conventions

Angles are always in degrees; apparent diameter and semidiameter are in arcseconds; distances follow the function name -- EarthDistance / SunDistance and planet.WherePlanet with zn=2 are in AU, while planet.MoonAway is in km.

PhaseAngle is in degrees, IlluminatedFraction and its alias Phase are 0-1, and ApparentMagnitude is a magnitude (dimensionless); rise/set, culmination, and every event search return time.Time in the input time zone.

Duration fields inside event structs (TransitInfo.Duration, TransitInfo.InternalDuration, GalileanPhenomenonEvent.Duration, GalileanPhenomenonContact.Duration) are Go time.Duration values, not Julian days or day counts; the Start / Greatest / End / InternalStart / InternalEnd fields of TransitInfo keep the caller's time zone.

Geocentric, topocentric, and distance are three different conventions: ApparentLo / ApparentBo / ApparentRa / ApparentDec / ApparentRaDec are geocentric apparent places, Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle are topocentric quantities, and EarthDistance / SunDistance are geometric geocentric distances.

Zero values, out-of-range input and sentinel errors

When an event search finds nothing inside its window it returns zero values or a struct with Valid == false (TransitInfo.Valid, GalileanPhenomenonEvent.Valid, GalileanPhenomenonContactEvent.Valid) instead of an error; when TransitInfo.HasInternal is false, InternalStart / InternalEnd are zero values because a partial transit has no internal contacts.

planet.WherePlanet / planet.WherePlanetN return NaN instead of panicking when xt / zn is out of range; in the ...N truncation family, n < 0 uses the full built-in series and n >= 0 truncates it.

RiseTime / SetTime / DownTime (including their N forms) are the only family that returns an error: when the body has no geometric rise or set on that day they return the sentinel error below with a zero instant.

Each planet package has its own polar-day/polar-night errors.

NEVER_RISE in the name means "never rises that day" (polar night), NEVER_SET means "never sets that day" (polar day), and NEVER_DOWN is a compatibility alias for NEVER_SET.

Package Never rises Never sets Set alias
mercury ERR_MERCURY_NEVER_RISE ERR_MERCURY_NEVER_SET ERR_MERCURY_NEVER_DOWN
venus ERR_VENUS_NEVER_RISE ERR_VENUS_NEVER_SET ERR_VENUS_NEVER_DOWN
mars ERR_MARS_NEVER_RISE ERR_MARS_NEVER_SET ERR_MARS_NEVER_DOWN
jupiter ERR_JUPITER_NEVER_RISE ERR_JUPITER_NEVER_SET ERR_JUPITER_NEVER_DOWN
saturn ERR_SATURN_NEVER_RISE ERR_SATURN_NEVER_SET ERR_SATURN_NEVER_DOWN
uranus ERR_URANUS_NEVER_RISE ERR_URANUS_NEVER_SET ERR_URANUS_NEVER_DOWN
neptune ERR_NEPTUNE_NEVER_RISE ERR_NEPTUNE_NEVER_SET ERR_NEPTUNE_NEVER_DOWN
// Polar day/night: the rise/set functions return a sentinel error and a zero time.Time.
rise, err := mercury.RiseTime(date, lon, lat, height, true)
switch {
case errors.Is(err, mercury.ERR_MERCURY_NEVER_RISE):
	fmt.Println("polar night: never rises that day", rise.IsZero())
case errors.Is(err, mercury.ERR_MERCURY_NEVER_SET):
	fmt.Println("polar day: never sets that day", rise.IsZero())
}

Topocentric versus geocentric

Altitude / Azimuth / Zenith / HourAngle / ParallacticAngle use date's time zone for local-time computation, with east-positive longitude, north-positive latitude, and height as the ellipsoidal height in meters (not orthometric elevation).

They are a different convention from the geocentric apparent place of ApparentRa / ApparentDec; convert through Coordinate Tools before mixing them.

Accuracy and scope

The planet packages use the built-in truncated VSOP87 series, covering about 4000 years around J2000; the truncation magnitudes relative to full VSOP87 are in Sun and planets and the overall scope is in Scope And Accuracy.

The ...N forms relax that baseline further and suit batch scans or live front-end refreshes.

saturn.Ring and the physical ephemerides affect only the disk and band rendering: the Saturn ring takes no part in occultation contact geometry and is not drawn as a disk boundary. The planets have no dedicated chart entry point; occultation and Saturn-ring charts are in Lunar Occultations.