From a6bffb81248fa86af338106b1f5428e523822af8 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Mon, 13 Apr 2020 13:34:30 +0200 Subject: [PATCH 01/11] bring back the toggle --- ksp_plugin/interface_vessel.cpp | 19 +++++++++++++++++++ ksp_plugin/pile_up.cpp | 19 +++++++++++++++++-- ksp_plugin/pile_up.hpp | 3 +++ ksp_plugin_adapter/main_window.cs | 15 +++++++++++++++ serialization/journal.proto | 26 ++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 2 deletions(-) diff --git a/ksp_plugin/interface_vessel.cpp b/ksp_plugin/interface_vessel.cpp index 724357806a..26e261134a 100644 --- a/ksp_plugin/interface_vessel.cpp +++ b/ksp_plugin/interface_vessel.cpp @@ -195,5 +195,24 @@ XYZ __cdecl principia__VesselVelocity(Plugin const* const plugin, return m.Return(ToXYZ(plugin->VesselVelocity(vessel_guid))); } +void __cdecl principia__SetAngularMomentumConservation( + bool const conserve) { + journal::Method m({conserve}); + ksp_plugin::PileUp::conserve_angular_momentum = conserve; + return m.Return(); +} + +char const* __cdecl principia__VesselGetPileUpTrace( + Plugin const* const plugin, + char const* const vessel_guid) { + journal::Method m({plugin, vessel_guid}); + CHECK_NOTNULL(plugin); + char const* trace; + plugin->GetVessel(vessel_guid)->ForSomePart([&trace](ksp_plugin::Part& part) { + trace = part.containing_pile_up()->trace.data(); + }); + return m.Return(trace); +} + } // namespace interface } // namespace principia diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index d54837312b..17f5750517 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -442,7 +442,8 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { ApparentPileUp::origin, EquivalentRigidPileUp::origin, OrthogonalMap::Identity()), - apparent_equivalent_angular_velocity, + conserve_angular_momentum ? apparent_equivalent_angular_velocity + : ApparentPileUp::nonrotating, ApparentPileUp::unmoving); RigidMotion const actual_pile_up_equivalent_rotation( @@ -451,7 +452,8 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { EquivalentRigidPileUp::origin, OrthogonalMap::Identity()), - actual_equivalent_angular_velocity, + conserve_angular_momentum ? actual_equivalent_angular_velocity + : NonRotatingPileUp::nonrotating, NonRotatingPileUp::unmoving); RigidMotion const apparent_bubble_to_pile_up_motion = @@ -469,6 +471,17 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { } apparent_part_rigid_motion_.clear(); + std::stringstream s; + s << "Apparent: " << apparent_angular_momentum << "\n" + << "norm: " << apparent_angular_momentum.Norm() << "\n" + << "Actual: " << angular_momentum_ << "\n" + << "norm: " << angular_momentum_.Norm() << "\n" + << u8"Δ: " + << (angular_momentum_ - Identity()( + apparent_angular_momentum)) + .Norm(); + trace = s.str(); + MakeEulerSolver(Identity()(inertia_tensor), t); } @@ -595,6 +608,8 @@ PileUpFuture::PileUpFuture(not_null const pile_up, : pile_up(pile_up), future(std::move(future)) {} +bool PileUp::conserve_angular_momentum = true; + } // namespace internal_pile_up } // namespace ksp_plugin } // namespace principia diff --git a/ksp_plugin/pile_up.hpp b/ksp_plugin/pile_up.hpp index 5b962b6f1d..686e567364 100644 --- a/ksp_plugin/pile_up.hpp +++ b/ksp_plugin/pile_up.hpp @@ -94,6 +94,9 @@ class PileUp { virtual ~PileUp(); + std::string trace; + static bool conserve_angular_momentum; + // This class is moveable. PileUp(PileUp&& pile_up) = default; PileUp& operator=(PileUp&& pile_up) = default; diff --git a/ksp_plugin_adapter/main_window.cs b/ksp_plugin_adapter/main_window.cs index a08403e3df..9f946e73ad 100644 --- a/ksp_plugin_adapter/main_window.cs +++ b/ksp_plugin_adapter/main_window.cs @@ -240,7 +240,22 @@ protected override void RenderWindow(int window_id) { UnityEngine.GUI.DragWindow(); } + static bool conserve = true; + private void RenderKSPFeatures() { + conserve = UnityEngine.GUILayout.Toggle( + conserve, + "Enforce angular momentum conservation out of warp"); + Interface.SetAngularMomentumConservation(conserve); + string trace = null; + if (FlightGlobals.ActiveVessel != null && + plugin.HasVessel(FlightGlobals.ActiveVessel.id.ToString())) { + trace = plugin.VesselGetPileUpTrace( + FlightGlobals.ActiveVessel.id.ToString()); + } + UnityEngine.GUILayout.TextArea( + trace ?? "No managed active vessel", + style : Style.Multiline(UnityEngine.GUI.skin.textArea)); display_patched_conics = UnityEngine.GUILayout.Toggle( value : display_patched_conics, text : "Display patched conics (do not use for flight planning!)"); diff --git a/serialization/journal.proto b/serialization/journal.proto index f88620cebc..977bc57439 100644 --- a/serialization/journal.proto +++ b/serialization/journal.proto @@ -225,6 +225,32 @@ message Method { extensions 5000 to 5999; // Last used: 5168. } +message SetAngularMomentumConservation { + extend Method { + optional SetAngularMomentumConservation extension = 5999; + } + message In { + required bool conserve = 1; + } + optional In in = 1; +} + +message VesselGetPileUpTrace { + extend Method { + optional VesselGetPileUpTrace extension = 5998; + } + message In { + required fixed64 plugin = 1 [(pointer_to) = "Plugin const", + (is_subject) = true]; + required string vessel_guid = 2; + } + message Return { + required string result = 1; + } + optional In in = 1; + optional Return return = 3; +} + message AdvanceTime { extend Method { optional AdvanceTime extension = 5019; From d3de168f6a3b3cb0b1d6b9d1cb34b013fb6b4389 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 17 Apr 2020 03:00:01 +0200 Subject: [PATCH 02/11] This is less broken, but something is still off. --- ksp_plugin/interface_vessel.cpp | 11 ++- ksp_plugin/pile_up.cpp | 108 +++++++++++++++++++++++++----- ksp_plugin/pile_up.hpp | 5 +- ksp_plugin/plugin.cpp | 4 +- ksp_plugin_adapter/main_window.cs | 21 ++++-- serialization/journal.proto | 4 +- 6 files changed, 122 insertions(+), 31 deletions(-) diff --git a/ksp_plugin/interface_vessel.cpp b/ksp_plugin/interface_vessel.cpp index 26e261134a..ab7396f2bb 100644 --- a/ksp_plugin/interface_vessel.cpp +++ b/ksp_plugin/interface_vessel.cpp @@ -196,9 +196,14 @@ XYZ __cdecl principia__VesselVelocity(Plugin const* const plugin, } void __cdecl principia__SetAngularMomentumConservation( - bool const conserve) { - journal::Method m({conserve}); - ksp_plugin::PileUp::conserve_angular_momentum = conserve; + bool const correct_orientation, + bool const correct_angular_velocity, + bool const thresholding) { + journal::Method m( + {correct_orientation, correct_angular_velocity, thresholding}); + ksp_plugin::PileUp::correct_orientation = correct_orientation; + ksp_plugin::PileUp::correct_angular_velocity = correct_angular_velocity; + ksp_plugin::PileUp::thresholding = thresholding; return m.Return(); } diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index 17f5750517..4e721be39d 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -23,8 +23,11 @@ using geometry::AngularVelocity; using geometry::BarycentreCalculator; using geometry::Bivector; using geometry::Frame; +using geometry::Commutator; using geometry::Identity; using geometry::NonRotating; +using geometry::Normalize; +using geometry::NormalizeOrZero; using geometry::OrthogonalMap; using geometry::Position; using geometry::RigidTransformation; @@ -419,30 +422,80 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { // is unaffected by the apparent-bubble-to-pile-up correction, which is rigid // and involves no change in axes. auto const inertia_tensor = apparent_system.InertiaTensor(); - // The angular velocity of a rigid body with the inertia and angular momentum - // of the apparent parts. - auto const apparent_equivalent_angular_velocity = - apparent_angular_momentum / inertia_tensor; - // The angular velocity of a rigid body with the inertia of the apparent - // parts, and the angular momentum of the pile up. - auto const actual_equivalent_angular_velocity = - angular_momentum_ / - Identity()(inertia_tensor); + + auto const apparent_correction_axis = NormalizeOrZero(Commutator( + apparent_angular_momentum, + Identity()(angular_momentum_))); + auto const actual_correction_axis = NormalizeOrZero(Commutator( + Identity()(apparent_angular_momentum), + angular_momentum_)); // In the |EquivalentRigidPileUp| reference frame, a rigid body with the same - // inertia and angular momentum as the pile up would be immobile. We use this + // inertia and angular momentum as the pile up would be immobile, and its + // angular momentum would be directed along the y axis. We use this // intermediate frame to apply a rigid rotational correction to the motions of // the part coming from the game (the apparent motions) so as to enforce the // conservation of the angular momentum (|angular_momentum_| is // authoritative). + // TODO(egg): A note about the singularity for a correction near 0 being + // removable as a composition of essential ones. using EquivalentRigidPileUp = Frame; + auto const yapp = NormalizeOrZero(apparent_angular_momentum); + auto const yact = NormalizeOrZero(angular_momentum_); + bool const trivial_rotations = + !correct_orientation || yapp == Bivector{} || + apparent_correction_axis == Bivector{} || + yact == Bivector{} || + actual_correction_axis == Bivector{}; + Rotation r_apparent = + trivial_rotations + ? Rotation::Identity() + : Rotation( + apparent_correction_axis, + yapp, + Commutator(apparent_correction_axis, yapp)); + Rotation r_actual = + trivial_rotations + ? Rotation::Identity() + : Rotation( + actual_correction_axis, + yact, + Commutator(actual_correction_axis, yact)); + + // The angular velocity of a rigid body with the inertia and angular momentum + // of the apparent parts. + auto const apparent_equivalent_angular_velocity = + apparent_angular_momentum / inertia_tensor; + // The angular velocity of a rigid body with the inertia of the apparent + // parts, and the angular momentum of the pile up. + auto actual_equivalent_angular_velocity = + angular_momentum_ / r_actual.Inverse()(r_apparent(inertia_tensor)); + + auto const ω = std::min(apparent_equivalent_angular_velocity.Norm(), + actual_equivalent_angular_velocity.Norm()); + geometry::Quaternion const q = (r_actual.Inverse() * r_apparent).quaternion(); + auto const α = + 2 * quantities::ArcTan(q.imaginary_part().Norm(), q.real_part()); + + if (thresholding && α > ω / (50 * quantities::si::Hertz)) { + r_apparent = Rotation::Identity(); + r_actual = Rotation::Identity(); + actual_equivalent_angular_velocity = + angular_momentum_ / + Identity()(inertia_tensor); + } + + bool const correcting_orientation = + r_apparent.quaternion() != geometry::Quaternion(1) || + r_actual.quaternion() != geometry::Quaternion(1); + RigidMotion const apparent_pile_up_equivalent_rotation( RigidTransformation( ApparentPileUp::origin, EquivalentRigidPileUp::origin, - OrthogonalMap::Identity()), - conserve_angular_momentum ? apparent_equivalent_angular_velocity + r_apparent.Forget()), + correct_angular_velocity ? apparent_equivalent_angular_velocity : ApparentPileUp::nonrotating, ApparentPileUp::unmoving); RigidMotion const @@ -450,9 +503,8 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { RigidTransformation( NonRotatingPileUp::origin, EquivalentRigidPileUp::origin, - OrthogonalMap::Identity()), - conserve_angular_momentum ? actual_equivalent_angular_velocity + r_actual.Forget()), + correct_angular_velocity ? actual_equivalent_angular_velocity : NonRotatingPileUp::nonrotating, NonRotatingPileUp::unmoving); RigidMotion const @@ -476,10 +528,27 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { << "norm: " << apparent_angular_momentum.Norm() << "\n" << "Actual: " << angular_momentum_ << "\n" << "norm: " << angular_momentum_.Norm() << "\n" - << u8"Δ: " + << u8"|Lap-Lac|: " << (angular_momentum_ - Identity()( apparent_angular_momentum)) - .Norm(); + .Norm() << "\n" + << u8"|Lap|-|Lac|: " + << angular_momentum_.Norm() - apparent_angular_momentum.Norm() << "\n" + << u8"∡Lap, Lac: " + << geometry::AngleBetween(angular_momentum_, + Identity()( + apparent_angular_momentum)) / + quantities::si::Degree + << u8"°\n" + << u8"|ωap|: " + << apparent_equivalent_angular_velocity.Norm() / + (2 * π * Radian / quantities::si::Minute) + << " rpm\n" + << u8"|ωac|: " + << actual_equivalent_angular_velocity.Norm() / + (2 * π * Radian / quantities::si::Minute) + << " rpm\n" + << (correcting_orientation ? "CORRECTING ORIENTATION" : u8"—"); trace = s.str(); MakeEulerSolver(Identity()(inertia_tensor), @@ -608,7 +677,10 @@ PileUpFuture::PileUpFuture(not_null const pile_up, : pile_up(pile_up), future(std::move(future)) {} -bool PileUp::conserve_angular_momentum = true; +bool PileUp::correct_orientation = true; +bool PileUp::correct_angular_velocity = true; +bool PileUp::thresholding = true; +bool PileUp::trust_the_game_under_the_threshold = true; } // namespace internal_pile_up } // namespace ksp_plugin diff --git a/ksp_plugin/pile_up.hpp b/ksp_plugin/pile_up.hpp index 686e567364..5f623ca894 100644 --- a/ksp_plugin/pile_up.hpp +++ b/ksp_plugin/pile_up.hpp @@ -95,7 +95,10 @@ class PileUp { virtual ~PileUp(); std::string trace; - static bool conserve_angular_momentum; + static bool correct_orientation; + static bool correct_angular_velocity; + static bool thresholding; + static bool trust_the_game_under_the_threshold; // This class is moveable. PileUp(PileUp&& pile_up) = default; diff --git a/ksp_plugin/plugin.cpp b/ksp_plugin/plugin.cpp index 28e3a2d3b4..61f83bb37c 100644 --- a/ksp_plugin/plugin.cpp +++ b/ksp_plugin/plugin.cpp @@ -662,13 +662,13 @@ void Plugin::SetPartApparentRigidMotion( // |Barycentric| centred on the current main body. RigidMotion world_to_apparent_bubble{ RigidTransformation{ - main_body_degrees_of_freedom.position(), + World::origin, ApparentBubble::origin, OrthogonalMap::Identity() * renderer_->WorldToBarycentric(PlanetariumRotation())}, renderer_->BarycentricToWorld(PlanetariumRotation())( -angular_velocity_of_world_), - main_body_degrees_of_freedom.velocity()}; + World::unmoving}; not_null vessel = FindOrDie(part_id_to_vessel_, part_id); CHECK(is_loaded(vessel)); diff --git a/ksp_plugin_adapter/main_window.cs b/ksp_plugin_adapter/main_window.cs index 9f946e73ad..1be7ab52f5 100644 --- a/ksp_plugin_adapter/main_window.cs +++ b/ksp_plugin_adapter/main_window.cs @@ -239,14 +239,23 @@ protected override void RenderWindow(int window_id) { } UnityEngine.GUI.DragWindow(); } - - static bool conserve = true; + + static bool correct_orientation = true; + static bool correct_angular_velocity = true; + static bool thresholding = true; private void RenderKSPFeatures() { - conserve = UnityEngine.GUILayout.Toggle( - conserve, - "Enforce angular momentum conservation out of warp"); - Interface.SetAngularMomentumConservation(conserve); + correct_orientation = UnityEngine.GUILayout.Toggle( + correct_orientation, + "Correct orientation"); + correct_angular_velocity = UnityEngine.GUILayout.Toggle( + correct_angular_velocity, + "Correct angular velocity"); + thresholding = UnityEngine.GUILayout.Toggle( + thresholding, + "Only correct orientation above ω threshold"); + Interface.SetAngularMomentumConservation( + correct_orientation, correct_angular_velocity, thresholding); string trace = null; if (FlightGlobals.ActiveVessel != null && plugin.HasVessel(FlightGlobals.ActiveVessel.id.ToString())) { diff --git a/serialization/journal.proto b/serialization/journal.proto index 977bc57439..dbebd61d71 100644 --- a/serialization/journal.proto +++ b/serialization/journal.proto @@ -230,7 +230,9 @@ message SetAngularMomentumConservation { optional SetAngularMomentumConservation extension = 5999; } message In { - required bool conserve = 1; + required bool correct_orientation = 1; + required bool correct_angular_velocity = 2; + required bool thresholding = 3; } optional In in = 1; } From 7b5022072ee074e467785c1b251fd77dd19d5b06 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 17 Apr 2020 03:00:39 +0200 Subject: [PATCH 03/11] todo --- ksp_plugin/pile_up.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index 4e721be39d..daf21832db 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -477,6 +477,7 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { auto const α = 2 * quantities::ArcTan(q.imaginary_part().Norm(), q.real_part()); + // TODO(egg): Use the real Δt. if (thresholding && α > ω / (50 * quantities::si::Hertz)) { r_apparent = Rotation::Identity(); r_actual = Rotation::Identity(); From 913a81a1363963fa0e1c2efc5e6623ccb3813720 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 17 Apr 2020 21:28:38 +0200 Subject: [PATCH 04/11] comments --- ksp_plugin/pile_up.cpp | 155 +++++++++++++++++++++++++++++------------ 1 file changed, 109 insertions(+), 46 deletions(-) diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index daf21832db..4ccf8af692 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -30,13 +30,17 @@ using geometry::Normalize; using geometry::NormalizeOrZero; using geometry::OrthogonalMap; using geometry::Position; +using geometry::Quaternion; using geometry::RigidTransformation; using geometry::Rotation; using geometry::Velocity; using geometry::Wedge; using physics::DegreesOfFreedom; using physics::RigidMotion; +using quantities::Angle; +using quantities::AngularFrequency; using quantities::AngularMomentum; +using quantities::Time; using quantities::si::Radian; using ::std::placeholders::_1; using ::std::placeholders::_2; @@ -423,6 +427,12 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { // and involves no change in axes. auto const inertia_tensor = apparent_system.InertiaTensor(); + // This is the axis around which we may perform an orientation correction. + // Identifying the uncorrected (apparent) and corrected coordinates, it is + // orthogonal to both. + // Note that the computations are identical in coordinates: + // |apparent_correction_axis.coordinates() == + // actual_correction_axis.coordinates()|. auto const apparent_correction_axis = NormalizeOrZero(Commutator( apparent_angular_momentum, Identity()(angular_momentum_))); @@ -430,37 +440,83 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { Identity()(apparent_angular_momentum), angular_momentum_)); + // We apply a rigid rotational correction to the motions of the parts coming + // from the game (the apparent motions) so as to enforce the conservation of + // the angular momentum (|angular_momentum_| is authoritative). + // Mapping L_apparent to L_actual by a rigid motion leaves can be done in many + // ways. + // We prefer doing some of that correction by a change of attitude, rather + // than a solely by a change in angular velocity, since, under isotropic + // conditions, a change in attitude does not alter the physical system (an in + // particular it does not mess with symplectic integration). We thus try to + // map L̂_apparent to L̂_actual, by rotation around the correction axis defined + // above, which is perpendicular to both, and imparting the appropriate + // angular velocity correction to correct the norm of L. + // This amounts to trusting the direction of the angular momentum with respect + // to the vessel as given to us by the game. + // However, mapping L̂_apparent to L̂_actual is essentially singular when either + // is 0. We remedy to that by only performing an attitude correction if its + // angle would be less than ω Δt, where ω is the smaller of the two equivalent + // angular frequencies |L_actual / I| and |L_apparent / I|. + // As a result, as either L tends towards 0, so does the greatest attitude + // correction that we allow. + // If the attitude correction would exceed this threshold, we leave the + // attitude unchanged, and the correction in angular momentum is effected + // solely by a change in angular velocity. + + // The correction is computed via an intermediate frame. // In the |EquivalentRigidPileUp| reference frame, a rigid body with the same - // inertia and angular momentum as the pile up would be immobile, and its - // angular momentum would be directed along the y axis. We use this - // intermediate frame to apply a rigid rotational correction to the motions of - // the part coming from the game (the apparent motions) so as to enforce the - // conservation of the angular momentum (|angular_momentum_| is - // authoritative). - // TODO(egg): A note about the singularity for a correction near 0 being - // removable as a composition of essential ones. + // inertia and angular momentum as the pile up would be immobile. + // If no attitude correction is performed, the axes of + // |EquivalentRigidPileUp|, |ApparentPileUp|, and |NonRotatingPileUp| are + // identical. + // If an attitude correction is performed, the y axis of + // |EquivalentRigidPileUp| is the direction of the angular momentum, and the x + // axis is the correction axis. + // NOTE(egg): while the correction axis is essentially singular at + // L_apparent = L_actual, the correction itself is only removably singular (as + // the angle goes to 0). Since the computation ensured that + // |apparent_correction_axis.coordinates() == + // actual_correction_axis.coordinates()| exactly, we need not worry about the + // intermediate essential singularity, and only need to remove it. using EquivalentRigidPileUp = Frame; - auto const yapp = NormalizeOrZero(apparent_angular_momentum); - auto const yact = NormalizeOrZero(angular_momentum_); - bool const trivial_rotations = - !correct_orientation || yapp == Bivector{} || + + auto const L̂_apparent = NormalizeOrZero(apparent_angular_momentum); + auto const L̂_actual = NormalizeOrZero(angular_momentum_); + // NOTE(egg): + // — The two sides of this disjunction are always equal; + // — technically this will also be true on the essential singularity. + bool const on_removable_singularity = apparent_correction_axis == Bivector{} || - yact == Bivector{} || actual_correction_axis == Bivector{}; - Rotation r_apparent = - trivial_rotations - ? Rotation::Identity() - : Rotation( - apparent_correction_axis, - yapp, - Commutator(apparent_correction_axis, yapp)); - Rotation r_actual = - trivial_rotations - ? Rotation::Identity() - : Rotation( - actual_correction_axis, - yact, - Commutator(actual_correction_axis, yact)); + bool const on_essential_singularity = + L̂_apparent == Bivector{} || + L̂_actual == Bivector{}; + + bool const trivial_rotations = !correct_orientation || + on_removable_singularity || + on_essential_singularity; + + Rotation + apparent_pile_up_equivalent_rotation = + trivial_rotations + ? Rotation::Identity() + : Rotation( + apparent_correction_axis, + L̂_apparent, + Commutator(apparent_correction_axis, L̂_apparent)); + Rotation + actual_pile_up_equivalent_rotation = + trivial_rotations + ? Rotation::Identity() + : Rotation( + actual_correction_axis, + L̂_actual, + Commutator(actual_correction_axis, L̂_actual)); + Rotation const + tentative_attitude_correction = + actual_pile_up_equivalent_rotation.Inverse() * + apparent_pile_up_equivalent_rotation; // The angular velocity of a rigid body with the inertia and angular momentum // of the apparent parts. @@ -469,49 +525,55 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { // The angular velocity of a rigid body with the inertia of the apparent // parts, and the angular momentum of the pile up. auto actual_equivalent_angular_velocity = - angular_momentum_ / r_actual.Inverse()(r_apparent(inertia_tensor)); - - auto const ω = std::min(apparent_equivalent_angular_velocity.Norm(), - actual_equivalent_angular_velocity.Norm()); - geometry::Quaternion const q = (r_actual.Inverse() * r_apparent).quaternion(); - auto const α = + angular_momentum_ / tentative_attitude_correction(inertia_tensor); + + // So far we have only dealt with the essential singularity by removing it. + // We now need to deal with its neighbourhood, by ensuring that the tentative + // correction is not too big compared to the angular velocities involved. + Quaternion const q = tentative_attitude_correction.quaternion(); + // α is the angle of the tentative attitude correction. + Angle const α = 2 * quantities::ArcTan(q.imaginary_part().Norm(), q.real_part()); - - // TODO(egg): Use the real Δt. - if (thresholding && α > ω / (50 * quantities::si::Hertz)) { - r_apparent = Rotation::Identity(); - r_actual = Rotation::Identity(); + AngularFrequency const ω = std::min(apparent_equivalent_angular_velocity.Norm(), + actual_equivalent_angular_velocity.Norm()); + Time const Δt = t - psychohistory_->back().time; + if (thresholding && α > ω * Δt) { + // The attitude correction is too large. Preserve attitude. + apparent_pile_up_equivalent_rotation = + Rotation::Identity(); + actual_pile_up_equivalent_rotation = + Rotation::Identity(); actual_equivalent_angular_velocity = angular_momentum_ / Identity()(inertia_tensor); } bool const correcting_orientation = - r_apparent.quaternion() != geometry::Quaternion(1) || - r_actual.quaternion() != geometry::Quaternion(1); + apparent_pile_up_equivalent_rotation.quaternion() != Quaternion(1) || + actual_pile_up_equivalent_rotation.quaternion() != Quaternion(1); RigidMotion const - apparent_pile_up_equivalent_rotation( + apparent_pile_up_equivalent_motion( RigidTransformation( ApparentPileUp::origin, EquivalentRigidPileUp::origin, - r_apparent.Forget()), + apparent_pile_up_equivalent_rotation.Forget()), correct_angular_velocity ? apparent_equivalent_angular_velocity : ApparentPileUp::nonrotating, ApparentPileUp::unmoving); RigidMotion const - actual_pile_up_equivalent_rotation( + actual_pile_up_equivalent_motion( RigidTransformation( NonRotatingPileUp::origin, EquivalentRigidPileUp::origin, - r_actual.Forget()), + actual_pile_up_equivalent_rotation.Forget()), correct_angular_velocity ? actual_equivalent_angular_velocity : NonRotatingPileUp::nonrotating, NonRotatingPileUp::unmoving); RigidMotion const apparent_bubble_to_pile_up_motion = - actual_pile_up_equivalent_rotation.Inverse() * - apparent_pile_up_equivalent_rotation * + actual_pile_up_equivalent_motion.Inverse() * + apparent_pile_up_equivalent_motion * apparent_system.LinearMotion().Inverse(); // Now update the motions of the parts in the pile-up frame. @@ -541,6 +603,7 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { apparent_angular_momentum)) / quantities::si::Degree << u8"°\n" + << u8"α: " << α / quantities::si::Degree << u8"°\n" << u8"|ωap|: " << apparent_equivalent_angular_velocity.Norm() / (2 * π * Radian / quantities::si::Minute) From 7f2fa19e82d22ff4d76e28ee1fe49097a209e1d5 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 17 Apr 2020 21:29:23 +0200 Subject: [PATCH 05/11] wording --- ksp_plugin_adapter/main_window.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ksp_plugin_adapter/main_window.cs b/ksp_plugin_adapter/main_window.cs index 1be7ab52f5..a55fa9b7ce 100644 --- a/ksp_plugin_adapter/main_window.cs +++ b/ksp_plugin_adapter/main_window.cs @@ -253,7 +253,7 @@ private void RenderKSPFeatures() { "Correct angular velocity"); thresholding = UnityEngine.GUILayout.Toggle( thresholding, - "Only correct orientation above ω threshold"); + "Only correct orientation slower than ω"); Interface.SetAngularMomentumConservation( correct_orientation, correct_angular_velocity, thresholding); string trace = null; From ede5efd995f7dae62cef62ec52dc023ab122abfb Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 17 Apr 2020 21:44:27 +0200 Subject: [PATCH 06/11] comment on ApparentBubble --- ksp_plugin/plugin.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ksp_plugin/plugin.cpp b/ksp_plugin/plugin.cpp index 61f83bb37c..8a2b662eac 100644 --- a/ksp_plugin/plugin.cpp +++ b/ksp_plugin/plugin.cpp @@ -658,8 +658,15 @@ void Plugin::SetPartApparentRigidMotion( PartId const part_id, RigidMotion const& rigid_motion, DegreesOfFreedom const& main_body_degrees_of_freedom) { - // Define |ApparentBubble| as the reference frame with the axes of - // |Barycentric| centred on the current main body. + // As a reference frame, |ApparentBubble| differs from |World| only by having + // the same axes as |Barycentric| and being nonrotating. However, there is + // another semantic distinction: |Apparent| coordinates are uncorrected data + // from the game, given immediately after its physics step; before + // using them, we must correct them in accordance with the data computed by + // the pile up. This correction overrides the origin of position and + // velocity, so we need not worry about the current definition of + // |{World::origin, World::unmoving}| as we do when getting the actual degrees + // of freedom (via |Plugin::BarycentricToWorld|). RigidMotion world_to_apparent_bubble{ RigidTransformation{ World::origin, From f42abdbb7ef63394377b4796513d56e6a9e53353 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 17 Apr 2020 21:49:05 +0200 Subject: [PATCH 07/11] trusting trust --- ksp_plugin/pile_up.cpp | 1 - ksp_plugin/pile_up.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index 4ccf8af692..43bc23fd72 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -744,7 +744,6 @@ PileUpFuture::PileUpFuture(not_null const pile_up, bool PileUp::correct_orientation = true; bool PileUp::correct_angular_velocity = true; bool PileUp::thresholding = true; -bool PileUp::trust_the_game_under_the_threshold = true; } // namespace internal_pile_up } // namespace ksp_plugin diff --git a/ksp_plugin/pile_up.hpp b/ksp_plugin/pile_up.hpp index 5f623ca894..33f1639977 100644 --- a/ksp_plugin/pile_up.hpp +++ b/ksp_plugin/pile_up.hpp @@ -98,7 +98,6 @@ class PileUp { static bool correct_orientation; static bool correct_angular_velocity; static bool thresholding; - static bool trust_the_game_under_the_threshold; // This class is moveable. PileUp(PileUp&& pile_up) = default; From 9e19fe0b8435ca4525b40e8f5c96cf62f4e4e541 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 17 Apr 2020 21:53:15 +0200 Subject: [PATCH 08/11] copy editing --- ksp_plugin/pile_up.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index 43bc23fd72..5d216d3b43 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -443,8 +443,7 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { // We apply a rigid rotational correction to the motions of the parts coming // from the game (the apparent motions) so as to enforce the conservation of // the angular momentum (|angular_momentum_| is authoritative). - // Mapping L_apparent to L_actual by a rigid motion leaves can be done in many - // ways. + // Mapping L_apparent to L_actual by a rigid motion can be done in many ways. // We prefer doing some of that correction by a change of attitude, rather // than a solely by a change in angular velocity, since, under isotropic // conditions, a change in attitude does not alter the physical system (an in From 33d33101a7b0744d4df62d97a9748cff0ea30e45 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 17 Apr 2020 21:54:35 +0200 Subject: [PATCH 09/11] more typos --- ksp_plugin/pile_up.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index 5d216d3b43..dacebefbfa 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -446,10 +446,10 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { // Mapping L_apparent to L_actual by a rigid motion can be done in many ways. // We prefer doing some of that correction by a change of attitude, rather // than a solely by a change in angular velocity, since, under isotropic - // conditions, a change in attitude does not alter the physical system (an in - // particular it does not mess with symplectic integration). We thus try to + // conditions, a change in attitude does not alter the physical system (and in + // particular it does not mess with symplectic integration). We thus try to // map L̂_apparent to L̂_actual, by rotation around the correction axis defined - // above, which is perpendicular to both, and imparting the appropriate + // above, which is perpendicular to both, and to impart the appropriate // angular velocity correction to correct the norm of L. // This amounts to trusting the direction of the angular momentum with respect // to the vessel as given to us by the game. From 69af6d239cdfe094a3c72324bd8eaecb3261e6a3 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Sat, 18 Apr 2020 13:57:11 +0200 Subject: [PATCH 10/11] =?UTF-8?q?after=20pleroy=E2=80=99s=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ksp_plugin/interface_part.cpp | 9 +++------ ksp_plugin/pile_up.cpp | 19 ++++++++++--------- ksp_plugin/plugin.cpp | 13 ++++++------- ksp_plugin/plugin.hpp | 3 +-- ksp_plugin_adapter/ksp_plugin_adapter.cs | 3 +-- ksp_plugin_adapter/main_window.cs | 11 +++++++---- serialization/journal.proto | 1 - 7 files changed, 28 insertions(+), 31 deletions(-) diff --git a/ksp_plugin/interface_part.cpp b/ksp_plugin/interface_part.cpp index 7c2f0b9c32..8c7c2a4043 100644 --- a/ksp_plugin/interface_part.cpp +++ b/ksp_plugin/interface_part.cpp @@ -112,20 +112,17 @@ void __cdecl principia__PartSetApparentRigidMotion( PartId const part_id, QP const degrees_of_freedom, WXYZ const rotation, - XYZ const angular_velocity, - QP const main_body_degrees_of_freedom) { + XYZ const angular_velocity) { journal::Method m( {plugin, part_id, degrees_of_freedom, rotation, - angular_velocity, - main_body_degrees_of_freedom}); + angular_velocity}); CHECK_NOTNULL(plugin); plugin->SetPartApparentRigidMotion( part_id, - MakePartRigidMotion(degrees_of_freedom, rotation, angular_velocity), - FromQP>(main_body_degrees_of_freedom)); + MakePartRigidMotion(degrees_of_freedom, rotation, angular_velocity)); return m.Return(); } diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index dacebefbfa..d589f4348a 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -22,8 +22,8 @@ using base::make_not_null_unique; using geometry::AngularVelocity; using geometry::BarycentreCalculator; using geometry::Bivector; -using geometry::Frame; using geometry::Commutator; +using geometry::Frame; using geometry::Identity; using geometry::NonRotating; using geometry::Normalize; @@ -445,7 +445,7 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { // the angular momentum (|angular_momentum_| is authoritative). // Mapping L_apparent to L_actual by a rigid motion can be done in many ways. // We prefer doing some of that correction by a change of attitude, rather - // than a solely by a change in angular velocity, since, under isotropic + // than solely by a change in angular velocity, since, under isotropic // conditions, a change in attitude does not alter the physical system (and in // particular it does not mess with symplectic integration). We thus try to // map L̂_apparent to L̂_actual, by rotation around the correction axis defined @@ -457,7 +457,7 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { // is 0. We remedy to that by only performing an attitude correction if its // angle would be less than ω Δt, where ω is the smaller of the two equivalent // angular frequencies |L_actual / I| and |L_apparent / I|. - // As a result, as either L tends towards 0, so does the greatest attitude + // As a result, as either L tends towards 0, so does the largest attitude // correction that we allow. // If the attitude correction would exceed this threshold, we leave the // attitude unchanged, and the correction in angular momentum is effected @@ -533,8 +533,9 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { // α is the angle of the tentative attitude correction. Angle const α = 2 * quantities::ArcTan(q.imaginary_part().Norm(), q.real_part()); - AngularFrequency const ω = std::min(apparent_equivalent_angular_velocity.Norm(), - actual_equivalent_angular_velocity.Norm()); + AngularFrequency const ω = + std::min(apparent_equivalent_angular_velocity.Norm(), + actual_equivalent_angular_velocity.Norm()); Time const Δt = t - psychohistory_->back().time; if (thresholding && α > ω * Δt) { // The attitude correction is too large. Preserve attitude. @@ -558,7 +559,7 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { EquivalentRigidPileUp::origin, apparent_pile_up_equivalent_rotation.Forget()), correct_angular_velocity ? apparent_equivalent_angular_velocity - : ApparentPileUp::nonrotating, + : ApparentPileUp::nonrotating, ApparentPileUp::unmoving); RigidMotion const actual_pile_up_equivalent_motion( @@ -567,7 +568,7 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { EquivalentRigidPileUp::origin, actual_pile_up_equivalent_rotation.Forget()), correct_angular_velocity ? actual_equivalent_angular_velocity - : NonRotatingPileUp::nonrotating, + : NonRotatingPileUp::nonrotating, NonRotatingPileUp::unmoving); RigidMotion const apparent_bubble_to_pile_up_motion = @@ -590,11 +591,11 @@ void PileUp::DeformPileUpIfNeeded(Instant const& t) { << "norm: " << apparent_angular_momentum.Norm() << "\n" << "Actual: " << angular_momentum_ << "\n" << "norm: " << angular_momentum_.Norm() << "\n" - << u8"|Lap-Lac|: " + << "|Lap-Lac|: " << (angular_momentum_ - Identity()( apparent_angular_momentum)) .Norm() << "\n" - << u8"|Lap|-|Lac|: " + << "|Lap|-|Lac|: " << angular_momentum_.Norm() - apparent_angular_momentum.Norm() << "\n" << u8"∡Lap, Lac: " << geometry::AngleBetween(angular_momentum_, diff --git a/ksp_plugin/plugin.cpp b/ksp_plugin/plugin.cpp index 8a2b662eac..26cf422645 100644 --- a/ksp_plugin/plugin.cpp +++ b/ksp_plugin/plugin.cpp @@ -656,15 +656,14 @@ void Plugin::FreeVesselsAndPartsAndCollectPileUps(Time const& Δt) { void Plugin::SetPartApparentRigidMotion( PartId const part_id, - RigidMotion const& rigid_motion, - DegreesOfFreedom const& main_body_degrees_of_freedom) { + RigidMotion const& rigid_motion) { // As a reference frame, |ApparentBubble| differs from |World| only by having // the same axes as |Barycentric| and being nonrotating. However, there is - // another semantic distinction: |Apparent| coordinates are uncorrected data - // from the game, given immediately after its physics step; before - // using them, we must correct them in accordance with the data computed by - // the pile up. This correction overrides the origin of position and - // velocity, so we need not worry about the current definition of + // another semantic distinction: |Apparent...| coordinates are uncorrected + // data from the game, given immediately after its physics step; before using + // them, we must correct them in accordance with the data computed by the pile + // up. This correction overrides the origin of position and velocity, so we + // need not worry about the current definition of // |{World::origin, World::unmoving}| as we do when getting the actual degrees // of freedom (via |Plugin::BarycentricToWorld|). RigidMotion world_to_apparent_bubble{ diff --git a/ksp_plugin/plugin.hpp b/ksp_plugin/plugin.hpp index 298cc95e6e..b2a690d4d9 100644 --- a/ksp_plugin/plugin.hpp +++ b/ksp_plugin/plugin.hpp @@ -254,8 +254,7 @@ class Plugin { // part. This part must be in a loaded vessel. virtual void SetPartApparentRigidMotion( PartId part_id, - RigidMotion const& rigid_motion, - DegreesOfFreedom const& main_body_degrees_of_freedom); + RigidMotion const& rigid_motion); // Returns the motion of the given part in |World|, assuming that // the origin of |World| is fixed at the centre of mass of the diff --git a/ksp_plugin_adapter/ksp_plugin_adapter.cs b/ksp_plugin_adapter/ksp_plugin_adapter.cs index 564fdcf191..b3e8ae81d4 100644 --- a/ksp_plugin_adapter/ksp_plugin_adapter.cs +++ b/ksp_plugin_adapter/ksp_plugin_adapter.cs @@ -1238,8 +1238,7 @@ private System.Collections.IEnumerator WaitedForFixedUpdate() { new QP{q = (XYZ)(Vector3d)part.rb.position, p = (XYZ)(Vector3d)part.rb.velocity}, (WXYZ)(UnityEngine.QuaternionD)part.rb.rotation, - (XYZ)(Vector3d)part.rb.angularVelocity, - main_body_degrees_of_freedom); + (XYZ)(Vector3d)part.rb.angularVelocity); } } } diff --git a/ksp_plugin_adapter/main_window.cs b/ksp_plugin_adapter/main_window.cs index a55fa9b7ce..b299dd2716 100644 --- a/ksp_plugin_adapter/main_window.cs +++ b/ksp_plugin_adapter/main_window.cs @@ -239,10 +239,6 @@ protected override void RenderWindow(int window_id) { } UnityEngine.GUI.DragWindow(); } - - static bool correct_orientation = true; - static bool correct_angular_velocity = true; - static bool thresholding = true; private void RenderKSPFeatures() { correct_orientation = UnityEngine.GUILayout.Toggle( @@ -519,6 +515,13 @@ private void RenderToggleableSection(string name, value = 7 * 24 * 60 * 60 }; + // These flags exist to facilitate investigation of #2519. + // They must not be serialized: their non-default values can lead to absurd + // behaviour. + private static bool correct_orientation = true; + private static bool correct_angular_velocity = true; + private static bool thresholding = true; + private static readonly double[] prediction_length_tolerances_ = {1e-3, 1e-2, 1e0, 1e1, 1e2, 1e3, 1e4}; private static readonly long[] prediction_steps_ = diff --git a/serialization/journal.proto b/serialization/journal.proto index dbebd61d71..e290d9bd1c 100644 --- a/serialization/journal.proto +++ b/serialization/journal.proto @@ -1635,7 +1635,6 @@ message PartSetApparentRigidMotion { required QP degrees_of_freedom = 3; required WXYZ rotation = 5; required XYZ angular_velocity = 6; - required QP main_body_degrees_of_freedom = 4; } optional In in = 1; } From f47dfb1cc6c36e65c8452f62ea1d53c79e38bb3b Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Sat, 18 Apr 2020 15:21:01 +0200 Subject: [PATCH 11/11] lint --- ksp_plugin/pile_up.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ksp_plugin/pile_up.cpp b/ksp_plugin/pile_up.cpp index d589f4348a..436238ae8e 100644 --- a/ksp_plugin/pile_up.cpp +++ b/ksp_plugin/pile_up.cpp @@ -1,6 +1,7 @@  #include "ksp_plugin/pile_up.hpp" +#include #include #include #include