diff --git a/bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/Orbit/Pass.cpp b/bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/Orbit/Pass.cpp index 79975aacb..e8628d127 100644 --- a/bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/Orbit/Pass.cpp +++ b/bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/Orbit/Pass.cpp @@ -135,6 +135,28 @@ inline void OpenSpaceToolkitAstrodynamicsPy_Trajectory_Orbit_Pass(pybind11::modu )doc" ) + .def( + "get_start_instant", + &Pass::getStartInstant, + R"doc( + Get the start instant of the pass. For partial passes, this is the minimum defined instant. + + Returns: + Instant: The start instant of the pass. + + )doc" + ) + .def( + "get_end_instant", + &Pass::getEndInstant, + R"doc( + Get the end instant of the pass. For partial passes, this is the maximum defined instant. + + Returns: + Instant: The end instant of the pass. + + )doc" + ) .def( "get_instant_at_ascending_node", &Pass::accessInstantAtAscendingNode, diff --git a/bindings/python/test/trajectory/orbit/test_pass.py b/bindings/python/test/trajectory/orbit/test_pass.py index 6c8a571d1..99f80b5b7 100644 --- a/bindings/python/test/trajectory/orbit/test_pass.py +++ b/bindings/python/test/trajectory/orbit/test_pass.py @@ -41,6 +41,12 @@ def test_get_revolution_number(self, pass_: Pass): def test_get_duration(self, pass_: Pass): assert pass_.get_duration() is not None + def test_get_start_instant(self, pass_: Pass): + assert pass_.get_start_instant() is not None + + def test_get_end_instant(self, pass_: Pass): + assert pass_.get_end_instant() is not None + def test_get_instant_at_ascending_node(self, pass_: Pass): assert pass_.get_instant_at_ascending_node() is not None diff --git a/include/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.hpp b/include/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.hpp index 1e0cba2e4..81f38d5fd 100644 --- a/include/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.hpp +++ b/include/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.hpp @@ -113,6 +113,16 @@ class Pass /// @return The duration of the pass. Duration getDuration() const; + /// @brief Get the start instant of the pass. For partial passes, this is the minimum defined instant. + /// + /// @return The start instant of the pass. + Instant getStartInstant() const; + + /// @brief Get the end instant of the pass. For partial passes, this is the maximum defined instant. + /// + /// @return The end instant of the pass. + Instant getEndInstant() const; + /// @brief Accesses the instant at the ascending node of the pass. /// /// @return The instant at the ascending node of the pass. diff --git a/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.cpp b/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.cpp index b9800be81..741909d95 100644 --- a/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.cpp +++ b/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.cpp @@ -1,5 +1,6 @@ /// Apache License 2.0 +#include #include #include @@ -31,6 +32,7 @@ namespace astrodynamics namespace trajectory { +using ostk::core::container::Tuple; using ostk::core::type::Index; using ostk::core::type::Real; using ostk::core::type::Uint8; @@ -179,54 +181,79 @@ Pass Orbit::getPassWithRevolutionNumber(const Integer& aRevolutionNumber, const const std::lock_guard lock {this->mutex_}; - const auto passMapIt = this->passMap_.find(aRevolutionNumber); - - if (passMapIt != this->passMap_.end()) - { - return passMapIt->second; - } - else + const auto getClosestPass = [this](const Integer& aRevolutionNumber) -> Pass { - // If any, get pass with closest revolution number + if (this->passMap_.empty()) + { + return Pass::Undefined(); + } - Pass const* closestPasSPtr = nullptr; + // exact revolution number exists + + if (this->passMap_.count(aRevolutionNumber)) + { + return this->passMap_.at(aRevolutionNumber); + } const auto lowerBoundMapIt = this->passMap_.lower_bound(aRevolutionNumber); - if (lowerBoundMapIt != this->passMap_.end()) - { - if (lowerBoundMapIt == this->passMap_.begin()) - { - closestPasSPtr = &(lowerBoundMapIt->second); - } - else - { - const auto closestPassMapIt = std::prev(lowerBoundMapIt); + // Revolution number is greater than any existing revolution number in map + // {5, 6, 9, 10} -> aRevolutionNumber=12 -> return 10 - if ((aRevolutionNumber - closestPassMapIt->first) < (lowerBoundMapIt->first - aRevolutionNumber)) - { - closestPasSPtr = &(closestPassMapIt->second); - } - else - { - closestPasSPtr = &(lowerBoundMapIt->second); - } - } + if (lowerBoundMapIt == this->passMap_.end()) + { + return this->passMap_.rbegin()->second; } - else if (this->passMap_.size() > 0) + + // Revolution number is lesser than any existing revolution number in map + // {5, 6, 9, 10} -> aRevolutionNumber=4 -> return 5 + + if (lowerBoundMapIt == this->passMap_.begin()) { - closestPasSPtr = &(this->passMap_.begin()->second); + return this->passMap_.begin()->second; } - Pass currentPass = Pass::Undefined(); + // Closest revolution number is within the map + + const auto closestPassMapIt = std::prev(lowerBoundMapIt); - if (closestPasSPtr != nullptr) // Pass with closest revolution number found + // {5, 6, 9, 10} -> aRevolutionNumber=7 -> return 6 + // lowerBoundMapIt = 9, closestPassMapIt = 6 + + if ((aRevolutionNumber - closestPassMapIt->first) < (lowerBoundMapIt->first - aRevolutionNumber)) { - currentPass = *closestPasSPtr; + return closestPassMapIt->second; } + // {5, 6, 9, 10} -> aRevolutionNumber=8 -> return 9 + // lowerBoundMapIt = 9, closestPassMapIt = 6 + + return lowerBoundMapIt->second; + }; + + Pass currentPass = getClosestPass(aRevolutionNumber); + + Integer currentRevolutionNumber = + currentPass.isDefined() ? currentPass.getRevolutionNumber() : this->modelPtr_->getRevolutionNumberAtEpoch(); + + const bool isForwardPropagated = currentRevolutionNumber <= aRevolutionNumber; + const Integer propagationSign = isForwardPropagated ? 1 : -1; + + const auto computeCrossings = [this, &isForwardPropagated]( + Instant previousInstant, const Duration& stepDuration + ) -> Tuple + { + Instant northPointCrossing = Instant::Undefined(); + Instant southPointCrossing = Instant::Undefined(); + Instant descendingNodeCrossing = Instant::Undefined(); + Instant passBreakCrossing = Instant::Undefined(); + const Instant epoch = this->modelPtr_->getEpoch(); + const State previousState = this->modelPtr_->calculateStateAt(previousInstant); + Real previousStateCoordinates_ECI_z = previousState.getPosition().accessCoordinates().z(); + Real previousStateCoordinates_ECI_zdot = previousState.getVelocity().accessCoordinates().z(); + const auto getZ = [this, &epoch](const double& aDurationInSeconds) -> Real { return this->modelPtr_->calculateStateAt(epoch + Duration::Seconds(aDurationInSeconds)) @@ -243,137 +270,149 @@ Pass Orbit::getPassWithRevolutionNumber(const Integer& aRevolutionNumber, const .z(); }; - while ((!currentPass.isDefined()) || (currentPass.getRevolutionNumber() != aRevolutionNumber)) + while (!passBreakCrossing.isDefined()) { - Integer currentRevolutionNumber = currentPass.isDefined() ? currentPass.getRevolutionNumber() - : this->modelPtr_->getRevolutionNumberAtEpoch(); - Instant previousInstant = currentPass.isDefined() - ? (currentPass.accessInstantAtPassBreak() + Duration::Microseconds(1.0)) - : this->modelPtr_->getEpoch(); - - Duration stepDuration = aStepDuration; - if (currentPass.isDefined() && currentPass.isComplete()) + const Instant currentInstant = previousInstant + stepDuration; + + const State currentState = this->modelPtr_->calculateStateAt(currentInstant); + const Real currentStateCoordinates_ECI_z = currentState.getPosition().accessCoordinates().z(); + const Real currentStateCoordinates_ECI_zdot = currentState.getVelocity().accessCoordinates().z(); + + if ((previousStateCoordinates_ECI_z == 0.0) && (currentStateCoordinates_ECI_z == 0.0)) { - Array durations = { - (currentPass.accessInstantAtNorthPoint() - currentPass.accessInstantAtAscendingNode()), - (currentPass.accessInstantAtDescendingNode() - currentPass.accessInstantAtNorthPoint()), - (currentPass.accessInstantAtSouthPoint() - currentPass.accessInstantAtDescendingNode()), - (currentPass.accessInstantAtPassBreak() - currentPass.accessInstantAtSouthPoint()), - }; - stepDuration = *std::min_element(durations.begin(), durations.end()) / 2.0; + throw ostk::core::error::runtime::ToBeImplemented("Equatorial orbit support."); } - if (currentRevolutionNumber <= aRevolutionNumber) // Forward propagation + if (((previousStateCoordinates_ECI_zdot > 0.0) && (currentStateCoordinates_ECI_zdot <= 0.0)) || + ((previousStateCoordinates_ECI_zdot < 0.0) && (currentStateCoordinates_ECI_zdot >= 0.0))) { - const State previousState = this->modelPtr_->calculateStateAt(previousInstant); - Real previousStateCoordinates_ECI_z = previousState.getPosition().accessCoordinates().z(); - Real previousStateCoordinates_ECI_zdot = previousState.getVelocity().accessCoordinates().z(); - - Instant northPointCrossing = Instant::Undefined(); - Instant southPointCrossing = Instant::Undefined(); - Instant descendingNodeCrossing = Instant::Undefined(); - - while (true) + if (currentStateCoordinates_ECI_z > 0.0) { - const Instant currentInstant = previousInstant + stepDuration; - - const State currentState = this->modelPtr_->calculateStateAt(currentInstant); - const Real currentStateCoordinates_ECI_z = currentState.getPosition().accessCoordinates().z(); - const Real currentStateCoordinates_ECI_zdot = currentState.getVelocity().accessCoordinates().z(); - - if ((previousStateCoordinates_ECI_z == 0.0) && (currentStateCoordinates_ECI_z == 0.0)) - { - throw ostk::core::error::runtime::ToBeImplemented("Equatorial orbit support."); - } - - if (((previousStateCoordinates_ECI_zdot > 0.0) && (currentStateCoordinates_ECI_zdot <= 0.0)) || - ((previousStateCoordinates_ECI_zdot < 0.0) && (currentStateCoordinates_ECI_zdot >= 0.0))) - { - if (currentStateCoordinates_ECI_z > 0.0) - { - northPointCrossing = - Orbit::GetCrossingInstant(epoch, previousInstant, currentInstant, getZDot); - } - else - { - southPointCrossing = - Orbit::GetCrossingInstant(epoch, previousInstant, currentInstant, getZDot); - } - } - - if ((previousStateCoordinates_ECI_z > 0.0) && (currentStateCoordinates_ECI_z <= 0.0)) - { - descendingNodeCrossing = - Orbit::GetCrossingInstant(epoch, previousInstant, currentInstant, getZ); - } - - if ((previousStateCoordinates_ECI_z < 0.0) && (currentStateCoordinates_ECI_z >= 0.0)) - { - previousInstant = Orbit::GetCrossingInstant(epoch, previousInstant, currentInstant, getZ); - break; - } - - previousStateCoordinates_ECI_z = currentStateCoordinates_ECI_z; - previousStateCoordinates_ECI_zdot = currentStateCoordinates_ECI_zdot; - previousInstant = currentInstant; + northPointCrossing = Orbit::GetCrossingInstant(epoch, previousInstant, currentInstant, getZDot); } + else + { + southPointCrossing = Orbit::GetCrossingInstant(epoch, previousInstant, currentInstant, getZDot); + } + } + + if ((previousStateCoordinates_ECI_z > 0.0) && (currentStateCoordinates_ECI_z <= 0.0)) + { + const Instant crossingInstant = Orbit::GetCrossingInstant(epoch, previousInstant, currentInstant, getZ); - if (currentPass.isDefined()) + if (isForwardPropagated) { - currentPass = { - currentRevolutionNumber + 1, - currentPass.accessInstantAtPassBreak(), - northPointCrossing, - descendingNodeCrossing, - southPointCrossing, - previousInstant, - }; + descendingNodeCrossing = crossingInstant; } else { - const Instant ascendingNodeCrossing = - Real(this->modelPtr_->calculateStateAt(this->modelPtr_->getEpoch()) - .getPosition() - .accessCoordinates() - .z()) - .isNear(0.0, epsilon) - ? this->modelPtr_->getEpoch() - : Instant::Undefined(); - - currentPass = { - currentRevolutionNumber, - ascendingNodeCrossing, - northPointCrossing, - descendingNodeCrossing, - southPointCrossing, - previousInstant, - }; + passBreakCrossing = crossingInstant; } } - else // Reverse propagation - { - throw ostk::core::error::runtime::ToBeImplemented( - "Orbit::getPassWithRevolutionNumber > Reverse propagation" - ); - } - if (currentPass.isDefined()) + if ((previousStateCoordinates_ECI_z < 0.0) && (currentStateCoordinates_ECI_z >= 0.0)) { - this->passMap_.insert({currentPass.getRevolutionNumber(), currentPass}); + const Instant crossingInstant = Orbit::GetCrossingInstant(epoch, previousInstant, currentInstant, getZ); + if (isForwardPropagated) + { + passBreakCrossing = crossingInstant; + } + else + { + descendingNodeCrossing = crossingInstant; + } } + + previousStateCoordinates_ECI_z = currentStateCoordinates_ECI_z; + previousStateCoordinates_ECI_zdot = currentStateCoordinates_ECI_zdot; + previousInstant = currentInstant; } - if (currentPass.getRevolutionNumber() == aRevolutionNumber) + return { + northPointCrossing, + descendingNodeCrossing, + southPointCrossing, + passBreakCrossing, + }; + }; + + Instant northPointCrossing = Instant::Undefined(); + Instant southPointCrossing = Instant::Undefined(); + Instant descendingNodeCrossing = Instant::Undefined(); + Instant passBreakCrossing = Instant::Undefined(); + + if (!currentPass.isDefined()) + { + const Instant previousInstant = this->modelPtr_->getEpoch(); + + std::tie(northPointCrossing, descendingNodeCrossing, southPointCrossing, passBreakCrossing) = + computeCrossings(previousInstant, aStepDuration * Real::Integer(propagationSign)); + + const Instant crossingInstant = + Real(this->modelPtr_->calculateStateAt(this->modelPtr_->getEpoch()).getPosition().accessCoordinates().z()) + .isNear(0.0, epsilon) + ? this->modelPtr_->getEpoch() + : Instant::Undefined(); + + const Instant ascendingNodeCrossing = isForwardPropagated ? crossingInstant : passBreakCrossing; + passBreakCrossing = isForwardPropagated ? passBreakCrossing : crossingInstant; + + currentPass = { + currentRevolutionNumber, + ascendingNodeCrossing, + northPointCrossing, + descendingNodeCrossing, + southPointCrossing, + passBreakCrossing, + }; + + if (currentPass.isComplete()) { - return currentPass; + this->passMap_.insert({currentPass.getRevolutionNumber(), currentPass}); } - else + } + + while ((currentPass.getRevolutionNumber() != aRevolutionNumber)) + { + Duration stepDuration = aStepDuration; + + if (currentPass.isComplete()) { - throw ostk::core::error::RuntimeError("Cannot get pass with revolution # [{}].", aRevolutionNumber); + Array durations = { + (currentPass.accessInstantAtNorthPoint() - currentPass.accessInstantAtAscendingNode()), + (currentPass.accessInstantAtDescendingNode() - currentPass.accessInstantAtNorthPoint()), + (currentPass.accessInstantAtSouthPoint() - currentPass.accessInstantAtDescendingNode()), + (currentPass.accessInstantAtPassBreak() - currentPass.accessInstantAtSouthPoint()), + }; + stepDuration = *std::min_element(durations.begin(), durations.end()) / 2.0; } + + const Instant previousInstant = isForwardPropagated + ? currentPass.getEndInstant() + Duration::Microseconds(1.0) + : currentPass.getStartInstant() - Duration::Microseconds(1.0); + + std::tie(northPointCrossing, descendingNodeCrossing, southPointCrossing, passBreakCrossing) = + computeCrossings(previousInstant, stepDuration * Real::Integer(propagationSign)); + + currentRevolutionNumber += propagationSign; + + const Instant ascendingNodeCrossing = + isForwardPropagated ? currentPass.accessInstantAtPassBreak() : passBreakCrossing; + passBreakCrossing = isForwardPropagated ? passBreakCrossing : currentPass.accessInstantAtAscendingNode(); + + currentPass = { + currentRevolutionNumber, + ascendingNodeCrossing, + northPointCrossing, + descendingNodeCrossing, + southPointCrossing, + passBreakCrossing, + }; + + this->passMap_.insert({currentPass.getRevolutionNumber(), currentPass}); } - return Pass::Undefined(); + return currentPass; } Shared Orbit::getOrbitalFrame(const Orbit::FrameType& aFrameType) const diff --git a/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.cpp b/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.cpp index e2420ab98..51e6e189c 100644 --- a/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.cpp +++ b/src/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.cpp @@ -187,6 +187,66 @@ Duration Pass::getDuration() const return Duration::Undefined(); } +Instant Pass::getStartInstant() const +{ + if (!this->isDefined()) + { + throw ostk::core::error::runtime::Undefined("Pass"); + } + + if (instantAtAscendingNode_.isDefined()) + { + return instantAtAscendingNode_; + } + + if (instantAtNorthPoint_.isDefined()) + { + return instantAtNorthPoint_; + } + + if (instantAtDescendingNode_.isDefined()) + { + return instantAtDescendingNode_; + } + + if (instantAtSouthPoint_.isDefined()) + { + return instantAtSouthPoint_; + } + + return instantAtPassBreak_; +} + +Instant Pass::getEndInstant() const +{ + if (!this->isDefined()) + { + throw ostk::core::error::runtime::Undefined("Pass"); + } + + if (instantAtPassBreak_.isDefined()) + { + return instantAtPassBreak_; + } + + if (instantAtSouthPoint_.isDefined()) + { + return instantAtSouthPoint_; + } + + if (instantAtDescendingNode_.isDefined()) + { + return instantAtDescendingNode_; + } + + if (instantAtNorthPoint_.isDefined()) + { + return instantAtNorthPoint_; + } + + return instantAtAscendingNode_; +} + const Instant& Pass::accessInstantAtAscendingNode() const { if (!this->isDefined()) diff --git a/test/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.test.cpp b/test/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.test.cpp index f40ed0db7..7c06a126f 100644 --- a/test/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.test.cpp +++ b/test/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit.test.cpp @@ -776,18 +776,111 @@ TEST(OpenSpaceToolkit_Astrodynamics_Trajectory_Orbit, GetPassWithRevolutionNumbe "1 7276U 74026A 19177.06815612 -.00000040 00000-0 -59146-3 0 9999", "2 7276 63.5942 41.6062 6850823 289.3685 12.0680 2.45095050222705", }; - const SGP4 sgp4 = SGP4(tle); - const Orbit orbit = {sgp4, environment.accessCelestialObjectWithName("Earth")}; + // Forward propagation (Validated with STK) + { + const Orbit orbit = {sgp4, environment.accessCelestialObjectWithName("Earth")}; + + { + const Pass pass = orbit.getPassWithRevolutionNumber(22273, Duration::Minutes(10.0)); + + EXPECT_TRUE(pass.isDefined()); + EXPECT_EQ(pass.getType(), Pass::Type::Complete); + EXPECT_EQ(pass.getRevolutionNumber(), 22273); + EXPECT_GT( + Duration::Milliseconds(1.0), + Duration::Between( + pass.accessInstantAtAscendingNode(), + Instant::Parse("2019-06-26 21:13:14.138.000.000", Scale::UTC) + ) + .getAbsolute() + ); + EXPECT_GT( + Duration::Milliseconds(1.0), + Duration::Between( + pass.accessInstantAtPassBreak(), Instant::Parse("2019-06-27 07:00:45.686.000.000", Scale::UTC) + ) + .getAbsolute() + ); + } + + // Backward propagation (seeded with first pass from the map) + { + const Pass pass = orbit.getPassWithRevolutionNumber(22267, Duration::Minutes(10.0)); + + EXPECT_TRUE(pass.isDefined()); + EXPECT_EQ(pass.getType(), Pass::Type::Complete); + EXPECT_EQ(pass.getRevolutionNumber(), 22267); + EXPECT_GT( + Duration::Milliseconds(1.0), + Duration::Between( + pass.accessInstantAtAscendingNode(), + Instant::Parse("2019-06-24 10:28:04.184.000.000", Scale::UTC) + ) + .getAbsolute() + ); + EXPECT_GT( + Duration::Milliseconds(1.0), + Duration::Between( + pass.accessInstantAtPassBreak(), Instant::Parse("2019-06-24 20:15:35.899.000.000", Scale::UTC) + ) + .getAbsolute() + ); + } + } + + // Backward propagation (Validated with STK) + { + const Orbit orbit = {sgp4, environment.accessCelestialObjectWithName("Earth")}; - const Pass pass = orbit.getPassWithRevolutionNumber(22273, Duration::Minutes(10.0)); + { + const Pass pass = orbit.getPassWithRevolutionNumber(22267, Duration::Minutes(10.0)); + + EXPECT_TRUE(pass.isDefined()); + EXPECT_EQ(pass.getType(), Pass::Type::Complete); + EXPECT_EQ(pass.getRevolutionNumber(), 22267); + EXPECT_GT( + Duration::Milliseconds(1.0), + Duration::Between( + pass.accessInstantAtAscendingNode(), + Instant::Parse("2019-06-24 10:28:04.184.000.000", Scale::UTC) + ) + .getAbsolute() + ); + EXPECT_GT( + Duration::Milliseconds(1.0), + Duration::Between( + pass.accessInstantAtPassBreak(), Instant::Parse("2019-06-24 20:15:35.899.000.000", Scale::UTC) + ) + .getAbsolute() + ); + } - EXPECT_TRUE(pass.isDefined()); - EXPECT_EQ(pass.getType(), Pass::Type::Complete); - EXPECT_EQ(pass.getRevolutionNumber(), 22273); - EXPECT_EQ(pass.accessInstantAtAscendingNode(), Instant::Parse("2019-06-26 21:13:14.137.431.788", Scale::UTC)); - EXPECT_EQ(pass.accessInstantAtPassBreak(), Instant::Parse("2019-06-27 07:00:45.686.118.913", Scale::UTC)); + // Forward propagation (seeded with last pass from the map) + { + const Pass pass = orbit.getPassWithRevolutionNumber(22273, Duration::Minutes(10.0)); + + EXPECT_TRUE(pass.isDefined()); + EXPECT_EQ(pass.getType(), Pass::Type::Complete); + EXPECT_EQ(pass.getRevolutionNumber(), 22273); + EXPECT_GT( + Duration::Milliseconds(1.0), + Duration::Between( + pass.accessInstantAtAscendingNode(), + Instant::Parse("2019-06-26 21:13:14.138.000.000", Scale::UTC) + ) + .getAbsolute() + ); + EXPECT_GT( + Duration::Milliseconds(1.0), + Duration::Between( + pass.accessInstantAtPassBreak(), Instant::Parse("2019-06-27 07:00:45.686.000.000", Scale::UTC) + ) + .getAbsolute() + ); + } + } } } diff --git a/test/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.test.cpp b/test/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.test.cpp index 652c01509..91924da9a 100644 --- a/test/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.test.cpp +++ b/test/OpenSpaceToolkit/Astrodynamics/Trajectory/Orbit/Pass.test.cpp @@ -564,6 +564,124 @@ TEST_F(OpenSpaceToolkit_Astrodynamics_Trajectory_Orbit_Pass, GetDuration) } } +TEST_F(OpenSpaceToolkit_Astrodynamics_Trajectory_Orbit_Pass, GetStartInstant) +{ + { + EXPECT_THROW(Pass::Undefined().getStartInstant(), ostk::core::error::runtime::Undefined); + } + + { + EXPECT_EQ(defaultPass_.getStartInstant(), defaultInstantAtAscendingNode_); + } + + { + const Pass pass = { + defaultRevolutionNumber_, + Instant::Undefined(), + defaultInstantAtNorthPoint_, + defaultInstantAtDescendingNode_, + defaultInstantAtSouthPoint_, + defaultInstantAtPassBreak_, + }; + EXPECT_EQ(pass.getStartInstant(), defaultInstantAtNorthPoint_); + } + + { + const Pass pass = { + defaultRevolutionNumber_, + Instant::Undefined(), + Instant::Undefined(), + defaultInstantAtDescendingNode_, + defaultInstantAtSouthPoint_, + defaultInstantAtPassBreak_, + }; + EXPECT_EQ(pass.getStartInstant(), defaultInstantAtDescendingNode_); + } + + { + const Pass pass = { + defaultRevolutionNumber_, + Instant::Undefined(), + Instant::Undefined(), + Instant::Undefined(), + defaultInstantAtSouthPoint_, + defaultInstantAtPassBreak_, + }; + EXPECT_EQ(pass.getStartInstant(), defaultInstantAtSouthPoint_); + } + + { + const Pass pass = { + defaultRevolutionNumber_, + Instant::Undefined(), + Instant::Undefined(), + Instant::Undefined(), + Instant::Undefined(), + defaultInstantAtPassBreak_, + }; + EXPECT_EQ(pass.getStartInstant(), defaultInstantAtPassBreak_); + } +} + +TEST_F(OpenSpaceToolkit_Astrodynamics_Trajectory_Orbit_Pass, GetEndInstant) +{ + { + EXPECT_THROW(Pass::Undefined().getEndInstant(), ostk::core::error::runtime::Undefined); + } + + { + EXPECT_EQ(defaultPass_.getEndInstant(), defaultInstantAtPassBreak_); + } + + { + const Pass pass = { + defaultRevolutionNumber_, + defaultInstantAtAscendingNode_, + defaultInstantAtNorthPoint_, + defaultInstantAtDescendingNode_, + defaultInstantAtSouthPoint_, + Instant::Undefined(), + }; + EXPECT_EQ(pass.getEndInstant(), defaultInstantAtSouthPoint_); + } + + { + const Pass pass = { + defaultRevolutionNumber_, + defaultInstantAtAscendingNode_, + defaultInstantAtNorthPoint_, + defaultInstantAtDescendingNode_, + Instant::Undefined(), + Instant::Undefined(), + }; + EXPECT_EQ(pass.getEndInstant(), defaultInstantAtDescendingNode_); + } + + { + const Pass pass = { + defaultRevolutionNumber_, + defaultInstantAtAscendingNode_, + defaultInstantAtNorthPoint_, + Instant::Undefined(), + Instant::Undefined(), + Instant::Undefined(), + }; + EXPECT_EQ(pass.getEndInstant(), defaultInstantAtNorthPoint_); + } + + { + const Pass pass = { + defaultRevolutionNumber_, + defaultInstantAtAscendingNode_, + Instant::Undefined(), + Instant::Undefined(), + Instant::Undefined(), + Instant::Undefined(), + }; + EXPECT_EQ(pass.getEndInstant(), defaultInstantAtAscendingNode_); + } +} + TEST_F(OpenSpaceToolkit_Astrodynamics_Trajectory_Orbit_Pass, AccessInstantAtAscendingNode) { {