[中文](../orbit.md) | [Back to README](../../../README.en.md)
`orbit` propagates heliocentric two-body positions from orbital elements. It supports asteroids, comets, dwarf planets, and custom hypothetical orbits.
The seven major planets use the selected ephemeris through their own packages: default builds default to the semianalytic model, with Legacy or custom engines available explicitly.
Use `StateWithOptions` to propagate to a target epoch with optional planetary perturbations:
| `PropagationOptions` field | Meaning |
| --- | --- |
| `Perturbers` | Perturbing bodies; a non-empty value also requires an ephemeris source |
| `Engine` / `Context` | The perturbation ephemeris source. Both fields have the same type (`StateProvider`, i.e. `astro.Engine`) and `Engine` wins; pass an `*spk.File` or a custom engine |
- The zero-value options struct follows the existing two-body path exactly, matching `State`.
- The integrator adds solar gravity plus direct and indirect perturbation terms. `Perturbers` accepts Mercury through Neptune, Earth, the Moon and the Earth-Moon barycenter (3); an unknown body, or the barycenter combined with Earth or the Moon, returns `ErrUnsupportedPerturber`.
- Integration starts at `EpochJD`; a perihelion-form set starts at `TpJD` only when `EpochJD` is 0.
-`NewEphemerisEngine` adapts the propagated result for `astro.NewStack`: elements and options are fixed at construction, and the engine's cached checkpoints never change results, so any query order matches a direct `StateWithOptions` call bit for bit.
- Accuracy depends on the supplied elements and propagation span; use SPK for long-term high-precision work.
- perihelion form: `Q/E/I/Omega/W/TpJD`, useful for comets and high-eccentricity orbits
The reference frame of `orbit.Elements` is always the J2000 mean ecliptic and mean equinox. The examples below use one set of Ceres elements.
Topocentric and rise/set helpers take east-positive longitude, north-positive latitude, and height in meters.
The positional and topocentric quantities here also work together with the interfaces documented in the [star](star.md#stars) and [coordinate tools](coord.md#coordinate-tools) manuals.
## Contents
- [Calculating the position of Ceres from orbital elements](#calculating-the-position-of-ceres-from-orbital-elements)
- [API Reference](#api-reference)
- [Orbital Elements](#orbital-elements)
- [Positions](#positions)
- [Geometry](#geometry)
- [Rise, Set, and Culmination](#rise-set-and-culmination)
- [Photometry](#photometry)
- [Visual Binaries](#visual-binaries)
- [Usage examples](#usage-examples)
- [Picking a position layer](#picking-a-position-layer)
- [Will it rise tonight, and how high is it now?](#will-it-rise-tonight-and-how-high-is-it-now)
- [Distances, phase angle and H-G magnitude](#distances-phase-angle-and-h-g-magnitude)
- [Orbit types and position layers](#orbit-types-and-position-layers)
- [Parameter and result conventions](#parameter-and-result-conventions)
- [Position Layers](#position-layers)
- [Units and Frame Conventions](#units-and-frame-conventions)
- [Time Scale and Input Reading](#time-scale-and-input-reading)
- [Zero Values and Out-of-Range Input](#zero-values-and-out-of-range-input)
- [Accuracy and Applicability](#accuracy-and-applicability)
Elements use the J2000 mean ecliptic frame and a TT/TDB Julian-day epoch. This is heliocentric two-body propagation; perturbation errors need separate evaluation over long spans or near planetary encounters.
## API Reference
### Orbital Elements
| Name | Purpose | Units and convention |
| --- | --- | --- |
| `Elements` | Heliocentric two-body conic elements | `EpochJD`/`TpJD` are TT/TDB Julian days; `A`/`Q` in AU, `I`/`Omega`/`W`/`M0` in degrees, `E` dimensionless; `ADot`…`MDot` are per-day rates that apply to the classical elliptical form only |
| `MeanMotion` | Mean angular rate | degrees/day; `NaN` for parabolic and hyperbolic cases; a non-zero `MDot` is used directly |
| `MeanAnomaly` | Mean anomaly | degrees (`[0,360)`); `NaN` for parabolic and hyperbolic cases |
| `TrueAnomaly` | True anomaly | degrees (`[0,360)`); defined for elliptical, parabolic, and hyperbolic orbits, `NaN` for invalid elements |
Mean anomaly and true anomaly are solved from the same element set, and `MDot` can replace the default mean motion:
Orbital elements have epochs. The farther the target date is from the epoch, the more static-element error can grow.
If the source provides long-term linear rates such as `ADot/EDot/IDot/OmegaDot/WDot/MDot`, they can be filled into `Elements` to reduce medium- and long-term drift.
### Positions
| Name | Purpose | Units and convention |
| --- | --- | --- |
| `EclipticPosition` | Ecliptic spherical return value | `Lon`/`Lat` in degrees, `Distance` in AU |
| `EquatorialPosition` | Equatorial spherical return value | `RA`/`Dec` in degrees, `Distance` in AU |
| `HeliocentricEclipticJ2000` | Heliocentric J2000 mean ecliptic | geometric, no light-time |
| `HeliocentricEcliptic` | Heliocentric ecliptic of date | geometric, referred to the mean equinox of date |
| `GeocentricEclipticJ2000` | Geocentric J2000 mean ecliptic | geometric, Earth and target at the same instant |
| `GeocentricEcliptic` | Geocentric ecliptic of date | geometric, referred to the mean equinox of date |
The `...J2000` variants and the of-date variants are two different frames: the former stay in the J2000 mean ecliptic/equinox, which suits catalogue comparison and long-term archiving; the latter use the mean ecliptic/equinox of date, which suits "today's sky" expressions. `Distance` is the instantaneous distance for the geometric interfaces and the light-time-converged distance for `Astrometric...`; the two differ by roughly the displacement during the light-time.
Near 180° elongation the phase angle approaches 0° and `IlluminatedFraction` approaches 1; all three quantities come from the same geocentric geometry.
`ParallacticAngle` does not solve for declination separately: it reuses the same topocentric solve as `HourAngle`, so the two are exactly self-consistent.
These observing helpers work on topocentric apparent coordinates and suit rise/set and pointing support for asteroids, comets, or custom two-body targets.
With `aero` false the criterion is the geometric horizon; with `aero` true the target altitude is taken as `-0.5667°` plus the horizon dip derived from ellipsoidal height and latitude.
The second return value of `RiseTime`/`SetTime` is a real error: only a day without a rise/set is mapped to `ERR_ORBIT_NEVER_RISE` / `ERR_ORBIT_NEVER_SET`, and any other failure passes through unchanged.
### Photometry
| Name | Purpose | Units and convention |
| --- | --- | --- |
| `AsteroidMagnitudeHG` | Asteroid apparent magnitude, H-G model | `absoluteMagnitude` is H and `slopeParameter` is G; both dimensionless |
The H-G model uses only heliocentric distance, geocentric distance, and phase angle; it does not introduce the target radius, albedo, or rotation, and this package never fills in a default value for `G` — that comes from the external catalogue.
### Visual Binaries
| Name | Purpose | Units and convention |
| --- | --- | --- |
| `VisualBinaryElements` | Visual-binary orbital elements | `PeriodYears` in mean solar years, `PeriastronYear` as a decimal year, `SemiMajorAxis` in arcseconds, `Inclination`/`AscendingNode`/`PeriastronArgument` in degrees, `Eccentricity` dimensionless |
| `VisualBinaryPosition` | Computed visual-binary position | `MeanAnomaly`/`EccentricAnomaly`/`TrueAnomaly`/`PositionAngle` in degrees, `Radius`/`Separation` in arcseconds |
| `VisualBinary` | Visual-binary position at an instant | converts the instant to a UTC decimal year, then applies the classical apparent-orbit formula |
| `VisualBinaryByYear` | Visual-binary position by decimal year | takes the decimal year directly, skipping the instant conversion |
The four layers mean different things: `Heliocentric*` is relative to the Sun (the first number is the heliocentric distance), `Geocentric*J2000` is the J2000 geocentric position, `Astrometric*J2000` adds light-time and suits catalog comparison, and `Apparent*` is the apparent position of the day used for observing and charts.
-`aero = true` solves against the horizon corrected for refraction and apparent radius; `height` is ellipsoidal height in metres and longitude is east positive.
- Circumpolar or polar targets have no rise or set: `RiseTime`/`SetTime` return the `orbit.ERR_ORBIT_NEVER_RISE` / `ERR_ORBIT_NEVER_SET` sentinels, so branch with `errors.Is`.
-`PhaseAngle` is the Sun-target-Earth angle in degrees and `IlluminatedFraction` is the illuminated fraction; `Phase` is only an alias of `IlluminatedFraction`, so do not read it as a phase angle.
- H-G magnitudes take the absolute magnitude `H` and the slope parameter `G` (Ceres uses `3.34` and `0.12` in the example).
- Parabolic and hyperbolic orbits only work in perihelion form (`Q` plus `TpJD`): mean motion and mean anomaly are undefined and return `NaN`, while the true anomaly solves for all three orbit types.
- When cross-checking against an external ephemeris, align the position layer and frame first (`Elements` is always J2000 mean ecliptic/equinox); `ADot…WDot` only apply to the classical ellipse form, and a non-zero `MDot` replaces the default mean motion.
## Parameter and result conventions
### Position Layers
| Layer | Representative interfaces | Corrections included |
| Topocentric apparent | `ApparentTopocentricEquatorial` and all rise/set helpers | light-time + nutation + topocentric parallax |
### Units and Frame Conventions
- Angles are always in degrees, distances in AU, ellipsoidal heights for topocentric and rise/set helpers in meters, and time as `time.Time`.
- The frame of `Elements` is the J2000 mean ecliptic and mean equinox. Interfaces ending in `...J2000` keep that frame; the other `HeliocentricEcliptic`/`GeocentricEcliptic`/`GeocentricEquatorial` variants are of-date quantities, so do not mix the two frames.
- Positions come in three layers: `Heliocentric*`/`Geocentric*` are geometric, `AstrometricGeocentricEquatorialJ2000` adds light-time to the geometric position (solved iteratively in distance, at most 8 passes, converged at `1e-12` days), and `Apparent*` adds nutation on top of light-time.
The planetary convention of this repository is exactly "light-time + nutation, without a full external aberration model", so `Apparent*` is at the same level as the planet packages and must not be treated as a full apparent place.
-`MeanMotion`/`MeanAnomaly`/`TrueAnomaly` return degrees; mean motion and mean anomaly are undefined for parabolic and hyperbolic orbits.
### Time Scale and Input Reading
-`EpochJD` and `TpJD` are TT/TDB Julian days. The coordinate, geometry, and photometry interfaces treat `date` as an absolute instant (`date.UTC()`, then `UTC2TT` to TT/TDB).
`UTC2TT` treats civil values as UT1 before 1972-01-01 and uses the built-in leap-second table inside the exact window; that table can be overridden with `astro.SetTTMinusUTC`.
- Instantaneous topocentric quantities combine the local clock fields with `date.Zone()` to recover the absolute instant. UTC and local-zone representations of the same instant give the same position. Rise/set searches also use the local date, so choose the zone for the observing calendar.
-`CulminationTime`, `RiseTime`, and `SetTime` keep the zone of the input `date` and return civil instants. Internally they step back 12 hours when `date.Hour() > 12`, so that the search anchor stays within the selected date.
- Every public `time.Time` output is a civil value (UTC label); UT1 and TT only appear in internal conversions. For explicit conversion use the root package's `astro.UT1FromUTC` / `astro.TTFromUTC`.
### Zero Values and Out-of-Range Input
- The zero value of `Elements` is not a valid orbit. The classical elliptical form requires a finite positive `A`, `E` within `[0,1)`, and finite `EpochJD` and `M0`.
Parabolic and hyperbolic orbits with `E >= 1` can only use the perihelion form, which requires a finite positive `Q`, a finite `TpJD`, `E >= 0`, and finite `I`/`Omega`/`W`.
- When `Q > 0` and `TpJD` is finite the perihelion form wins: `A`, `M0`, and `EpochJD` are ignored, `ADot`/`EDot`/`IDot`/`OmegaDot`/`WDot` have no effect, and only a non-zero `MDot` is used as the mean angular rate. Perturbed propagation is the exception: when a perihelion-form set also gives `EpochJD`, that epoch is the osculating epoch where integration starts.
- With invalid elements `MeanMotion` and `MeanAnomaly` return `NaN` and the positional interfaces return three `NaN`s.
`AsteroidMagnitudeHG` returns `NaN` when its inputs are non-finite or when heliocentric distance, geocentric distance, or phase angle is non-positive, and `+Inf` when the H-G phase blend is zero.
-`RiseTime` and `SetTime` return the sentinel errors `ERR_ORBIT_NEVER_RISE` / `ERR_ORBIT_NEVER_SET` when no rise/set exists on the supplied local day, and pass any other failure through; on success the second return value is `nil`.
-`VisualBinary` and `VisualBinaryByYear` fill every numeric field of `VisualBinaryPosition` with `NaN` when `PeriodYears <= 0`, `SemiMajorAxis <= 0`, `Eccentricity` outside `[0,1)`, or any element is non-finite.
### Accuracy and Applicability
-`orbit` is two-body conic propagation: it contains only the supplied elements, with no planetary perturbations, non-gravitational terms, or relativistic corrections. The farther the date from the epoch, the larger the static-element error; `ADot`…`WDot` only mitigate linear drift and cannot replace a re-fit or a numerical integration.
- The difference between the `Apparent*` and topocentric quantities is purely geometric plus nutation, and neither includes atmospheric refraction; only `aero=true` in the rise/set helpers folds refraction into the criterion (target altitude `-0.5667°` plus the horizon dip derived from ellipsoidal height and latitude).
-`RiseTime`/`SetTime` iterate the rise/set geometry in the nominal zone `round(observerLon/15)`, so the site should sit near the center of its zone; only one root is solved, and boundary cases such as polar or circumpolar targets are reported through the sentinel errors.
- The visual-binary solver uses the classical apparent-orbit formula from chapter 55 of *Astronomical Algorithms* and does not apply when `Eccentricity >= 1`; it is a geometric projection only, with no mass, photometry, or perturbation information.
### Common Pitfalls
- Treating a `HeliocentricEcliptic` result as geocentric: heliocentric quantities are centered on the Sun, while geocentric ones have already subtracted the heliocentric Earth position.
- Treating `Apparent*` as a refraction-corrected apparent place: refraction appears only in the rise/set and observing helpers, not in the coordinate interfaces.
- Probing with a zero-value `Elements`: mean motion and the positional interfaces quietly return `NaN` instead of reporting an error.
- Expecting `ADot`…`WDot` to take effect in the perihelion form: those rates only propagate in the classical elliptical form.
- Frame conversion and topocentric quantities: [coordinate tools](coord.md#coordinate-tools)
- The analogous rise/set interfaces: [Sun and Moon](sun-moon.md#sunrisesunset-and-moonrisemoonset)
> To feed the ecliptic/equatorial coordinates here into a star catalogue or topocentric quantities, see [star](star.md#stars) and [coordinate tools](coord.md#coordinate-tools).