diff --git a/physics/discrete_trajectory2.hpp b/physics/discrete_trajectory2.hpp
index f67f263eb5..6388116234 100644
--- a/physics/discrete_trajectory2.hpp
+++ b/physics/discrete_trajectory2.hpp
@@ -101,17 +101,11 @@ class DiscreteTrajectory2 : public Trajectory {
not_null message,
std::vector const& tracked,
std::vector const& exact) const;
- void WriteToMessage(
- not_null message,
- iterator begin, iterator end,
- std::vector const& tracked,
- std::vector const& exact) const;
-
template>>
- static not_null> ReadFromMessage(
+ static DiscreteTrajectory2 ReadFromMessage(
serialization::DiscreteTrajectory const& message,
- std::vector const& tracked);
+ std::vector const& tracked);
private:
using Segments = internal_discrete_trajectory_types::Segments;
diff --git a/physics/discrete_trajectory2_body.hpp b/physics/discrete_trajectory2_body.hpp
index dcab069c47..de3fc77739 100644
--- a/physics/discrete_trajectory2_body.hpp
+++ b/physics/discrete_trajectory2_body.hpp
@@ -1,9 +1,10 @@
-#pragma once
+#pragma once
#include "physics/discrete_trajectory2.hpp"
#include
+#include "absl/container/flat_hash_map.h"
#include "astronomy/epoch.hpp"
namespace principia {
@@ -271,26 +272,94 @@ void DiscreteTrajectory2::WriteToMessage(
not_null message,
std::vector const& tracked,
std::vector const& exact) const {
- // TODO(phl): Implement.
-}
+ // Construct a map to efficiently find if a segment must be tracked. The
+ // keys are pointers to segments in |tracked|, the values are the
+ // corresponding indices.
+ absl::flat_hash_map const*, int>
+ segment_to_position;
+ for (int i = 0; i < tracked.size(); ++i) {
+ segment_to_position.emplace(&*tracked[i], i);
+ }
-template
-void DiscreteTrajectory2::WriteToMessage(
- not_null message,
- iterator begin,
- iterator end,
- std::vector const& tracked,
- std::vector const& exact) const {
- // TODO(phl): Implement.
+ // Initialize the tracked positions to be able to recognize if some are
+ // missing.
+ message->mutable_tracked_position()->Resize(
+ tracked.size(),
+ serialization::DiscreteTrajectory::MISSING_TRACKED_POSITION);
+
+ // The position of a segment in the repeated field |segment|.
+ int segment_position = 0;
+ for (auto sit = segments_->begin();
+ sit != segments_->end();
+ ++sit, ++segment_position) {
+ sit->WriteToMessage(message->add_segment(), exact);
+
+ if (auto const position_it = segment_to_position.find(&*sit);
+ position_it != segment_to_position.end()) {
+ // The field |tracked_position| is indexed by the indices in |tracked|.
+ // Its value is the position of a tracked segment in the field |segment|.
+ message->set_tracked_position(position_it->second, segment_position);
+ }
+ }
+
+ for (auto const& [t, _] : segment_by_left_endpoint_) {
+ t.WriteToMessage(message->add_left_endpoint());
+ }
+
+ // Check that all the segments in |tracked| were mapped.
+ // NOTE(phl): This might be too strong a constraint in Entwurf.
+ for (auto const tracked_position : message->tracked_position()) {
+ CHECK_NE(serialization::DiscreteTrajectory::MISSING_TRACKED_POSITION,
+ tracked_position);
+ }
}
template
template
-not_null>>
+DiscreteTrajectory2
DiscreteTrajectory2::ReadFromMessage(
serialization::DiscreteTrajectory const& message,
- std::vector const& tracked) {
- // TODO(phl): Implement.
+ std::vector const& tracked) {
+ DiscreteTrajectory2 trajectory(uninitialized);
+
+ bool const is_pre_ζήνων = message.segment_size() == 0;
+ if (is_pre_ζήνων) {
+ // TODO(phl): Implement.
+ LOG(FATAL) << "Pre-Ζήνων compatibility NYI";
+ }
+
+ // First restore the segments themselves. This vector will be used to restore
+ // the tracked segments.
+ std::vector segment_iterators;
+ segment_iterators.reserve(message.segment_size());
+ for (auto const& serialized_segment : message.segment()) {
+ trajectory.segments_->emplace_back();
+ auto const sit = --trajectory.segments_->end();
+ auto const self = SegmentIterator(trajectory.segments_.get(), sit);
+ *sit = DiscreteTrajectorySegment::ReadFromMessage(serialized_segment,
+ self);
+ segment_iterators.push_back(self);
+ }
+
+ // Restore the tracked segments.
+ CHECK_EQ(tracked.size(), message.tracked_position_size());
+ for (int i = 0; i < message.tracked_position_size(); ++i) {
+ int const tracked_position = message.tracked_position(i);
+ CHECK_NE(serialization::DiscreteTrajectory::MISSING_TRACKED_POSITION,
+ tracked_position);
+ *tracked[i] = segment_iterators[tracked_position];
+ }
+
+ // Finally restore the left endpoints.
+ auto sit = trajectory.segments_->begin();
+ for (auto const& serialized_t : message.left_endpoint()) {
+ auto const t = Instant::ReadFromMessage(serialized_t);
+ trajectory.segment_by_left_endpoint_.emplace_hint(
+ trajectory.segment_by_left_endpoint_.end(), t, sit);
+ ++sit;
+ }
+
+ return trajectory;
}
template
diff --git a/physics/discrete_trajectory2_test.cpp b/physics/discrete_trajectory2_test.cpp
index 66e08a3aff..6f544b0a53 100644
--- a/physics/discrete_trajectory2_test.cpp
+++ b/physics/discrete_trajectory2_test.cpp
@@ -7,27 +7,36 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/si.hpp"
+#include "serialization/physics.pb.h"
#include "testing_utilities/almost_equals.hpp"
#include "testing_utilities/componentwise.hpp"
#include "testing_utilities/discrete_trajectory_factories.hpp"
+#include "testing_utilities/matchers.hpp"
namespace principia {
namespace physics {
using geometry::Displacement;
using geometry::Frame;
+using geometry::Handedness;
+using geometry::Inertial;
using geometry::Instant;
using geometry::Velocity;
using quantities::si::Metre;
using quantities::si::Second;
using testing_utilities::AlmostEquals;
using testing_utilities::Componentwise;
+using testing_utilities::EqualsProto;
using testing_utilities::NewLinearTrajectoryTimeline;
using ::testing::ElementsAre;
class DiscreteTrajectory2Test : public ::testing::Test {
protected:
- using World = Frame;
+ using World = Frame;
+
// Constructs a trajectory with three 5-second segments starting at |t0| and
// the given |degrees_of_freedom|.
@@ -469,5 +478,43 @@ TEST_F(DiscreteTrajectory2Test, TMinTMaxEvaluate) {
0 * Metre / Second}), 0)));
}
+TEST_F(DiscreteTrajectory2Test, SerializationRoundTrip) {
+ auto const trajectory = MakeTrajectory();
+ auto const trajectory_first_segment = trajectory.segments().begin();
+ auto const trajectory_second_segment = std::next(trajectory_first_segment);
+
+ serialization::DiscreteTrajectory message1;
+ trajectory.WriteToMessage(&message1,
+ /*tracked=*/{trajectory_second_segment},
+ /*exact=*/
+ {trajectory.lower_bound(t0_ + 2 * Second),
+ trajectory.lower_bound(t0_ + 3 * Second)});
+
+ DiscreteTrajectorySegmentIterator deserialized_second_segment;
+ auto const deserialized_trajectory =
+ DiscreteTrajectory2::ReadFromMessage(
+ message1, /*tracked=*/{&deserialized_second_segment});
+
+ // Check that the tracked segment was properly retrieved.
+ EXPECT_EQ(t0_ + 4 * Second, deserialized_second_segment->begin()->first);
+ EXPECT_EQ(t0_ + 9 * Second, deserialized_second_segment->rbegin()->first);
+
+ // Check that the exact points are exact.
+ EXPECT_EQ(deserialized_trajectory.lower_bound(t0_ + 2 * Second)->second,
+ trajectory.lower_bound(t0_ + 2 * Second)->second);
+ EXPECT_EQ(deserialized_trajectory.lower_bound(t0_ + 3 * Second)->second,
+ trajectory.lower_bound(t0_ + 3 * Second)->second);
+
+ serialization::DiscreteTrajectory message2;
+ deserialized_trajectory.WriteToMessage(
+ &message2,
+ /*tracked=*/{deserialized_second_segment},
+ /*exact=*/
+ {deserialized_trajectory.lower_bound(t0_ + 2 * Second),
+ deserialized_trajectory.lower_bound(t0_ + 3 * Second)});
+
+ EXPECT_THAT(message2, EqualsProto(message1));
+}
+
} // namespace physics
} // namespace principia
diff --git a/physics/discrete_trajectory_body.hpp b/physics/discrete_trajectory_body.hpp
index 633e8ece35..c42d3f1bd5 100644
--- a/physics/discrete_trajectory_body.hpp
+++ b/physics/discrete_trajectory_body.hpp
@@ -486,10 +486,10 @@ typename DiscreteTrajectory::Downsampling
DiscreteTrajectory::Downsampling::ReadFromMessage(
serialization::DiscreteTrajectory::Downsampling const& message,
Timeline const& timeline) {
- bool const is_pre_grotendieck_haar = message.has_start_of_dense_timeline();
+ bool const is_pre_haar = message.has_start_of_dense_timeline();
Downsampling downsampling({message.max_dense_intervals(),
Length::ReadFromMessage(message.tolerance())});
- if (is_pre_grotendieck_haar) {
+ if (is_pre_haar) {
// No support for forks in legacy saves, so |find| will succeed and ++ is
// safe.
auto it = timeline.find(
diff --git a/serialization/physics.proto b/serialization/physics.proto
index adebe8641d..8fcde4db63 100644
--- a/serialization/physics.proto
+++ b/serialization/physics.proto
@@ -83,9 +83,9 @@ message DiscreteTrajectory {
required int64 max_dense_intervals = 2;
required Quantity tolerance = 3;
// The instant of the iterator; absent if it is the end of the timeline.
- // Pre Grothendieck/Haar.
+ // Pre Haar.
optional Point start_of_dense_timeline = 1;
- // Added in Grothendieck/Haar.
+ // Added in Haar.
repeated Point dense_timeline = 4;
}
message InstantaneousDegreesOfFreedom {
@@ -112,6 +112,17 @@ message DiscreteTrajectory {
required int32 timeline_size = 4;
}
optional Zfp zfp = 5;
+
+ // All the declarations above are pre-Ζήνων. All the declarations below were
+ // added in Ζήνων.
+
+ // A marker to indicate that a segment doesn't have its position tracked.
+ enum TrackedPosition {
+ MISSING_TRACKED_POSITION = -1;
+ }
+ repeated DiscreteTrajectorySegment segment = 7;
+ repeated Point left_endpoint = 8;
+ repeated int32 tracked_position = 9;
}
// Added in Ζήνων.