Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3a6d4df53cbe
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 37005d9700d7
Choose a head ref
  • 12 commits
  • 5 files changed
  • 1 contributor

Commits on Oct 30, 2021

  1. Start changing Vessel.

    pleroy committed Oct 30, 2021
    Copy the full SHA
    b976384 View commit details
  2. Prognostication.

    pleroy committed Oct 30, 2021
    Copy the full SHA
    4591cdc View commit details

Commits on Oct 31, 2021

  1. Copy the full SHA
    f71d37d View commit details

Commits on Nov 2, 2021

  1. Copy the full SHA
    c10a0ce View commit details
  2. Vessel compiles.

    pleroy committed Nov 2, 2021
    Copy the full SHA
    0cc8e3b View commit details
  3. The test compiles.

    pleroy committed Nov 2, 2021
    Copy the full SHA
    6fabec4 View commit details
  4. Fix a bug.

    pleroy committed Nov 2, 2021
    Copy the full SHA
    f71eeb1 View commit details
  5. Tests passing.

    pleroy committed Nov 2, 2021
    Copy the full SHA
    c293386 View commit details
  6. Copy the full SHA
    b22c9b2 View commit details

Commits on Nov 3, 2021

  1. Comments.

    pleroy committed Nov 3, 2021
    Copy the full SHA
    0a12427 View commit details
  2. More comments.

    pleroy committed Nov 3, 2021
    Copy the full SHA
    ab39a5a View commit details
  3. Merge pull request #3189 from pleroy/Vessel

    Convert Vessel and its test to DiscreteTraject0ry
    pleroy authored Nov 3, 2021
    Copy the full SHA
    37005d9 View commit details
Showing with 206 additions and 151 deletions.
  1. +87 −70 ksp_plugin/vessel.cpp
  2. +29 −21 ksp_plugin/vessel.hpp
  3. +72 −60 ksp_plugin_test/vessel_test.cpp
  4. +7 −0 testing_utilities/discrete_trajectory_factories.hpp
  5. +11 −0 testing_utilities/discrete_trajectory_factories_body.hpp
157 changes: 87 additions & 70 deletions ksp_plugin/vessel.cpp
Original file line number Diff line number Diff line change
@@ -68,7 +68,9 @@ Vessel::Vessel(GUID guid,
return FlowPrognostication(parameters);
},
20ms), // 50 Hz.
history_(make_not_null_unique<DiscreteTrajectory<Barycentric>>()) {
history_(trajectory_.segments().begin()),
psychohistory_(trajectory_.segments().end()),
prediction_(trajectory_.segments().end()) {
// Can't create the |psychohistory_| and |prediction_| here because |history_|
// is empty;
}
@@ -159,10 +161,10 @@ void Vessel::ClearAllIntrinsicForcesAndTorques() {

void Vessel::PrepareHistory(
Instant const& t,
DiscreteTrajectory<Barycentric>::DownsamplingParameters const&
DiscreteTrajectorySegment<Barycentric>::DownsamplingParameters const&
downsampling_parameters) {
CHECK(!parts_.empty());
if (history_->Empty()) {
if (trajectory_.empty()) {
LOG(INFO) << "Preparing history of vessel " << ShortDebugString()
<< " at " << t;
BarycentreCalculator<DegreesOfFreedom<Barycentric>, Mass> calculator;
@@ -171,11 +173,11 @@ void Vessel::PrepareHistory(
part.rigid_motion()({RigidPart::origin, RigidPart::unmoving}),
part.mass());
});
CHECK(psychohistory_ == nullptr);
CHECK(psychohistory_ == trajectory_.segments().end());
history_->SetDownsampling(downsampling_parameters);
history_->Append(t, calculator.Get());
psychohistory_ = history_->NewForkAtLast();
prediction_ = psychohistory_->NewForkAtLast();
trajectory_.Append(t, calculator.Get());
psychohistory_ = trajectory_.NewSegment();
prediction_ = trajectory_.NewSegment();
}
}

@@ -198,12 +200,16 @@ void Vessel::ForAllParts(std::function<void(Part&)> action) const {
}
}

DiscreteTrajectory<Barycentric> const& Vessel::psychohistory() const {
return *psychohistory_;
DiscreteTrajectorySegmentIterator<Barycentric> Vessel::history() const {
return history_;
}

DiscreteTrajectory<Barycentric> const& Vessel::prediction() const {
return *prediction_;
DiscreteTrajectorySegmentIterator<Barycentric> Vessel::psychohistory() const {
return psychohistory_;
}

DiscreteTrajectorySegmentIterator<Barycentric> Vessel::prediction() const {
return prediction_;
}

void Vessel::set_prediction_adaptive_step_parameters(
@@ -229,18 +235,18 @@ bool Vessel::has_flight_plan() const {
void Vessel::AdvanceTime() {
// Squirrel away the prediction so that we can reattach it if we don't have a
// prognostication.
auto prediction = prediction_->DetachFork();
prediction_ = nullptr;
auto prediction = trajectory_.DetachSegments(prediction_);
prediction_ = trajectory_.segments().end();

// Read the wall of text below and realize that this can happen for the
// history as well as the psychohistory, if the history of the part was
// obtained using an adaptive step integrator, which is the case during a
// burn. See #2931.
history_->DeleteFork(psychohistory_);
trajectory_.DeleteSegments(psychohistory_);
AppendToVesselTrajectory(&Part::history_begin,
&Part::history_end,
*history_);
psychohistory_ = history_->NewForkAtLast();
psychohistory_ = trajectory_.NewSegment();

// The reason why we may want to skip the start of the psychohistory is
// subtle. Say that we have a vessel A with points at t₀, t₀ + 10 s,
@@ -331,9 +337,9 @@ absl::Status Vessel::RebaseFlightPlan(Mass const& initial_mass) {
}

void Vessel::RefreshPrediction() {
// The |prognostication| is a root trajectory which is computed asynchronously
// and may be used as a prediction;
std::unique_ptr<DiscreteTrajectory<Barycentric>> prognostication;
// The |prognostication| is a trajectory which is computed asynchronously and
// may be used as a prediction;
std::optional<DiscreteTraject0ry<Barycentric>> prognostication;

// Note that we know that |RefreshPrediction| is called on the main thread,
// therefore the ephemeris currently covers the last time of the
@@ -351,16 +357,16 @@ void Vessel::RefreshPrediction() {
} else {
prognosticator_.Put(std::move(prognosticator_parameters));
prognosticator_.Start();
prognostication = prognosticator_.Get().value_or(nullptr);
prognostication = prognosticator_.Get();
}
if (prognostication != nullptr) {
AttachPrediction(std::move(prognostication));
if (prognostication.has_value()) {
AttachPrediction(std::move(prognostication).value());
}
}

void Vessel::RefreshPrediction(Instant const& time) {
RefreshPrediction();
prediction_->ForgetAfter(time);
trajectory_.ForgetAfter(trajectory_.upper_bound(time));
}

void Vessel::StopPrognosticator() {
@@ -387,14 +393,13 @@ void Vessel::WriteToMessage(not_null<serialization::Vessel*> const message,
message->add_kept_parts(part_id);
}
// Starting with Gateaux we don't save the prediction, see #2685. Instead we
// save an empty prediction that we re-read as a prediction. This is a bit
// hacky, but hopefully we can remove this hack once #2400 is solved.
DiscreteTrajectory<Barycentric>* empty_prediction =
psychohistory_->NewForkAtLast();
history_->WriteToMessage(message->mutable_history(),
/*forks=*/{psychohistory_, empty_prediction},
/*exact=*/{});
psychohistory_->DeleteFork(empty_prediction);
// just save its first point and re-read as if it was the whole prediction.
trajectory_.WriteToMessage(
message->mutable_history(),
/*begin=*/trajectory_.begin(),
/*end=*/std::next(prediction_->begin()),
/*tracked=*/{history_, psychohistory_, prediction_},
/*exact=*/{});
if (flight_plan_ != nullptr) {
flight_plan_->WriteToMessage(message->mutable_flight_plan());
}
@@ -408,10 +413,13 @@ not_null<std::unique_ptr<Vessel>> Vessel::ReadFromMessage(
bool const is_pre_cesàro = message.has_psychohistory_is_authoritative();
bool const is_pre_chasles = message.has_prediction();
bool const is_pre_陈景润 = !message.history().has_downsampling();
LOG_IF(WARNING, is_pre_陈景润)
<< "Reading pre-" << (is_pre_cesàro ? u8"Cesàro"
: is_pre_chasles ? "Chasles"
: u8"陈景润") << " Vessel";
bool const is_pre_ζήνων = message.history().segment_size() == 0;
LOG_IF(WARNING, is_pre_ζήνων)
<< "Reading pre-"
<< (is_pre_cesàro ? u8"Cesàro"
: is_pre_chasles ? "Chasles"
: is_pre_陈景润 ? u8"陈景润"
: u8"Ζήνων") << " Vessel";

// NOTE(egg): for now we do not read the |MasslessBody| as it can contain no
// information.
@@ -439,37 +447,46 @@ not_null<std::unique_ptr<Vessel>> Vessel::ReadFromMessage(

if (is_pre_cesàro) {
auto const psychohistory =
DiscreteTrajectory<Barycentric>::ReadFromMessage(message.history(),
DiscreteTraject0ry<Barycentric>::ReadFromMessage(message.history(),
/*forks=*/{});
// The |history_| has been created by the constructor above. Reconstruct
// it from the |psychohistory|.
for (auto it = psychohistory->begin(); it != psychohistory->end();) {
for (auto it = psychohistory.begin(); it != psychohistory.end();) {
auto const& [time, degrees_of_freedom] = *it;
++it;
if (it == psychohistory->end() &&
if (it == psychohistory.end() &&
!message.psychohistory_is_authoritative()) {
vessel->psychohistory_ = vessel->history_->NewForkAtLast();
vessel->psychohistory_->Append(time, degrees_of_freedom);
} else {
vessel->history_->Append(time, degrees_of_freedom);
vessel->psychohistory_ = vessel->trajectory_.NewSegment();
}
vessel->trajectory_.Append(time, degrees_of_freedom);
}
if (message.psychohistory_is_authoritative()) {
vessel->psychohistory_ = vessel->history_->NewForkAtLast();
vessel->psychohistory_ = vessel->trajectory_.NewSegment();
}
vessel->prediction_ = vessel->psychohistory_->NewForkAtLast();
vessel->prediction_ = vessel->trajectory_.NewSegment();
} else if (is_pre_chasles) {
vessel->history_ = DiscreteTrajectory<Barycentric>::ReadFromMessage(
vessel->trajectory_ = DiscreteTraject0ry<Barycentric>::ReadFromMessage(
message.history(),
/*forks=*/{&vessel->psychohistory_});
vessel->prediction_ = vessel->psychohistory_->NewForkAtLast();
} else {
vessel->history_ = DiscreteTrajectory<Barycentric>::ReadFromMessage(
/*tracked=*/{&vessel->psychohistory_});
vessel->history_ = vessel->trajectory_.segments().begin();
vessel->prediction_ = vessel->trajectory_.NewSegment();
} else if (is_pre_ζήνων) {
vessel->trajectory_ = DiscreteTraject0ry<Barycentric>::ReadFromMessage(
message.history(),
/*forks=*/{&vessel->psychohistory_, &vessel->prediction_});
/*tracked=*/{&vessel->psychohistory_, &vessel->prediction_});
vessel->history_ = vessel->trajectory_.segments().begin();
// Necessary after Εὔδοξος because the ephemeris has not been prolonged
// during deserialization. Doesn't hurt prior to Εὔδοξος.
ephemeris->Prolong(vessel->prediction_->back().time);
} else {
vessel->trajectory_ = DiscreteTraject0ry<Barycentric>::ReadFromMessage(
message.history(),
/*tracked=*/{&vessel->history_,
&vessel->psychohistory_,
&vessel->prediction_});
// Necessary after Εὔδοξος because the ephemeris has not been prolonged
// during deserialization.
ephemeris->Prolong(vessel->prediction_->back().time);
}

if (is_pre_陈景润) {
@@ -548,19 +565,21 @@ Vessel::Vessel()
prediction_adaptive_step_parameters_(DefaultPredictionParameters()),
parent_(testing_utilities::make_not_null<Celestial const*>()),
ephemeris_(testing_utilities::make_not_null<Ephemeris<Barycentric>*>()),
history_(make_not_null_unique<DiscreteTrajectory<Barycentric>>()),
history_(trajectory_.segments().begin()),
psychohistory_(trajectory_.segments().end()),
prediction_(trajectory_.segments().end()),
prognosticator_(nullptr, 20ms) {}

absl::StatusOr<std::unique_ptr<DiscreteTrajectory<Barycentric>>>
absl::StatusOr<DiscreteTraject0ry<Barycentric>>
Vessel::FlowPrognostication(
PrognosticatorParameters prognosticator_parameters) {
auto prognostication = std::make_unique<DiscreteTrajectory<Barycentric>>();
prognostication->Append(
DiscreteTraject0ry<Barycentric> prognostication;
prognostication.Append(
prognosticator_parameters.first_time,
prognosticator_parameters.first_degrees_of_freedom);
absl::Status status;
status = ephemeris_->FlowWithAdaptiveStep(
prognostication.get(),
&prognostication,
Ephemeris<Barycentric>::NoIntrinsicAcceleration,
ephemeris_->t_max(),
prognosticator_parameters.adaptive_step_parameters,
@@ -569,15 +588,15 @@ Vessel::FlowPrognostication(
if (reached_t_max) {
// This will prolong the ephemeris by |max_ephemeris_steps_per_frame|.
status = ephemeris_->FlowWithAdaptiveStep(
prognostication.get(),
&prognostication,
Ephemeris<Barycentric>::NoIntrinsicAcceleration,
InfiniteFuture,
prognosticator_parameters.adaptive_step_parameters,
FlightPlan::max_ephemeris_steps_per_frame);
}
LOG_IF_EVERY_N(INFO, !status.ok(), 50)
<< "Prognostication from " << prognosticator_parameters.first_time
<< " finished at " << prognostication->back().time << " with "
<< " finished at " << prognostication.back().time << " with "
<< status.ToString() << " for " << ShortDebugString();
if (absl::IsCancelled(status)) {
return status;
@@ -591,10 +610,10 @@ Vessel::FlowPrognostication(
void Vessel::AppendToVesselTrajectory(
TrajectoryIterator const part_trajectory_begin,
TrajectoryIterator const part_trajectory_end,
DiscreteTrajectory<Barycentric>& trajectory) {
DiscreteTrajectorySegment<Barycentric> const& segment) {
CHECK(!parts_.empty());
std::vector<DiscreteTrajectory<Barycentric>::Iterator> its;
std::vector<DiscreteTrajectory<Barycentric>::Iterator> ends;
std::vector<DiscreteTraject0ry<Barycentric>::iterator> its;
std::vector<DiscreteTraject0ry<Barycentric>::iterator> ends;
its.reserve(parts_.size());
ends.reserve(parts_.size());
for (auto const& [_, part] : parts_) {
@@ -604,7 +623,7 @@ void Vessel::AppendToVesselTrajectory(

// We cannot append a point before this time, see the comments in AdvanceTime.
Instant const last_time =
trajectory.Empty() ? InfinitePast : trajectory.back().time;
segment.empty() ? InfinitePast : segment.back().time;

// Loop over the times of the trajectory.
for (;;) {
@@ -637,22 +656,20 @@ void Vessel::AppendToVesselTrajectory(
if (can_be_appended) {
DegreesOfFreedom<Barycentric> const vessel_degrees_of_freedom =
calculator.Get();
trajectory.Append(first_time, vessel_degrees_of_freedom);
trajectory_.Append(first_time, vessel_degrees_of_freedom);
}
}
}

void Vessel::AttachPrediction(
not_null<std::unique_ptr<DiscreteTrajectory<Barycentric>>> trajectory) {
trajectory->ForgetBefore(psychohistory_->back().time);
if (trajectory->Empty()) {
prediction_ = psychohistory_->NewForkAtLast();
void Vessel::AttachPrediction(DiscreteTraject0ry<Barycentric>&& trajectory) {
trajectory.ForgetBefore(psychohistory_->back().time);
if (trajectory.empty()) {
prediction_ = trajectory_.NewSegment();
} else {
if (prediction_ != nullptr) {
psychohistory_->DeleteFork(prediction_);
if (prediction_ != trajectory_.segments().end()) {
trajectory_.DeleteSegments(prediction_);
}
prediction_ = trajectory.get();
psychohistory_->AttachFork(std::move(trajectory));
prediction_ = trajectory_.AttachSegments(std::move(trajectory));
}
}

50 changes: 29 additions & 21 deletions ksp_plugin/vessel.hpp
Original file line number Diff line number Diff line change
@@ -17,7 +17,9 @@
#include "ksp_plugin/orbit_analyser.hpp"
#include "ksp_plugin/part.hpp"
#include "ksp_plugin/pile_up.hpp"
#include "physics/discrete_trajectory.hpp"
#include "physics/discrete_traject0ry.hpp"
#include "physics/discrete_trajectory_segment.hpp"
#include "physics/discrete_trajectory_segment_iterator.hpp"
#include "physics/ephemeris.hpp"
#include "physics/massless_body.hpp"
#include "quantities/named_quantities.hpp"
@@ -32,7 +34,9 @@ using base::RecurringThread;
using geometry::Instant;
using geometry::Vector;
using physics::DegreesOfFreedom;
using physics::DiscreteTrajectory;
using physics::DiscreteTraject0ry;
using physics::DiscreteTrajectorySegment;
using physics::DiscreteTrajectorySegmentIterator;
using physics::Ephemeris;
using physics::MasslessBody;
using physics::RotatingBody;
@@ -104,7 +108,7 @@ class Vessel {
// |history_| is never empty again and the psychohistory is usable.
virtual void PrepareHistory(
Instant const& t,
DiscreteTrajectory<Barycentric>::DownsamplingParameters const&
DiscreteTrajectorySegment<Barycentric>::DownsamplingParameters const&
downsampling_parameters);

// Disables downsampling for the history of this vessel. This is useful when
@@ -121,8 +125,9 @@ class Vessel {
// Calls |action| on all parts.
virtual void ForAllParts(std::function<void(Part&)> action) const;

virtual DiscreteTrajectory<Barycentric> const& psychohistory() const;
virtual DiscreteTrajectory<Barycentric> const& prediction() const;
virtual DiscreteTrajectorySegmentIterator<Barycentric> history() const;
virtual DiscreteTrajectorySegmentIterator<Barycentric> psychohistory() const;
virtual DiscreteTrajectorySegmentIterator<Barycentric> prediction() const;

virtual void set_prediction_adaptive_step_parameters(
Ephemeris<Barycentric>::AdaptiveStepParameters const&
@@ -215,24 +220,24 @@ class Vessel {
PrognosticatorParameters const& right);

using TrajectoryIterator =
DiscreteTrajectory<Barycentric>::Iterator (Part::*)();
DiscreteTraject0ry<Barycentric>::iterator (Part::*)();

// Runs the integrator to compute the |prognostication_| based on the given
// parameters.
absl::StatusOr<std::unique_ptr<DiscreteTrajectory<Barycentric>>>
absl::StatusOr<DiscreteTraject0ry<Barycentric>>
FlowPrognostication(PrognosticatorParameters prognosticator_parameters);

// Appends to |trajectory| the centre of mass of the trajectories of the parts
// denoted by |part_trajectory_begin| and |part_trajectory_end|. Only the
// points that are strictly after the fork time of the trajectory are used.
void AppendToVesselTrajectory(TrajectoryIterator part_trajectory_begin,
TrajectoryIterator part_trajectory_end,
DiscreteTrajectory<Barycentric>& trajectory);
// Appends to |trajectory_| the centre of mass of the trajectories of the
// parts denoted by |part_trajectory_begin| and |part_trajectory_end|. Only
// the points that are strictly after the start of the |segment| are used.
void AppendToVesselTrajectory(
TrajectoryIterator part_trajectory_begin,
TrajectoryIterator part_trajectory_end,
DiscreteTrajectorySegment<Barycentric> const& segment);

// Attaches the given |trajectory| to the end of the |psychohistory_| to
// become the new |prediction_|. If |prediction_| is not null, it is deleted.
void AttachPrediction(
not_null<std::unique_ptr<DiscreteTrajectory<Barycentric>>> trajectory);
void AttachPrediction(DiscreteTraject0ry<Barycentric>&& trajectory);

GUID const guid_;
std::string name_;
@@ -247,16 +252,19 @@ class Vessel {
std::map<PartId, not_null<std::unique_ptr<Part>>> parts_;
std::set<PartId> kept_parts_;

// The vessel trajectory is made of the history (always present) and (most of
// the time) the psychohistory and prediction. The prediction is periodically
// recomputed by the prognosticator.
DiscreteTraject0ry<Barycentric> trajectory_;

// See the comments in pile_up.hpp for an explanation of the terminology.
not_null<std::unique_ptr<DiscreteTrajectory<Barycentric>>> history_;
DiscreteTrajectory<Barycentric>* psychohistory_ = nullptr;
DiscreteTrajectorySegmentIterator<Barycentric> history_;
DiscreteTrajectorySegmentIterator<Barycentric> psychohistory_;

// The |prediction_| is forked off the end of the |psychohistory_|.
DiscreteTrajectory<Barycentric>* prediction_ = nullptr;
DiscreteTrajectorySegmentIterator<Barycentric> prediction_;

RecurringThread<PrognosticatorParameters,
std::unique_ptr<DiscreteTrajectory<Barycentric>>>
prognosticator_;
DiscreteTraject0ry<Barycentric>> prognosticator_;

std::unique_ptr<FlightPlan> flight_plan_;

132 changes: 72 additions & 60 deletions ksp_plugin_test/vessel_test.cpp
Original file line number Diff line number Diff line change
@@ -14,7 +14,10 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "ksp_plugin/celestial.hpp"
#include "ksp_plugin/frames.hpp"
#include "ksp_plugin/integrators.hpp"
#include "physics/degrees_of_freedom.hpp"
#include "physics/discrete_traject0ry.hpp"
#include "physics/massive_body.hpp"
#include "physics/rigid_motion.hpp"
#include "physics/rotating_body.hpp"
@@ -24,24 +27,28 @@
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"
#include "testing_utilities/componentwise.hpp"
#include "testing_utilities/discrete_trajectory_factories.hpp"
#include "testing_utilities/matchers.hpp"
#include "testing_utilities/trajectory_factories.hpp"

namespace principia {
namespace ksp_plugin {
namespace internal_vessel {

using base::not_null;
using base::make_not_null_unique;
using geometry::Barycentre;
using geometry::Displacement;
using geometry::InertiaTensor;
using geometry::Instant;
using geometry::Position;
using geometry::R3x3Matrix;
using geometry::Velocity;
using physics::DegreesOfFreedom;
using physics::DiscreteTraject0ry;
using physics::MassiveBody;
using physics::MockEphemeris;
using physics::RigidMotion;
using physics::RotatingBody;
using quantities::Mass;
using quantities::MomentOfInertia;
using quantities::si::Degree;
using quantities::si::Kilogram;
@@ -51,7 +58,8 @@ using quantities::si::Second;
using testing_utilities::AlmostEquals;
using testing_utilities::Componentwise;
using testing_utilities::EqualsProto;
using testing_utilities::NewLinearTrajectory;
using testing_utilities::AppendTrajectoryTimeline;
using testing_utilities::NewLinearTrajectoryTimeline;
using ::testing::AnyNumber;
using ::testing::DoAll;
using ::testing::ElementsAre;
@@ -101,13 +109,6 @@ class VesselTest : public testing::Test {
vessel_.AddPart(std::move(p2));
}

void AppendToPartHistory(DiscreteTrajectory<Barycentric> const& trajectory,
Part& part) {
for (auto const& [time, degrees_of_freedom] : trajectory) {
part.AppendToHistory(time, degrees_of_freedom);
}
}

MockEphemeris<Barycentric> ephemeris_;
RotatingBody<Barycentric> const body_;
Celestial const celestial_;
@@ -176,11 +177,11 @@ TEST_F(VesselTest, PrepareHistory) {
auto const expected_dof = Barycentre<DegreesOfFreedom<Barycentric>, Mass>(
{p1_dof_, p2_dof_}, {mass1_, mass2_});

EXPECT_EQ(1, vessel_.psychohistory().Size());
EXPECT_EQ(1, vessel_.psychohistory()->size());
EXPECT_EQ(t0_ + 1 * Second,
vessel_.psychohistory().back().time);
vessel_.psychohistory()->back().time);
EXPECT_THAT(
vessel_.psychohistory().back().degrees_of_freedom,
vessel_.psychohistory()->back().degrees_of_freedom,
Componentwise(AlmostEquals(expected_dof.position(), 0),
AlmostEquals(expected_dof.velocity(), 8)));
}
@@ -197,31 +198,42 @@ TEST_F(VesselTest, AdvanceTime) {
vessel_.PrepareHistory(t0_,
DefaultDownsamplingParameters());

AppendToPartHistory(*NewLinearTrajectory(p1_dof_,
/*Δt=*/0.5 * Second,
/*t1=*/t0_ + 0.5 * Second,
/*t2=*/t0_ + 1.5 * Second),
*p1_);
AppendToPartHistory(*NewLinearTrajectory(p2_dof_,
/*Δt=*/0.5 * Second,
/*t1=*/t0_ + 0.5 * Second,
/*t2=*/t0_ + 1.5 * Second),
*p2_);
AppendTrajectoryTimeline<Barycentric>(
NewLinearTrajectoryTimeline<Barycentric>(p1_dof_,
/*Δt=*/0.5 * Second,
/*t0=*/t0_,
/*t1=*/t0_ + 0.5 * Second,
/*t2=*/t0_ + 1.5 * Second),
[this](Instant const& time,
DegreesOfFreedom<Barycentric> const& degrees_of_freedom) {
p1_->AppendToHistory(time, degrees_of_freedom);
});
AppendTrajectoryTimeline<Barycentric>(
NewLinearTrajectoryTimeline<Barycentric>(p2_dof_,
/*Δt=*/0.5 * Second,
/*t0=*/t0_,
/*t1=*/t0_ + 0.5 * Second,
/*t2=*/t0_ + 1.5 * Second),
[this](Instant const& time,
DegreesOfFreedom<Barycentric> const& degrees_of_freedom) {
p2_->AppendToHistory(time, degrees_of_freedom);
});

vessel_.AdvanceTime();

auto const expected_vessel_psychohistory =
NewLinearTrajectory(Barycentre<DegreesOfFreedom<Barycentric>, Mass>(
{p1_dof_, p2_dof_}, {mass1_, mass2_}),
/*Δt=*/0.5 * Second,
/*t1=*/t0_,
/*t2=*/t0_ + 1.1 * Second);

EXPECT_EQ(3, vessel_.psychohistory().Size());
for (auto it1 = vessel_.psychohistory().begin(),
it2 = expected_vessel_psychohistory->begin();
it1 != vessel_.psychohistory().end() &&
it2 != expected_vessel_psychohistory->end();
auto const expected_vessel_psychohistory = NewLinearTrajectoryTimeline(
Barycentre<DegreesOfFreedom<Barycentric>, Mass>({p1_dof_, p2_dof_},
{mass1_, mass2_}),
/*Δt=*/0.5 * Second,
/*t1=*/t0_,
/*t2=*/t0_ + 1.1 * Second);

EXPECT_EQ(3, vessel_.history()->size() + vessel_.psychohistory()->size() - 1);
auto it1 = vessel_.history()->begin();
auto it2 = expected_vessel_psychohistory.begin();
for (;
it1 != vessel_.psychohistory()->end() &&
it2 != expected_vessel_psychohistory.end();
++it1, ++it2) {
EXPECT_EQ(it1->time, it2->time);
EXPECT_THAT(
@@ -238,16 +250,16 @@ TEST_F(VesselTest, Prediction) {
.WillRepeatedly(Return(t0_ + 2 * Second));

// The call to fill the prognostication until t_max.
auto const expected_vessel_prediction =
NewLinearTrajectory(Barycentre<DegreesOfFreedom<Barycentric>, Mass>(
{p1_dof_, p2_dof_}, {mass1_, mass2_}),
/*Δt=*/0.5 * Second,
/*t1=*/t0_,
/*t2=*/t0_ + 2 * Second);
auto const expected_vessel_prediction = NewLinearTrajectoryTimeline(
Barycentre<DegreesOfFreedom<Barycentric>, Mass>({p1_dof_, p2_dof_},
{mass1_, mass2_}),
/*Δt=*/0.5 * Second,
/*t1=*/t0_,
/*t2=*/t0_ + 2 * Second);
EXPECT_CALL(ephemeris_,
FlowWithAdaptiveStep(_, _, t0_ + 2 * Second, _, _))
.WillOnce(DoAll(
AppendPointsToDiscreteTrajectory(expected_vessel_prediction.get()),
AppendPointsToDiscreteTrajectory(&expected_vessel_prediction),
Return(absl::OkStatus())))
.WillRepeatedly(Return(absl::OkStatus()));

@@ -265,13 +277,14 @@ TEST_F(VesselTest, Prediction) {
vessel_.RefreshPrediction(t0_ + 1 * Second);
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms);
} while (vessel_.prediction().back().time == t0_);

EXPECT_EQ(3, vessel_.prediction().Size());
for (auto it1 = vessel_.prediction().begin(),
it2 = expected_vessel_prediction->begin();
it1 != vessel_.prediction().end() &&
it2 != expected_vessel_prediction->end();
} while (vessel_.prediction()->back().time == t0_);

EXPECT_EQ(3, vessel_.prediction()->size());
auto it1 = vessel_.prediction()->begin();
auto it2 = expected_vessel_prediction.begin();
for (;
it1 != vessel_.prediction()->end() &&
it2 != expected_vessel_prediction.end();
++it1, ++it2) {
EXPECT_EQ(it1->time, it2->time);
EXPECT_THAT(
@@ -288,7 +301,7 @@ TEST_F(VesselTest, PredictBeyondTheInfinite) {
.WillRepeatedly(Return(t0_ + 5 * Second));

// The call to fill the prognostication until t_max.
auto const expected_vessel_prediction1 = NewLinearTrajectory(
auto const expected_vessel_prediction1 = NewLinearTrajectoryTimeline(
Barycentre<DegreesOfFreedom<Barycentric>, Mass>({p1_dof_, p2_dof_},
{mass1_, mass2_}),
/*Δt=*/0.5 * Second,
@@ -297,12 +310,12 @@ TEST_F(VesselTest, PredictBeyondTheInfinite) {
EXPECT_CALL(ephemeris_,
FlowWithAdaptiveStep(_, _, t0_ + 5 * Second, _, _))
.WillOnce(DoAll(
AppendPointsToDiscreteTrajectory(expected_vessel_prediction1.get()),
AppendPointsToDiscreteTrajectory(&expected_vessel_prediction1),
Return(absl::OkStatus())))
.WillRepeatedly(Return(absl::OkStatus()));

// The call to extend the exphemeris by many points.
auto const expected_vessel_prediction2 = NewLinearTrajectory(
auto const expected_vessel_prediction2 = NewLinearTrajectoryTimeline(
Barycentre<DegreesOfFreedom<Barycentric>, Mass>({p1_dof_, p2_dof_},
{mass1_, mass2_}),
/*Δt=*/0.5 * Second,
@@ -311,7 +324,7 @@ TEST_F(VesselTest, PredictBeyondTheInfinite) {
EXPECT_CALL(ephemeris_,
FlowWithAdaptiveStep(_, _, astronomy::InfiniteFuture, _, _))
.WillOnce(DoAll(
AppendPointsToDiscreteTrajectory(expected_vessel_prediction2.get()),
AppendPointsToDiscreteTrajectory(&expected_vessel_prediction2),
Return(absl::OkStatus())))
.WillRepeatedly(Return(absl::OkStatus()));

@@ -322,19 +335,19 @@ TEST_F(VesselTest, PredictBeyondTheInfinite) {
vessel_.RefreshPrediction();
using namespace std::chrono_literals;
std::this_thread::sleep_for(100ms);
} while (vessel_.prediction().Size() <
expected_vessel_prediction1->Size() +
expected_vessel_prediction2->Size());
} while (vessel_.prediction()->size() <
expected_vessel_prediction1.size() +
expected_vessel_prediction2.size());

auto it = expected_vessel_prediction1->begin();
for (auto const& [time, degrees_of_freedom] : vessel_.prediction()) {
auto it = expected_vessel_prediction1.begin();
for (auto const& [time, degrees_of_freedom] : *vessel_.prediction()) {
EXPECT_EQ(time, it->time);
EXPECT_THAT(
degrees_of_freedom,
Componentwise(AlmostEquals(it->degrees_of_freedom.position(), 0, 0),
AlmostEquals(it->degrees_of_freedom.velocity(), 0, 8)));
if (it->time == t0_ + 5 * Second) {
it = expected_vessel_prediction2->begin();
it = expected_vessel_prediction2.begin();
} else {
++it;
}
@@ -416,6 +429,5 @@ TEST_F(VesselTest, SerializationSuccess) {
EXPECT_THAT(message, EqualsProto(second_message));
}

} // namespace internal_vessel
} // namespace ksp_plugin
} // namespace principia
7 changes: 7 additions & 0 deletions testing_utilities/discrete_trajectory_factories.hpp
Original file line number Diff line number Diff line change
@@ -87,6 +87,13 @@ template<typename Frame>
void AppendTrajectoryTimeline(Timeline<Frame> const& from,
DiscreteTraject0ry<Frame>& to);

template<typename Frame>
void AppendTrajectoryTimeline(
Timeline<Frame> const& from,
std::function<void(
Instant const& time,
DegreesOfFreedom<Frame> const& degrees_of_freedom)> const& append_to);

} // namespace internal_discrete_trajectory_factories

using internal_discrete_trajectory_factories::AppendTrajectoryTimeline;
11 changes: 11 additions & 0 deletions testing_utilities/discrete_trajectory_factories_body.hpp
Original file line number Diff line number Diff line change
@@ -120,6 +120,17 @@ void AppendTrajectoryTimeline(Timeline<Frame> const& from,
}
}

template<typename Frame>
void AppendTrajectoryTimeline(
Timeline<Frame> const& from,
std::function<void(
Instant const& time,
DegreesOfFreedom<Frame> const& degrees_of_freedom)> const& append_to) {
for (auto const& [t, degrees_of_freedom] : from) {
append_to(t, degrees_of_freedom);
}
}

} // namespace internal_discrete_trajectory_factories
} // namespace testing_utilities
} // namespace principia