From 633e01878f7992c4dca9aa91adfbb651dc792a17 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Sun, 17 Feb 2019 05:02:21 +0100 Subject: [PATCH 1/4] Geopotential API --- ksp_plugin/interface_external.cpp | 75 +++++++++++++++++++++ ksp_plugin_test/interface_external_test.cpp | 70 +++++++++++++++++++ serialization/journal.proto | 51 +++++++++++++- 3 files changed, 195 insertions(+), 1 deletion(-) diff --git a/ksp_plugin/interface_external.cpp b/ksp_plugin/interface_external.cpp index 81e97ecbec..e8e40349d6 100644 --- a/ksp_plugin/interface_external.cpp +++ b/ksp_plugin/interface_external.cpp @@ -3,6 +3,7 @@ #include +#include "absl/strings/str_cat.h" #include "base/array.hpp" #include "base/status.hpp" #include "base/status_or.hpp" @@ -23,6 +24,7 @@ using ksp_plugin::Vessel; using physics::BodyCentredNonRotatingDynamicFrame; using physics::ComputeApsides; using physics::DiscreteTrajectory; +using physics::OblateBody; using physics::RigidMotion; using physics::RigidTransformation; @@ -192,5 +194,78 @@ Status principia__ExternalGetNearestPlannedCoastDegreesOfFreedom( return m.Return(OK()); } +Status principia__ExternalGetGeopotentialCoefficient( + Plugin const* const plugin, + int const body_index, + int const degree, + int const order, + XY* const coefficient) { + journal::Method m{ + {plugin, + body_index, + degree, + order}, + {coefficient}}; + if (plugin == nullptr) { + return m.Return( + MakeStatus(Error::INVALID_ARGUMENT, "|plugin| must not be null")); + } + if (!plugin->HasCelestial(body_index)) { + return m.Return(MakeStatus( + Error::NOT_FOUND, + absl::StrCat("No celestial with index ", body_index))); + } + if (order < 0 || order > degree) { + return m.Return(MakeStatus( + Error::INVALID_ARGUMENT, + absl::StrCat(u8"Expected 0 ≤ order ≤ degree; got degree = ", + degree, ", order = ", order))); + } + if (degree == 0) { + *coefficient = {1, 0}; + return m.Return(OK()); + } + auto const& body = *plugin->GetCelestial(body_index).body(); + if (!body.is_oblate()) { + *coefficient = {0, 0}; + return m.Return(OK()); + } + auto const& oblate_body = dynamic_cast const&>(body); + if (degree > oblate_body.geopotential_degree()) { + *coefficient = {0, 0}; + return m.Return(OK()); + } + *coefficient = {oblate_body.cos()[degree][order], + oblate_body.sin()[degree][order]}; + return m.Return(OK()); +} + +Status principia__ExternalGetGeopotentialReferenceRadius( + Plugin const* const plugin, + int const body_index, + double* const reference_radius) { + journal::Method m{ + {plugin, + body_index}, + {reference_radius}}; + if (plugin == nullptr) { + return m.Return( + MakeStatus(Error::INVALID_ARGUMENT, "|plugin| must not be null")); + } + if (!plugin->HasCelestial(body_index)) { + return m.Return(MakeStatus( + Error::NOT_FOUND, + absl::StrCat("No celestial with index ", body_index))); + } + auto const& body = *plugin->GetCelestial(body_index).body(); + if (!body.is_oblate()) { + *reference_radius = body.mean_radius() / Metre; + return m.Return(OK()); + } + auto const& oblate_body = dynamic_cast const&>(body); + *reference_radius = oblate_body.reference_radius() / Metre; + return m.Return(OK()); +} + } // namespace interface } // namespace principia diff --git a/ksp_plugin_test/interface_external_test.cpp b/ksp_plugin_test/interface_external_test.cpp index ba0c8ccbe5..75487fb856 100644 --- a/ksp_plugin_test/interface_external_test.cpp +++ b/ksp_plugin_test/interface_external_test.cpp @@ -20,6 +20,7 @@ using ksp_plugin::PartId; using ksp_plugin::FakePlugin; using ksp_plugin::Vessel; using physics::SolarSystem; +using quantities::Sqrt; using quantities::si::Centi; using quantities::si::Hour; using quantities::si::Kilo; @@ -114,5 +115,74 @@ TEST_F(InterfaceExternalTest, GetNearestPlannedCoastDegreesOfFreedom) { Lt(1 * Centi(Metre) / Second))))); } +TEST_F(InterfaceExternalTest, Geopotential) { + XY coefficient; + double radius; + auto status = principia__ExternalGetGeopotentialCoefficient( + &plugin_, + SolarSystemFactory::Earth, + /*degree=*/2, + /*order=*/0, + &coefficient); + EXPECT_THAT(base::Status(static_cast(status.error), ""), IsOk()); + EXPECT_THAT(-coefficient.x * Sqrt(5), IsNear(1.08e-3)); + EXPECT_THAT(coefficient.y, Eq(0)); + + status = principia__ExternalGetGeopotentialCoefficient( + &plugin_, + SolarSystemFactory::Earth, + /*degree=*/3, + /*order=*/1, + &coefficient); + EXPECT_THAT(base::Status(static_cast(status.error), ""), IsOk()); + EXPECT_THAT(coefficient.x, IsNear(2.03e-6)); + EXPECT_THAT(coefficient.y, IsNear(0.248e-6)); + + status = principia__ExternalGetGeopotentialCoefficient( + &plugin_, + SolarSystemFactory::Earth, + /*degree=*/1729, + /*order=*/163, + &coefficient); + EXPECT_THAT(base::Status(static_cast(status.error), ""), IsOk()); + EXPECT_THAT(coefficient.x, Eq(0)); + EXPECT_THAT(coefficient.y, Eq(0)); + + status = principia__ExternalGetGeopotentialReferenceRadius( + &plugin_, + SolarSystemFactory::Earth, + &radius); + EXPECT_THAT(base::Status(static_cast(status.error), ""), IsOk()); + EXPECT_THAT(radius, Eq(6'378'136.3)); + + status = principia__ExternalGetGeopotentialCoefficient( + &plugin_, + SolarSystemFactory::Ariel, + /*degree=*/2, + /*order=*/2, + &coefficient); + EXPECT_THAT(base::Status(static_cast(status.error), ""), IsOk()); + EXPECT_THAT(coefficient.x, Eq(0)); + EXPECT_THAT(coefficient.y, Eq(0)); + + status = principia__ExternalGetGeopotentialCoefficient( + &plugin_, + SolarSystemFactory::Ariel, + /*degree=*/0, + /*order=*/0, + &coefficient); + EXPECT_THAT(base::Status(static_cast(status.error), ""), IsOk()); + EXPECT_THAT(coefficient.x, Eq(1)); + EXPECT_THAT(coefficient.y, Eq(0)); + + status = principia__ExternalGetGeopotentialReferenceRadius( + &plugin_, + SolarSystemFactory::Ariel, + &radius); + EXPECT_THAT(base::Status(static_cast(status.error), ""), IsOk()); + EXPECT_THAT(radius, Eq(578'900)); + +} + } // namespace interface } // namespace principia diff --git a/serialization/journal.proto b/serialization/journal.proto index b10f95182a..2667bc3f68 100644 --- a/serialization/journal.proto +++ b/serialization/journal.proto @@ -151,7 +151,7 @@ message XY { } message Method { - extensions 5000 to 5999; // Last used: 5155. + extensions 5000 to 5999; // Last used: 5157. } message AdvanceTime { @@ -449,6 +449,55 @@ message ExternalGetNearestPlannedCoastDegreesOfFreedom { optional Return return = 3; } +// Sets |coefficient| to the geopotential coefficient of the given |degree| and +// |order| of the body with index |body_index|. +// |coefficient.x| is set to Cnm, |coefficient.y| is set to Snm. +message ExternalGetGeopotentialCoefficient { + extend Method { + optional ExternalGetGeopotentialCoefficient extension = 5156; + } + message In { + required fixed64 plugin = 1 [(pointer_to) = "Plugin const", + (is_subject) = true]; + required int32 body_index = 2; + required int32 degree = 3; + required int32 order = 4; + } + message Out { + required XY coefficient = 1; + } + message Return { + required Status result = 1; + } + optional In in = 1; + optional Out out = 2; + optional Return return = 3; +} + +// Sets |reference_radius| to the reference radius of the geopotential model for +// the body with index |body_index|. +// If the body does not have a geopotential model, i.e., if it is modeled as an +// isotropic sphere, the mean radius is used. +message ExternalGetGeopotentialReferenceRadius { + extend Method { + optional ExternalGetGeopotentialReferenceRadius extension = 5157; + } + message In { + required fixed64 plugin = 1 [(pointer_to) = "Plugin const", + (is_subject) = true]; + required int32 body_index = 2; + } + message Out { + required double reference_radius = 1; + } + message Return { + required Status result = 1; + } + optional In in = 1; + optional Out out = 2; + optional Return return = 3; +} + // End of external API. message FlightPlanAppend { From 80daecbd629173715e175a2617b249f02fd111f9 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Sun, 17 Feb 2019 16:46:54 +0100 Subject: [PATCH 2/4] after pleroy's review --- ksp_plugin/interface_external.cpp | 146 +++++++++++++++--------------- serialization/journal.proto | 52 +++++------ 2 files changed, 99 insertions(+), 99 deletions(-) diff --git a/ksp_plugin/interface_external.cpp b/ksp_plugin/interface_external.cpp index e8e40349d6..6cfadcae0e 100644 --- a/ksp_plugin/interface_external.cpp +++ b/ksp_plugin/interface_external.cpp @@ -73,6 +73,79 @@ Status principia__ExternalFlowFreefall( "|ExternalFlowFreefall| is not yet implemented")); } +Status principia__ExternalGeopotentialGetCoefficient( + Plugin const* const plugin, + int const body_index, + int const degree, + int const order, + XY* const coefficient) { + journal::Method m{ + {plugin, + body_index, + degree, + order}, + {coefficient}}; + if (plugin == nullptr) { + return m.Return( + MakeStatus(Error::INVALID_ARGUMENT, "|plugin| must not be null")); + } + if (!plugin->HasCelestial(body_index)) { + return m.Return(MakeStatus( + Error::NOT_FOUND, + absl::StrCat("No celestial with index ", body_index))); + } + if (order < 0 || order > degree) { + return m.Return(MakeStatus( + Error::INVALID_ARGUMENT, + absl::StrCat(u8"Expected 0 ≤ order ≤ degree; got degree = ", + degree, ", order = ", order))); + } + if (degree == 0) { + *coefficient = {1, 0}; + return m.Return(OK()); + } + auto const& body = *plugin->GetCelestial(body_index).body(); + if (!body.is_oblate()) { + *coefficient = {0, 0}; + return m.Return(OK()); + } + auto const& oblate_body = dynamic_cast const&>(body); + if (degree > oblate_body.geopotential_degree()) { + *coefficient = {0, 0}; + return m.Return(OK()); + } + *coefficient = {oblate_body.cos()[degree][order], + oblate_body.sin()[degree][order]}; + return m.Return(OK()); +} + +Status principia__ExternalGeopotentialGetReferenceRadius( + Plugin const* const plugin, + int const body_index, + double* const reference_radius) { + journal::Method m{ + {plugin, + body_index}, + {reference_radius}}; + if (plugin == nullptr) { + return m.Return( + MakeStatus(Error::INVALID_ARGUMENT, "|plugin| must not be null")); + } + if (!plugin->HasCelestial(body_index)) { + return m.Return(MakeStatus( + Error::NOT_FOUND, + absl::StrCat("No celestial with index ", body_index))); + } + auto const& body = *plugin->GetCelestial(body_index).body(); + if (!body.is_oblate()) { + *reference_radius = body.mean_radius() / Metre; + return m.Return(OK()); + } + auto const& oblate_body = dynamic_cast const&>(body); + *reference_radius = oblate_body.reference_radius() / Metre; + return m.Return(OK()); +} + Status principia__ExternalGetNearestPlannedCoastDegreesOfFreedom( Plugin const* const plugin, int const central_body_index, @@ -194,78 +267,5 @@ Status principia__ExternalGetNearestPlannedCoastDegreesOfFreedom( return m.Return(OK()); } -Status principia__ExternalGetGeopotentialCoefficient( - Plugin const* const plugin, - int const body_index, - int const degree, - int const order, - XY* const coefficient) { - journal::Method m{ - {plugin, - body_index, - degree, - order}, - {coefficient}}; - if (plugin == nullptr) { - return m.Return( - MakeStatus(Error::INVALID_ARGUMENT, "|plugin| must not be null")); - } - if (!plugin->HasCelestial(body_index)) { - return m.Return(MakeStatus( - Error::NOT_FOUND, - absl::StrCat("No celestial with index ", body_index))); - } - if (order < 0 || order > degree) { - return m.Return(MakeStatus( - Error::INVALID_ARGUMENT, - absl::StrCat(u8"Expected 0 ≤ order ≤ degree; got degree = ", - degree, ", order = ", order))); - } - if (degree == 0) { - *coefficient = {1, 0}; - return m.Return(OK()); - } - auto const& body = *plugin->GetCelestial(body_index).body(); - if (!body.is_oblate()) { - *coefficient = {0, 0}; - return m.Return(OK()); - } - auto const& oblate_body = dynamic_cast const&>(body); - if (degree > oblate_body.geopotential_degree()) { - *coefficient = {0, 0}; - return m.Return(OK()); - } - *coefficient = {oblate_body.cos()[degree][order], - oblate_body.sin()[degree][order]}; - return m.Return(OK()); -} - -Status principia__ExternalGetGeopotentialReferenceRadius( - Plugin const* const plugin, - int const body_index, - double* const reference_radius) { - journal::Method m{ - {plugin, - body_index}, - {reference_radius}}; - if (plugin == nullptr) { - return m.Return( - MakeStatus(Error::INVALID_ARGUMENT, "|plugin| must not be null")); - } - if (!plugin->HasCelestial(body_index)) { - return m.Return(MakeStatus( - Error::NOT_FOUND, - absl::StrCat("No celestial with index ", body_index))); - } - auto const& body = *plugin->GetCelestial(body_index).body(); - if (!body.is_oblate()) { - *reference_radius = body.mean_radius() / Metre; - return m.Return(OK()); - } - auto const& oblate_body = dynamic_cast const&>(body); - *reference_radius = oblate_body.reference_radius() / Metre; - return m.Return(OK()); -} - } // namespace interface } // namespace principia diff --git a/serialization/journal.proto b/serialization/journal.proto index 2667bc3f68..723929d418 100644 --- a/serialization/journal.proto +++ b/serialization/journal.proto @@ -423,23 +423,22 @@ message ExternalFlowFreefall { optional Return return = 3; } -// Returns the first point of the coast phase following the given manoeuvre -// which is locally nearest to the reference position, or the nearest endpoint -// if there is no local minimum. -message ExternalGetNearestPlannedCoastDegreesOfFreedom { +// Sets |coefficient| to the geopotential coefficient of the given |degree| and +// |order| of the body with index |body_index|. +// |coefficient.x| is set to Cnm, |coefficient.y| is set to Snm. +message ExternalGeopotentialGetCoefficient { extend Method { - optional ExternalGetNearestPlannedCoastDegreesOfFreedom extension = 5152; + optional ExternalGeopotentialGetCoefficient extension = 5156; } message In { required fixed64 plugin = 1 [(pointer_to) = "Plugin const", (is_subject) = true]; - required int32 central_body_index = 2; - required string vessel_guid = 3; - required int32 manoeuvre_index = 4; - required XYZ world_body_centred_reference_position = 5; + required int32 body_index = 2; + required int32 degree = 3; + required int32 order = 4; } message Out { - required QP world_body_centred_nearest_degrees_of_freedom = 1; + required XY coefficient = 1; } message Return { required Status result = 1; @@ -449,22 +448,21 @@ message ExternalGetNearestPlannedCoastDegreesOfFreedom { optional Return return = 3; } -// Sets |coefficient| to the geopotential coefficient of the given |degree| and -// |order| of the body with index |body_index|. -// |coefficient.x| is set to Cnm, |coefficient.y| is set to Snm. -message ExternalGetGeopotentialCoefficient { +// Sets |reference_radius| to the value in metres of the reference radius of +// the geopotential model for the body with index |body_index|. +// If the body does not have a geopotential model, i.e., if it is modeled as an +// isotropic sphere, the mean radius is used. +message ExternalGeopotentialGetReferenceRadius { extend Method { - optional ExternalGetGeopotentialCoefficient extension = 5156; + optional ExternalGeopotentialGetReferenceRadius extension = 5157; } message In { required fixed64 plugin = 1 [(pointer_to) = "Plugin const", (is_subject) = true]; required int32 body_index = 2; - required int32 degree = 3; - required int32 order = 4; } message Out { - required XY coefficient = 1; + required double reference_radius = 1; } message Return { required Status result = 1; @@ -474,21 +472,23 @@ message ExternalGetGeopotentialCoefficient { optional Return return = 3; } -// Sets |reference_radius| to the reference radius of the geopotential model for -// the body with index |body_index|. -// If the body does not have a geopotential model, i.e., if it is modeled as an -// isotropic sphere, the mean radius is used. -message ExternalGetGeopotentialReferenceRadius { +// Returns the first point of the coast phase following the given manoeuvre +// which is locally nearest to the reference position, or the nearest endpoint +// if there is no local minimum. +message ExternalGetNearestPlannedCoastDegreesOfFreedom { extend Method { - optional ExternalGetGeopotentialReferenceRadius extension = 5157; + optional ExternalGetNearestPlannedCoastDegreesOfFreedom extension = 5152; } message In { required fixed64 plugin = 1 [(pointer_to) = "Plugin const", (is_subject) = true]; - required int32 body_index = 2; + required int32 central_body_index = 2; + required string vessel_guid = 3; + required int32 manoeuvre_index = 4; + required XYZ world_body_centred_reference_position = 5; } message Out { - required double reference_radius = 1; + required QP world_body_centred_nearest_degrees_of_freedom = 1; } message Return { required Status result = 1; From 381a8b1b40b55dc4831c96d36e2f53219d212fcf Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Sun, 17 Feb 2019 17:51:12 +0100 Subject: [PATCH 3/4] rename in the test too --- ksp_plugin_test/interface_external_test.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ksp_plugin_test/interface_external_test.cpp b/ksp_plugin_test/interface_external_test.cpp index 75487fb856..0e0e77376e 100644 --- a/ksp_plugin_test/interface_external_test.cpp +++ b/ksp_plugin_test/interface_external_test.cpp @@ -118,7 +118,7 @@ TEST_F(InterfaceExternalTest, GetNearestPlannedCoastDegreesOfFreedom) { TEST_F(InterfaceExternalTest, Geopotential) { XY coefficient; double radius; - auto status = principia__ExternalGetGeopotentialCoefficient( + auto status = principia__ExternalGeopotentialGetCoefficient( &plugin_, SolarSystemFactory::Earth, /*degree=*/2, @@ -128,7 +128,7 @@ TEST_F(InterfaceExternalTest, Geopotential) { EXPECT_THAT(-coefficient.x * Sqrt(5), IsNear(1.08e-3)); EXPECT_THAT(coefficient.y, Eq(0)); - status = principia__ExternalGetGeopotentialCoefficient( + status = principia__ExternalGeopotentialGetCoefficient( &plugin_, SolarSystemFactory::Earth, /*degree=*/3, @@ -138,7 +138,7 @@ TEST_F(InterfaceExternalTest, Geopotential) { EXPECT_THAT(coefficient.x, IsNear(2.03e-6)); EXPECT_THAT(coefficient.y, IsNear(0.248e-6)); - status = principia__ExternalGetGeopotentialCoefficient( + status = principia__ExternalGeopotentialGetCoefficient( &plugin_, SolarSystemFactory::Earth, /*degree=*/1729, @@ -148,14 +148,14 @@ TEST_F(InterfaceExternalTest, Geopotential) { EXPECT_THAT(coefficient.x, Eq(0)); EXPECT_THAT(coefficient.y, Eq(0)); - status = principia__ExternalGetGeopotentialReferenceRadius( + status = principia__ExternalGeopotentialGetReferenceRadius( &plugin_, SolarSystemFactory::Earth, &radius); EXPECT_THAT(base::Status(static_cast(status.error), ""), IsOk()); EXPECT_THAT(radius, Eq(6'378'136.3)); - status = principia__ExternalGetGeopotentialCoefficient( + status = principia__ExternalGeopotentialGetCoefficient( &plugin_, SolarSystemFactory::Ariel, /*degree=*/2, @@ -165,7 +165,7 @@ TEST_F(InterfaceExternalTest, Geopotential) { EXPECT_THAT(coefficient.x, Eq(0)); EXPECT_THAT(coefficient.y, Eq(0)); - status = principia__ExternalGetGeopotentialCoefficient( + status = principia__ExternalGeopotentialGetCoefficient( &plugin_, SolarSystemFactory::Ariel, /*degree=*/0, @@ -175,7 +175,7 @@ TEST_F(InterfaceExternalTest, Geopotential) { EXPECT_THAT(coefficient.x, Eq(1)); EXPECT_THAT(coefficient.y, Eq(0)); - status = principia__ExternalGetGeopotentialReferenceRadius( + status = principia__ExternalGeopotentialGetReferenceRadius( &plugin_, SolarSystemFactory::Ariel, &radius); From 0db2459029efdd08919743bcff669a89baa88a1f Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Sun, 17 Feb 2019 20:35:53 +0100 Subject: [PATCH 4/4] after pleroy's second review --- serialization/journal.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serialization/journal.proto b/serialization/journal.proto index 723929d418..d8ed382158 100644 --- a/serialization/journal.proto +++ b/serialization/journal.proto @@ -423,8 +423,8 @@ message ExternalFlowFreefall { optional Return return = 3; } -// Sets |coefficient| to the geopotential coefficient of the given |degree| and -// |order| of the body with index |body_index|. +// Sets |coefficient| to the normalized geopotential coefficient of the given +// |degree| and |order| of the body with index |body_index|. // |coefficient.x| is set to Cnm, |coefficient.y| is set to Snm. message ExternalGeopotentialGetCoefficient { extend Method {