Skip to content

Commit

Permalink
feat: getBodyFrame and getAxesAt for Tabulated Profile (#429)
Browse files Browse the repository at this point in the history
* feat: getBodyFrame and getAxesAt for Tabulated Profile

* Apply suggestions from code review
  • Loading branch information
vishwa2710 authored Aug 21, 2024
1 parent df5fb49 commit 122a3e0
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 74 deletions.
94 changes: 44 additions & 50 deletions bindings/python/test/flight/test_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,55 @@

@pytest.fixture
def instant() -> Instant:
return Instant.date_time(DateTime(2020, 1, 3), Scale.UTC)
return Instant.date_time(DateTime(2020, 1, 1, 0, 0, 30), Scale.UTC)


@pytest.fixture
def profile() -> Profile:
def transform_model() -> TransformModel:
def dynamic_provider_generator(instant: Instant):
return Transform.identity(instant)

return Profile(
model=TransformModel(
dynamic_provider=DynamicProvider(dynamic_provider_generator),
frame=Frame.GCRF(),
),
return TransformModel(
dynamic_provider=DynamicProvider(dynamic_provider_generator),
frame=Frame.GCRF(),
)


@pytest.fixture
def tabulated_model() -> TabulatedModel:
return TabulatedModel(
states=[
State(
instant=Instant.date_time(datetime(2020, 1, 1, 0, 0, 0), Scale.UTC),
position=Position.meters((0.0, 0.0, 0.0), Frame.GCRF()),
velocity=Velocity.meters_per_second((0.0, 0.0, 0.0), Frame.GCRF()),
attitude=Quaternion.unit(),
angular_velocity=(0.0, 0.0, 0.0),
attitude_frame=Frame.GCRF(),
),
State(
instant=Instant.date_time(datetime(2020, 1, 1, 0, 1, 0), Scale.UTC),
position=Position.meters((0.0, 0.0, 0.0), Frame.GCRF()),
velocity=Velocity.meters_per_second((0.0, 0.0, 0.0), Frame.GCRF()),
attitude=Quaternion.unit(),
angular_velocity=(0.0, 0.0, 0.0),
attitude_frame=Frame.GCRF(),
),
],
)


@pytest.fixture(
params=[
"transform_model",
"tabulated_model",
]
)
def profile(request) -> Profile:
model: TransformModel | TabulatedModel = request.getfixturevalue(request.param)
return Profile(model=model)


class TestProfile:
def test_constructors(self, profile: Profile):
assert profile is not None
Expand All @@ -68,7 +101,10 @@ def test_get_axes_at(self, profile: Profile, instant: Instant):
assert axes is not None
assert isinstance(axes, Axes)

def test_get_body_frame(self, profile: Profile, instant: Instant):
def test_get_body_frame(self, profile: Profile):
if Frame.exists("Name"):
Frame.destruct("Name")

frame = profile.get_body_frame("Name")

assert frame is not None
Expand Down Expand Up @@ -109,45 +145,3 @@ def test_nadir_pointing(self):
assert profile is not None
assert isinstance(profile, Profile)
assert profile.is_defined()

def test_tabulated(self):
profile = Profile(
model=TabulatedModel(
states=[
State(
instant=Instant.date_time(
datetime(2020, 1, 1, 0, 0, 0), Scale.UTC
),
position=Position.meters((0.0, 0.0, 0.0), Frame.GCRF()),
velocity=Velocity.meters_per_second(
(0.0, 0.0, 0.0), Frame.GCRF()
),
attitude=Quaternion.unit(),
angular_velocity=(0.0, 0.0, 0.0),
attitude_frame=Frame.GCRF(),
),
State(
instant=Instant.date_time(
datetime(2020, 1, 1, 0, 1, 0), Scale.UTC
),
position=Position.meters((0.0, 0.0, 0.0), Frame.GCRF()),
velocity=Velocity.meters_per_second(
(0.0, 0.0, 0.0), Frame.GCRF()
),
attitude=Quaternion.unit(),
angular_velocity=(0.0, 0.0, 0.0),
attitude_frame=Frame.GCRF(),
),
],
),
)

assert isinstance(profile, Profile)
assert profile.is_defined()

assert (
profile.get_state_at(
Instant.date_time(datetime(2020, 1, 1, 0, 0, 30), Scale.UTC)
)
is not None
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#include <OpenSpaceToolkit/Mathematics/Geometry/3D/Transformation/Rotation/Quaternion.hpp>

#include <OpenSpaceToolkit/Physics/Coordinate/Frame/Provider/Dynamic.hpp>

#include <OpenSpaceToolkit/Astrodynamics/Flight/Profile/Model/Tabulated.hpp>

namespace ostk
Expand All @@ -18,7 +20,18 @@ namespace profile
namespace model
{

using ostk::core::container::Unpack;
using ostk::core::type::Real;
using ostk::core::type::String;

using ostk::mathematics::geometry::d3::transformation::rotation::Quaternion;
using ostk::mathematics::object::Vector3d;

using DynamicProvider = ostk::physics::coordinate::frame::provider::Dynamic;
using ostk::physics::coordinate::Position;
using ostk::physics::coordinate::Transform;
using ostk::physics::coordinate::Velocity;
using ostk::physics::time::Duration;

Tabulated::Tabulated(const Array<State>& aStateArray)
: Model(),
Expand Down Expand Up @@ -71,14 +84,6 @@ Interval Tabulated::getInterval() const

State Tabulated::calculateStateAt(const Instant& anInstant) const
{
using ostk::core::type::Real;

using ostk::mathematics::object::Vector3d;

using ostk::physics::coordinate::Position;
using ostk::physics::coordinate::Velocity;
using ostk::physics::time::Duration;

if (!anInstant.isDefined())
{
throw ostk::core::error::runtime::Undefined("Instant");
Expand Down Expand Up @@ -147,9 +152,14 @@ Axes Tabulated::getAxesAt(const Instant& anInstant) const
throw ostk::core::error::runtime::Undefined("Tabulated");
}

throw ostk::core::error::runtime::ToBeImplemented("Tabulated::getAxesAt");
const State state = this->calculateStateAt(anInstant);
const Quaternion q_GCRF_BODY = state.getAttitude().toConjugate();

return Axes::Undefined();
const Vector3d xAxis = q_GCRF_BODY * Vector3d::X();
const Vector3d yAxis = q_GCRF_BODY * Vector3d::Y();
const Vector3d zAxis = q_GCRF_BODY * Vector3d::Z();

return {xAxis, yAxis, zAxis, state.accessFrame()};
}

Shared<const Frame> Tabulated::getBodyFrame(const String& aFrameName) const
Expand All @@ -159,15 +169,35 @@ Shared<const Frame> Tabulated::getBodyFrame(const String& aFrameName) const
throw ostk::core::error::runtime::Undefined("Body frame name");
}

throw ostk::core::error::runtime::ToBeImplemented("Tabulated::getBodyFrame");
if (!this->isDefined())
{
throw ostk::core::error::runtime::Undefined("Tabulated");
}

return nullptr;
const DynamicProvider dynamicTransformProvider = {
[this](const Instant& anInstant) -> Transform
{
const State state = this->calculateStateAt(anInstant);
return Transform::Passive(
anInstant,
-state.getPosition().getCoordinates(),
-state.getVelocity().getCoordinates(),
state.getAttitude().normalize(),
Vector3d::Zero()
);
}
};

return Frame::Construct(
aFrameName,
false,
this->states_.accessFirst().accessFrame(),
std::make_shared<const DynamicProvider>(dynamicTransformProvider)
);
}

void Tabulated::print(std::ostream& anOutputStream, bool displayDecorator) const
{
using ostk::core::type::String;

displayDecorator ? ostk::core::utils::Print::Header(anOutputStream, "Tabulated") : void();

ostk::core::utils::Print::Line(anOutputStream)
Expand Down Expand Up @@ -224,8 +254,6 @@ bool Tabulated::operator!=(const Model& aModel) const

Pair<const State*, const State*> Tabulated::accessStateRangeAt(const Instant& anInstant) const
{
using ostk::core::container::Unpack;

State const* previousStatePtr = nullptr;
State const* nextStatePtr = nullptr;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

using ostk::core::container::Array;
using ostk::core::container::String;
using ostk::core::type::Shared;

using ostk::mathematics::geometry::d3::transformation::rotation::Quaternion;
using ostk::mathematics::object::Vector3d;
using ostk::mathematics::object::VectorXd;

using ostk::physics::coordinate::Axes;
using ostk::physics::coordinate::Frame;
using ostk::physics::coordinate::Position;
using ostk::physics::coordinate::Velocity;
Expand Down Expand Up @@ -160,7 +162,7 @@ TEST_F(OpenSpaceToolkit_Astrodynamics_Flight_Profile_Models_Tabulated, Calculate
}
}

TEST_F(OpenSpaceToolkit_Astrodynamics_Flight_Profile_Models_Tabulated, getAxesAt)
TEST_F(OpenSpaceToolkit_Astrodynamics_Flight_Profile_Models_Tabulated, GetAxesAt)
{
// undefined
{
Expand All @@ -176,23 +178,50 @@ TEST_F(OpenSpaceToolkit_Astrodynamics_Flight_Profile_Models_Tabulated, getAxesAt
EXPECT_THROW(tabulated_.getAxesAt(instant), ostk::core::error::runtime::Undefined);
}

// not yet implemented
{
EXPECT_THROW(
tabulated_.getAxesAt(states_.accessFirst().accessInstant()), ostk::core::error::runtime::ToBeImplemented
);
const Instant instant = Instant::DateTime(DateTime(2024, 1, 29, 0, 0, 15), Scale::UTC);
const Axes axes = tabulated_.getAxesAt(instant);
const State state = tabulated_.calculateStateAt(instant);
const Quaternion q_B_GCRF = state.getAttitude();
EXPECT_VECTORS_ALMOST_EQUAL(q_B_GCRF * axes.x(), Vector3d::X(), 1e-15);
EXPECT_VECTORS_ALMOST_EQUAL(q_B_GCRF * axes.y(), Vector3d::Y(), 1e-15);
EXPECT_VECTORS_ALMOST_EQUAL(q_B_GCRF * axes.z(), Vector3d::Z(), 1e-15);
}
}

TEST_F(OpenSpaceToolkit_Astrodynamics_Flight_Profile_Models_Tabulated, getBodyFrame)
TEST_F(OpenSpaceToolkit_Astrodynamics_Flight_Profile_Models_Tabulated, GetBodyFrame)
{
// instant undefined
// name undefined
{
const String frameName = String::Empty();
EXPECT_THROW(tabulated_.getBodyFrame(frameName), ostk::core::error::runtime::Undefined);
}

// undefined tabulated
{
EXPECT_THROW(tabulated_.getBodyFrame("Body Frame Name"), ostk::core::error::runtime::ToBeImplemented);
const Tabulated undefinedTabulated = {{}};
const String frameName = "test";
EXPECT_THROW(undefinedTabulated.getBodyFrame(frameName), ostk::core::error::runtime::Undefined);
}

{
const Shared<const Frame> bodyFrame = tabulated_.getBodyFrame("test");
EXPECT_EQ(bodyFrame->getName(), "test");
EXPECT_EQ(
Frame::GCRF()->getTransformTo(bodyFrame, states_.accessFirst().accessInstant()).getTranslation(),
-states_.accessFirst().getPosition().getCoordinates()
);
EXPECT_EQ(
Frame::GCRF()->getTransformTo(bodyFrame, states_.accessFirst().accessInstant()).getVelocity(),
-states_.accessFirst().getVelocity().getCoordinates()
);
EXPECT_EQ(
Frame::GCRF()->getTransformTo(bodyFrame, states_.accessFirst().accessInstant()).getOrientation(),
states_.accessFirst().getAttitude().normalize()
);
EXPECT_EQ(
Frame::GCRF()->getTransformTo(bodyFrame, states_.accessFirst().accessInstant()).getAngularVelocity(),
Vector3d::Zero()
);
}
}

0 comments on commit 122a3e0

Please sign in to comment.