# Planets [中文](../planets.md) | [Back to README](../../../README.en.md) 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](coord.md). - 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](occultation.md#lunar-occultation-charts). ## Contents - [The position and rise time of Mars](#the-position-and-rise-time-of-mars) - [API Reference](#api-reference) - [Cross-planet comparison](#cross-planet-comparison) - [Common capabilities and units](#common-capabilities-and-units) - [The `...N` truncation family](#the-n-truncation-family) - [Usage examples](#usage-examples) - [Which planet can I see tonight?](#which-planet-can-i-see-tonight) - [Oppositions, conjunctions, elongations, stations and retrogrades](#oppositions-conjunctions-elongations-stations-and-retrogrades) - [Mercury and Venus transits](#mercury-and-venus-transits) - [Phase, diameter, magnitude and nodes](#phase-diameter-magnitude-and-nodes) - [Physical ephemerides and the Galilean satellites](#physical-ephemerides-and-the-galilean-satellites) - [Comparison conventions](#comparison-conventions) - [Basic examples](#basic-examples) - [Inner planets](#inner-planets) - [Outer planets](#outer-planets) - [Topic examples](#topic-examples) - [Position and coordinates](#position-and-coordinates) - [Rise, set and culmination](#rise-set-and-culmination) - [Conjunctions, oppositions, stations and quadratures](#conjunctions-oppositions-stations-and-quadratures) - [Greatest elongation and geocentric transits](#greatest-elongation-and-geocentric-transits) - [Nodes, phase, magnitude, diameter and parallactic angle](#nodes-phase-magnitude-diameter-and-parallactic-angle) - [Division of labour with other manuals](#division-of-labour-with-other-manuals) - [Physical ephemerides](#physical-ephemerides) - [Galilean satellites of Jupiter](#galilean-satellites-of-jupiter) - [Shared types and constants from the `planet` package](#shared-types-and-constants-from-the-planet-package) - [Parameter and result conventions](#parameter-and-result-conventions) - [Time scale and civil time](#time-scale-and-civil-time) - [Units and conventions](#units-and-conventions) - [Zero values, out-of-range input and sentinel errors](#zero-values-out-of-range-input-and-sentinel-errors) - [Topocentric versus geocentric](#topocentric-versus-geocentric) - [Accuracy and scope](#accuracy-and-scope) ## The position and rise time of Mars ```go 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](#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. ```go 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? ```go 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 ``` ```text 2020-01-01 10:02:34.350145161 +0800 CST 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](#rise-set-and-culmination). ### Oppositions, conjunctions, elongations, stations and retrogrades ```go 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 ``` ```text 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](#conjunctions-oppositions-stations-and-quadratures); Mercury's and Venus's `NextGreatestElongationEast` / `...West` are in [Greatest elongation and geocentric transits](#greatest-elongation-and-geocentric-transits). ### Mercury and Venus transits ```go 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 ``` ```text 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](#greatest-elongation-and-geocentric-transits). ### Phase, diameter, magnitude and nodes ```go 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) ``` ```text 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](#nodes-phase-magnitude-diameter-and-parallactic-angle). ### Physical ephemerides and the Galilean satellites ```go 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 ``` ```text -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](#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](#galilean-satellites-of-jupiter). ### Comparison conventions ```go 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)) ``` ```text 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](#external-baselines), and the overall boundaries under [Parameter and result conventions](#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 ```go 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: ```text 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 // Venus rise time in Xi'an; no error 2020-01-01 20:25:37.36411482 +0800 CST // 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: ```go 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: ```text 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. ```go 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: ```text 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 ```go 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: ```text 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 // Mars rise time in Xi'an; no error 2020-01-01 14:55:32.963508367 +0800 CST // 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](coord.md). ```go // 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](#parameter-and-result-conventions) instead of an instant. `CulminationTime` gives upper culmination; `Altitude` / `Azimuth` / `Zenith` / `HourAngle` / `ParallacticAngle` form the topocentric family, with geometry under [Coordinate Tools](coord.md). ```go // 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)) ``` ```go // 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)) ``` ```go // 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. ```go // 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)) ``` ```go // Outer planets: conjunction, opposition, and eastern/western quadrature. fmt.Println(jupiter.NextConjunction(date), mars.NextOpposition(date)) fmt.Println(mars.NextEasternQuadrature(date), neptune.LastWesternQuadrature(date)) ``` ```go // 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. ```go // 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)) ``` ```go // 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) } ``` ```go // 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](coord.md). ```go 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 ``` ```go fmt.Println(mars.Diameter(date), mars.Semidiameter(date)) // apparent diameter and semidiameter, arcseconds fmt.Println(mars.ParallacticAngle(date, lon, lat)) // parallactic angle, degrees ``` ```go // 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](coord.md). - Sun and Moon positions, phases, rise/set, and syzygies are in the [Sun and Moon manual](sun-moon.md). - Planetary occultations, occultation bands, and lunar-disk charts are in [Lunar Occultations](occultation.md#lunar-occultation-charts). - Asteroids, comets, and other bodies computed from orbital elements are in [Small-body orbits](orbit.md). - Time-scale declarations, UT1 conventions, and GeoJSON output are in [Event Maps and GeoJSON](map-geojson.md); the scale convention itself is in [Time Scale Declaration](map-geojson.md#time-scale-declaration). - The authoritative capability list, dependency matrix, and accuracy summary are in the root [README](../../../README.en.md). ### 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. ```go 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: ```text 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: ```go 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: ```go 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](occultation.md#lunar-occultation-charts). ```go 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) ``` ```go // 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. ```go // 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` ```go // 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 ```go 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: ```text 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 | ```go // 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 ``` ```go // 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](#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](map-geojson.md#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` | ```go // 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](coord.md) 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](accuracy.md#sun-and-planets) and the overall scope is in [Scope And Accuracy](accuracy.md). 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](occultation.md#lunar-occultation-charts).