# Orbits¶

The `org.orekit.orbits`

package provides classes to represent orbits.

This package is the basis for all of the other space mechanics tools.

It provides an abstract class, Orbit, extended in four different ways

corresponding to the different possible representations of orbital parameters.

Since version 3.0, keplerian, circular, equinoctial and cartesian representations

are supported.

## Design History¶

Early designs for the orbit package were much more complex than the current design.

Looking back at these designs, they tried to do far too much in a single class and

resulted in huge systems which were difficult to understand, difficult to

use, difficult to maintain and difficult to reuse. They mixed several different notions:

- representation (Keplerian, Cartesian ...)
- kinematics (parameters) and dynamics (propagation)
- physical models (complete or simplified force models, often implicitly assumed with no real reference)
- filtering (osculating and centered or mean parameters, related to some models)

They also often neglected all frames issues.

The current design has been reached by progressively removing spurious layers and

setting them apart in dedicated packages. All these notions are now still handled,

but all in different classes that are cleanly linked to each other. Without knowing

it, we have followed Antoine de Saint Exupéry's saying:

It seems that perfection is reached not when there is nothing left to add, but

when there is nothing left to take away.

The current design is not perfect, of course, but it is easy to understand, easy to use,

easy to maintain and reusable.

## Current state versus evolving state¶

From the early design, the various orbit classes retained only the kinematical

notions at a single time. They basically represent the current state, and

serve as data holders. Most of the methods provided by these orbits classes are

getters. Some of them (the non-ambiguous ones that must be always available) are

defined in the top Orbit abstract class (`Orbit.getA()`

, `Orbit.getDate()`

,

`Orbit.getPVCoordinates()`

). The more specific ones depending on the type of

representation are only defined in the corresponding class

(CircularOrbit.getCircularEx() for example).

It is important to note that some parameters are available even if they seem

out of place with regard to the representation. For example the semi-major axis is

available even in Cartesian representation and the position/velocity even in

non-Cartesian representation. This choice is a pragmatic one. These parameters

are really used in many places in algorithms, for computation related to

period (setting a convergence threshold or a search interval) or geometry

(computing swath or lighting). A side-effect of this choice is that all orbits

do include a value for µ, the acceleration coefficient of the central body.

This value is only used for the representation of the parameters and for conversion

purposes, it is *not* always the same as the value used for propagation (but

of course, using different values should be done with care).

Orbits also include a reference to a date and a defining frame. Only pseudo-inertial

frames can be used to define orbits, as Newtonian mechanics should apply within the

context of the frame. Including frames allows transparent conversions to any other

frames at given date, without having to externally preserve a mapping between orbits

and their frame: it is already done. As an example, getting the position and velocity

of a satellite given by a circular orbit in a ground station frame is simply a matter

of calling orbit.getPVCoordinates(stationFrame), regardless of the pseudo-inertial frame

in which the orbit is defined (EME2000, GCRF ...).

Since orbits are used everywhere in space dynamics applications and since we

have chosen to restrict them to a simple state holder, all orbit classes are

guaranteed to be immutable. They are small objects and they are shared by

many parts. This change was done in 2006 and tremendously simplified the

library and the users applications that were previously forced to copy orbits

as an application of defensive programming, and that were plagued by

difficult-to-find bugs when they forgot to copy.

For orbit evolution computation, this package is not sufficient. There is a

need to include notions of dynamics, forces models, propagation algorithms ...

The entry point for this is the Propagator interface.

## Existing orbit representations¶

Available orbit representations are :

Classic elliptical Keplerian orbit, which parameters are :

- a : semi-major axis (m)
- e : eccentricity (any value of e is supported, i.e. both elliptical and hyperbolic orbits can be used)
- i : inclination (rad)
- ω : perigee argument (rad)
- Ω : right ascension of the ascending node (rad)
- v, M or E (rad) : respectively true anomaly, mean anomaly or eccentric anomaly

Circular orbit, used to represent almost circular orbit, i.e orbit with low eccentricity. Its parameters are:

- a : semi-major axis (m)
- ex : X component of eccentricity vector : e × cos(ω)
- ey : Y component of eccentricity vector : e × sin(ω)
- i : inclination (rad)
- Ω : right ascension of the ascending node (rad)
- αv, αM or αE (rad) : respectively (ω + v), (ω + M) or (ω + E)

Equinoctial orbit, used to represent equinoctial orbits (almost circular orbit with almost null inclination). Its parameters are:

- a : semi-major axis (m)
- ex = e × cos(ω + Ω)
- ey = e × sin(ω + Ω)
- hx = tan(i/2) cos(Ω)
- hy = tan(i/2) sin(Ω)
- λv, λM or λE (rad) : respectively true longitude argument (ω + Ω + v) mean longitude argument (ω + Ω + M) or eccentric longitude argument (ω + Ω + E)

Cartesian orbit, associated to its frame definition, the parameters for which are:

- (X, Y, Z) (m) : position vector of the point in given frame
- (Vx, Vy, Vz) (m/s) : velocity vector of the point in given frame

Note that Two-Lines Elements are *not* considered orbits representation here. This is

because TLE are in fact a merge between orbital state and a propagation model. The state

is *only* meaningful with respect to the associated SGP4/SDP4 propagation model, and cannot

be used in any other model.

## Representations Conversions¶

All representations can be converted into all other ones. No error is triggered

if some conversion is ambiguous (like converting a perfectly circular orbit from

Cartesian representation to Keplerian representation, with an ambiguity on the

perigee argument). This design choice is the result of *many*

different attempts and pragmatic considerations. The rationale is that from a

physical point of view, there is no singularity. The singularity is only introduced

by a choice of *representations*. Even considering this, it appears that

rather than having a parameter with *no* realistic value, there is an

*infinite* possible number of values that all represent the same physical

orbit. Orekit simply does an arbitrary choice, often choosing simply the value 0.

In our example case, we would then get a converted orbit with a 0 perigee argument.

This choice is valid, just as any other choice (π/2, π, whatever ...) would

have been valid, in the sense that it *does* represent correctly the orbit

and when converted back to the original non-ambiguous representation it does give

the right result.

We therefore consider it the responsibility of the user to be aware of the correct

definition of the different representations and of the singularities relative to each

one of them. If the user really needs to do some conversion (for example to provide

an orbit as Two-Line Elements later on, remembering that TLEs do use keplerian-like

parameters), then he can do so.

The way conversion is handled in Orekit is very simple and allows easy and transparent

processing, while avoiding blindly creating a new object if the the original orbit was

already of the appropriate type. So the appropriate way to convert an orbit is

the following one, using CircularOrbit as an example:

OrbitType type = OrbitType.CIRCULAR; CircularOrbit circular = (CircularOrbit) type.convertType(orbit);

In this example, the cast to CircularOrbit is garanteed to succeed because

the type enumerate is CIRCULAR. No object will be created if orbit was already

the proper type. As an example, the Eckstein-Hechler propagator is defined in

terms of circular orbit only. So there is an implicit conversion done at propagator

initialization or reset state time, using internally a code similar to the example

above.