Skip to content

Commit

Permalink
feat: frozen orbit conditions (#473)
Browse files Browse the repository at this point in the history
* feat: add method to get coe for frozen orbit

* feat: add frozen Orbit constructor

* test: add coe gtest

* chore: improve error handling

* refactor: make re, j2, and j3 args

* test: add tests for orbit

* chore: bump ostk-physics dependency version

* test: add python tests

* perf: loosen criticality requirement

* style: use camelCase

* style: add units and long for to var names

* style: make lambda captures explicit

* perf: use ostk Arrays

* test: add more tests

* doc: add more documentation

* test: improve tests

* doc: add docs for Orbit constructor

* feat: add overloaded ctor for COE

* test: add test for ctor with Celestial

* test: add python test

* perf: use reference

* chore: fix typos

* doc: add doc to orbit.hpp

* test: add negative tests for celestial ctor
  • Loading branch information
alex-liang3 authored Dec 17, 2024
1 parent 827f75f commit 6956a4e
Show file tree
Hide file tree
Showing 11 changed files with 782 additions and 1 deletion.
2 changes: 1 addition & 1 deletion bindings/python/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
open-space-toolkit-core~=4.1
open-space-toolkit-io~=4.0
open-space-toolkit-mathematics~=4.3
open-space-toolkit-physics~=11.1
open-space-toolkit-physics~=11.2
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,54 @@ inline void OpenSpaceToolkitAstrodynamicsPy_Trajectory_Orbit(pybind11::module& a
)doc"
)

.def_static(
"frozen",
&Orbit::Frozen,
arg("epoch"),
arg("altitude"),
arg("celestial_object"),
arg_v("eccentricity", Real::Undefined(), "Real.undefined()"),
arg_v("inclination", Angle::Undefined(), "Angle.undefined()"),
arg_v("raan", Angle::Degrees(0.0), "Angle.degrees(0.0)"),
arg_v("aop", Angle::Undefined(), "Angle.undefined()"),
arg_v("true_anomaly", Angle::Degrees(0.0), "Angle.degrees(0.0)"),
R"doc(
Create a frozen `Orbit` object.
The critical angles for inclination are 63.4349 degrees and 116.5651 degrees.
The critical angles for AoP are 90.0 degrees and 270.0 degrees.
At a minimum, an epoch, altitude, and celestial body with a defined J2 and J3 must be provided.
In this case, the inclination and AoP are set to critical angles, and the eccentricity is derived
from inclination. RAAN and true anomaly default to zero degrees.
Additionally, the following combinations of inputs are supported:
- AoP (inclination set to critical value, eccentricity derived)
- AoP and eccentricity (inclination derived)
- AoP and inclination, but at least one of them must be a critical value (eccentricity derived)
- Inclination (AoP set to critical value, eccentricity derived)
- Eccentricity (AoP set to critical value, inclination derived)
Note that inclination and eccentricity cannot both be provided.
RAAN and True Anomaly may be provided alongside any of these arguments, and will be passed through
to the resulting Orbit as they do not impact the frozen orbit condition.
Args:
epoch (Instant): The epoch.
altitude (Length): The altitude.
celestial_object (Celestial): The celestial object.
eccentricity (float): The eccentricity.
inclination (Angle): The inclination.
raan (Angle): The right ascension of the ascending node.
aop (Angle): The argument of periapsis.
true_anomaly (Angle): The true anomaly.
Returns:
Orbit: The frozen `Orbit` object.
)doc"
)

.def_static(
"compute_passes",
&Orbit::ComputePasses,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ inline void OpenSpaceToolkitAstrodynamicsPy_Trajectory_Orbit_Model_Kepler_COE(py

using ostk::core::type::Real;

using ostk::physics::environment::object::Celestial;
using ostk::physics::unit::Angle;
using ostk::physics::unit::Length;

Expand Down Expand Up @@ -387,6 +388,118 @@ inline void OpenSpaceToolkitAstrodynamicsPy_Trajectory_Orbit_Model_Kepler_COE(py
arg("anomaly_type")
)

.def_static(
"frozen_orbit",
overload_cast<
const Length&,
const Shared<const Celestial>&,
const Real&,
const Angle&,
const Angle&,
const Angle&,
const Angle&>(&COE::FrozenOrbit),
R"doc(
Build a `COE` model of a frozen orbit.
The critical angles for inclination are 63.4349 degrees and 116.5651 degrees.
The critical angles for AoP are 90.0 degrees and 270.0 degrees.
At a minimum, a semi-major axis and shared pointer to a central celestial body with a defined J2 and J3
must be provided. In this case, the inclination and AoP are set to critical angles, and the eccentricity
is derived from inclination. RAAN and true anomaly default to zero degrees.
Additionally, the following combinations of inputs are supported:
- AoP (inclination set to critical value, eccentricity derived)
- AoP and eccentricity (inclination derived)
- AoP and inclination, but at least one of them must be a critical value (eccentricity derived)
- Inclination (AoP set to critical value, eccentricity derived)
- Eccentricity (AoP set to critical value, inclination derived)
Note that inclination and eccentricity cannot both be provided.
RAAN and True Anomaly may be provided alongside any of these arguments, and will be passed through
to the resulting COE as they do not impact the frozen orbit condition.
Args:
semi_major_axis (Length): The semi-major axis.
celestial_object (Celestial): The celestial object.
eccentricity (float): The eccentricity.
inclination (Angle): The inclination.
raan (Angle): The right ascension of the ascending node.
aop (Angle): The argument of periapsis.
true_anomaly (Angle): The true anomaly.
Returns:
COE: The `COE` model.
)doc",
arg("semi_major_axis"),
arg("celestial_object"),
arg_v("eccentricity", Real::Undefined(), "Real.undefined()"),
arg_v("inclination", Angle::Undefined(), "Angle.undefined()"),
arg_v("raan", Angle::Degrees(0.0), "Angle.degrees(0.0)"),
arg_v("aop", Angle::Undefined(), "Angle.undefined()"),
arg_v("true_anomaly", Angle::Degrees(0.0), "Angle.degrees(0.0)")
)

.def_static(
"frozen_orbit",
overload_cast<
const Length&,
const Length&,
const Real&,
const Real&,
const Real&,
const Angle&,
const Angle&,
const Angle&,
const Angle&>(&COE::FrozenOrbit),
R"doc(
Build a `COE` model of a frozen orbit.
The critical angles for inclination are 63.4349 degrees and 116.5651 degrees.
The critical angles for AoP are 90.0 degrees and 270.0 degrees.
At a minimum, a semi-major axis, equatorial radius, J2, and J3 must be provided. In this case,
the inclination and AoP are set to critical angles, and the eccentricity is derived from inclination.
RAAN and true anomaly default to zero degrees.
Additionally, the following combinations of inputs are supported:
- AoP (inclination set to critical value, eccentricity derived)
- AoP and eccentricity (inclination derived)
- AoP and inclination, but at least one of them must be a critical value (eccentricity derived)
- Inclination (AoP set to critical value, eccentricity derived)
- Eccentricity (AoP set to critical value, inclination derived)
Note that inclination and eccentricity cannot both be provided.
RAAN and True Anomaly may be provided alongside any of these arguments, and will be passed through
to the resulting COE as they do not impact the frozen orbit condition.
Args:
semi_major_axis (Length): The semi-major axis.
equatorial_radius (Length): The equatorial radius.
j2 (float): The second zonal harmonic coefficient.
j3 (float): The third zonal harmonic coefficient.
eccentricity (float): The eccentricity.
inclination (Angle): The inclination.
raan (Angle): The right ascension of the ascending node.
aop (Angle): The argument of periapsis.
true_anomaly (Angle): The true anomaly.
Returns:
COE: The `COE` model.
)doc",
arg("semi_major_axis"),
arg("equatorial_radius"),
arg("j2"),
arg("j3"),
arg_v("eccentricity", Real::Undefined(), "Real.undefined()"),
arg_v("inclination", Angle::Undefined(), "Angle.undefined()"),
arg_v("raan", Angle::Degrees(0.0), "Angle.degrees(0.0)"),
arg_v("aop", Angle::Undefined(), "Angle.undefined()"),
arg_v("true_anomaly", Angle::Degrees(0.0), "Angle.degrees(0.0)")
)

.def_static(
"eccentric_anomaly_from_true_anomaly",
&COE::EccentricAnomalyFromTrueAnomaly,
Expand Down
21 changes: 21 additions & 0 deletions bindings/python/test/trajectory/orbit/models/kepler/test_coe.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ostk.physics.time import Instant
from ostk.physics.environment.gravitational import Earth
from ostk.physics.environment.object.celestial import Sun
from ostk.physics import Environment

from ostk.astrodynamics.trajectory.orbit.model.kepler import COE

Expand Down Expand Up @@ -74,6 +75,26 @@ def test_constructors(self):
assert isinstance(coe, COE)
assert coe.is_defined() is False

coe: COE = COE.frozen_orbit(
a,
Earth.EGM2008.equatorial_radius,
Earth.EGM2008.J2,
Earth.EGM2008.J3,
inclination=i,
)

assert coe is not None
assert isinstance(coe, COE)
assert coe.is_defined()

coe: COE = COE.frozen_orbit(
a, Environment.default().access_celestial_object_with_name("Earth")
)

assert coe is not None
assert isinstance(coe, COE)
assert coe.is_defined()

def test_comparators(
self,
coe: COE,
Expand Down
9 changes: 9 additions & 0 deletions bindings/python/test/trajectory/test_orbit.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ def test_sun_synchronous(self, earth):
argument_of_latitude=Angle.degrees(50.0),
).is_defined()

def test_frozen(self, earth, epoch):
altitude = Length.kilometers(500.0)

orbit: Orbit = Orbit.frozen(epoch, altitude, earth)

assert orbit is not None
assert isinstance(orbit, Orbit)
assert orbit.is_defined()

def test_compute_passes(self, states: list[State]):
passes: list[tuple[int, Pass]] = Orbit.compute_passes(states, 1)
assert passes is not None
Expand Down
43 changes: 43 additions & 0 deletions include/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,49 @@ class Orbit : public Trajectory
const Angle& anArgumentOfLatitude = Angle::Zero()
);

/// @brief Constructs a Frozen orbit
///
/// Model: Kepler (J2 Perturbation)
///
/// The critical angles for inclination are 63.4349 degrees and 116.5651 degrees.
/// The critical angles for AoP are 90.0 degrees and 270.0 degrees.
///
/// At a minimum, an epoch, altitude, and celestial body with a defined J2 and J3 must be provided.
/// In this case, the inclination and AoP are set to critical angles, and the eccentricity is derived
/// from inclination. RAAN and true anomaly default to zero degrees.
///
/// Additionally, the following combinations of inputs are supported:
/// - AoP (inclination set to critical value, eccentricity derived)
/// - AoP and eccentricity (inclination derived)
/// - AoP and inclination, but at least one of them must be a critical value (eccentricity derived)
/// - Inclination (AoP set to critical value, eccentricity derived)
/// - Eccentricity (AoP set to critical value, inclination derived)
///
/// Note that inclination and eccentricity cannot both be provided.
///
/// RAAN and True Anomaly may be provided alongside any of these arguments, and will be passed through
/// to the resulting Orbit as they do not impact the frozen orbit condition.
///
/// @param anEpoch An orbit epoch
/// @param anAltitude An orbit altitude (wrt. equatorial radius)
/// @param aCelestialObjectSPtr A shared pointer to a central celestial body
/// @param anEccentricity An eccentricity
/// @param anInclination An inclination
/// @param aRaan A raan
/// @param anAop An aop
/// @param aTrueAnomaly A true anomaly
/// @return Frozen orbit
static Orbit Frozen(
const Instant& anEpoch,
const Length& anAltitude,
const Shared<const Celestial>& aCelestialObjectSPtr,
const Real& anEccentricity = Real::Undefined(),
const Angle& anInclination = Angle::Undefined(),
const Angle& aRaan = Angle::Degrees(0.0),
const Angle& anAop = Angle::Undefined(),
const Angle& aTrueAnomaly = Angle::Degrees(0.0)
);

/// @brief Get the string representation of a frame type
///
/// @param aFrameType Type of the frame to get the string representation of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ using ostk::mathematics::object::Vector6d;
using ostk::physics::coordinate::Frame;
using ostk::physics::coordinate::Position;
using ostk::physics::coordinate::Velocity;
using ostk::physics::environment::object::Celestial;
using ostk::physics::environment::object::celestial::Sun;
using ostk::physics::time::Duration;
using ostk::physics::time::Instant;
Expand Down Expand Up @@ -252,6 +253,88 @@ class COE
/// @return COE
static COE FromSIVector(const Vector6d& aCOEVector, const AnomalyType& anAnomalyType);

/// @brief Construct a frozen orbit from an incomplete set of COEs
///
/// The critical angles for inclination are 63.4349 degrees and 116.5651 degrees.
/// The critical angles for AoP are 90.0 degrees and 270.0 degrees.
///
/// At a minimum, a semi-major axis and shared pointer to a central celestial body with a defined J2 and J3
/// must be provided. In this case, the inclination and AoP are set to critical angles, and the eccentricity
/// is derived from inclination. RAAN and true anomaly default to zero degrees.
///
/// Additionally, the following combinations of inputs are supported:
/// - AoP (inclination set to critical value, eccentricity derived)
/// - AoP and eccentricity (inclination derived)
/// - AoP and inclination, but at least one of them must be a critical value (eccentricity derived)
/// - Inclination (AoP set to critical value, eccentricity derived)
/// - Eccentricity (AoP set to critical value, inclination derived)
///
/// Note that inclination and eccentricity cannot both be provided.
///
/// RAAN and True Anomaly may be provided alongside any of these arguments, and will be passed through
/// to the resulting COE as they do not impact the frozen orbit condition.
///
/// @param aSemiMajorAxis A semi-major axis
/// @param aCelestialObjectSPtr A shared pointer to a central celestial body
/// @param anEccentricity An eccentricity
/// @param anInclination An inclination
/// @param aRaan A raan
/// @param anAop An aop
/// @param aTrueAnomaly A true anomaly
/// @return COE
static COE FrozenOrbit(
const Length& aSemiMajorAxis,
const Shared<const Celestial>& aCelestialObjectSPtr,
const Real& anEccentricity = Real::Undefined(),
const Angle& anInclination = Angle::Undefined(),
const Angle& aRaan = Angle::Degrees(0.0),
const Angle& anAop = Angle::Undefined(),
const Angle& aTrueAnomaly = Angle::Degrees(0.0)
);

/// @brief Construct a frozen orbit from an incomplete set of COEs
///
/// The critical angles for inclination are 63.4349 degrees and 116.5651 degrees.
/// The critical angles for AoP are 90.0 degrees and 270.0 degrees.
///
/// At a minimum, a semi-major axis, equatorial radius, J2, and J3 must be provided. In this case,
/// the inclination and AoP are set to critical angles, and the eccentricity is derived from inclination.
/// RAAN and true anomaly default to zero degrees.
///
/// Additionally, the following combinations of inputs are supported:
/// - AoP (inclination set to critical value, eccentricity derived)
/// - AoP and eccentricity (inclination derived)
/// - AoP and inclination, but at least one of them must be a critical value (eccentricity derived)
/// - Inclination (AoP set to critical value, eccentricity derived)
/// - Eccentricity (AoP set to critical value, inclination derived)
///
/// Note that inclination and eccentricity cannot both be provided.
///
/// RAAN and True Anomaly may be provided alongside any of these arguments, and will be passed through
/// to the resulting COE as they do not impact the frozen orbit condition.
///
/// @param aSemiMajorAxis A semi-major axis
/// @param anEquatorialRadius An equatorial radius
/// @param aJ2 A J2
/// @param aJ3 A J3
/// @param anEccentricity An eccentricity
/// @param anInclination An inclination
/// @param aRaan A raan
/// @param anAop An aop
/// @param aTrueAnomaly A true anomaly
/// @return COE
static COE FrozenOrbit(
const Length& aSemiMajorAxis,
const Length& anEquatorialRadius,
const Real& aJ2,
const Real& aJ3,
const Real& anEccentricity = Real::Undefined(),
const Angle& anInclination = Angle::Undefined(),
const Angle& aRaan = Angle::Degrees(0.0),
const Angle& anAop = Angle::Undefined(),
const Angle& aTrueAnomaly = Angle::Degrees(0.0)
);

/// @brief Convert True anomaly to Eccentric anomaly
///
/// @param aTrueAnomaly A true anomaly
Expand Down
Loading

0 comments on commit 6956a4e

Please sign in to comment.