Skip to content

Commit 95f3d66

Browse files
authored
Merge pull request #3148 from pleroy/Evaluate
DiscreteTrajectorySegment, part 3: evaluation
2 parents 91dd45b + 8bccb77 commit 95f3d66

6 files changed

+176
-28
lines changed

physics/discrete_trajectory_segment.hpp

+22-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#include "absl/container/btree_set.h"
88
#include "absl/status/status.h"
99
#include "geometry/named_quantities.hpp"
10+
#include "numerics/hermite3.hpp"
1011
#include "physics/degrees_of_freedom.hpp"
1112
#include "physics/discrete_trajectory_iterator.hpp"
1213
#include "physics/discrete_trajectory_segment_iterator.hpp"
1314
#include "physics/discrete_trajectory_types.hpp"
15+
#include "physics/trajectory.hpp"
1416

1517
namespace principia {
1618

@@ -28,10 +30,13 @@ class DiscreteTrajectorySegmentTest;
2830
namespace internal_discrete_trajectory_segment {
2931

3032
using geometry::Instant;
33+
using geometry::Position;
34+
using geometry::Velocity;
35+
using numerics::Hermite3;
3136
using physics::DegreesOfFreedom;
3237

3338
template<typename Frame>
34-
class DiscreteTrajectorySegment {
39+
class DiscreteTrajectorySegment : public Trajectory<Frame> {
3540
using Timeline = internal_discrete_trajectory_types::Timeline<Frame>;
3641

3742
public:
@@ -61,14 +66,22 @@ class DiscreteTrajectorySegment {
6166
reverse_iterator rbegin() const;
6267
reverse_iterator rend() const;
6368

69+
// TODO(phl): We probably don't want empty segments.
70+
bool empty() const;
71+
virtual std::int64_t size() const;
72+
6473
iterator find(Instant const& t) const;
6574

6675
iterator lower_bound(Instant const& t) const;
6776
iterator upper_bound(Instant const& t) const;
6877

69-
// TODO(phl): We probably don't want empty segments.
70-
bool empty() const;
71-
virtual std::int64_t size() const;
78+
Instant t_min() const override;
79+
Instant t_max() const override;
80+
81+
Position<Frame> EvaluatePosition(Instant const& t) const override;
82+
Velocity<Frame> EvaluateVelocity(Instant const& t) const override;
83+
DegreesOfFreedom<Frame> EvaluateDegreesOfFreedom(
84+
Instant const& t) const override;
7285

7386
private:
7487
absl::Status Append(Instant const& t,
@@ -84,6 +97,11 @@ class DiscreteTrajectorySegment {
8497
void ForgetBefore(Instant const& t);
8598
void ForgetBefore(typename Timeline::const_iterator end);
8699

100+
// Returns the Hermite interpolation for the left-open, right-closed
101+
// trajectory segment bounded above by |upper|.
102+
Hermite3<Instant, Position<Frame>> GetInterpolation(
103+
typename Timeline::const_iterator upper) const;
104+
87105
virtual typename Timeline::const_iterator timeline_begin() const;
88106
virtual typename Timeline::const_iterator timeline_end() const;
89107

physics/discrete_trajectory_segment_body.hpp

+76-6
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22

33
#include "physics/discrete_trajectory_segment.hpp"
44

5+
#include <iterator>
6+
7+
#include "astronomy/epoch.hpp"
58
#include "glog/logging.h"
69

710
namespace principia {
811
namespace physics {
912
namespace internal_discrete_trajectory_segment {
1013

14+
using astronomy::InfiniteFuture;
15+
using astronomy::InfinitePast;
16+
1117
template<typename Frame>
1218
DiscreteTrajectorySegment<Frame>::DiscreteTrajectorySegment(
1319
DiscreteTrajectorySegmentIterator<Frame> const self)
@@ -44,6 +50,18 @@ DiscreteTrajectorySegment<Frame>::rend() const {
4450
return reverse_iterator(begin());
4551
}
4652

53+
template<typename Frame>
54+
bool DiscreteTrajectorySegment<Frame>::empty() const {
55+
return timeline_.empty();
56+
}
57+
58+
template<typename Frame>
59+
std::int64_t DiscreteTrajectorySegment<Frame>::size() const {
60+
// NOTE(phl): This assumes that there are no repeated times *within* a
61+
// segment. This is enforced by Append.
62+
return timeline_.size();
63+
}
64+
4765
template<typename Frame>
4866
typename DiscreteTrajectorySegment<Frame>::iterator
4967
DiscreteTrajectorySegment<Frame>::find(Instant const& t) const {
@@ -78,15 +96,51 @@ DiscreteTrajectorySegment<Frame>::upper_bound(Instant const& t) const {
7896
}
7997

8098
template<typename Frame>
81-
bool DiscreteTrajectorySegment<Frame>::empty() const {
82-
return timeline_.empty();
99+
Instant DiscreteTrajectorySegment<Frame>::t_min() const {
100+
return empty() ? InfiniteFuture : timeline_.cbegin()->first;
83101
}
84102

85103
template<typename Frame>
86-
std::int64_t DiscreteTrajectorySegment<Frame>::size() const {
87-
// NOTE(phl): This assumes that there are no repeated times *within* a
88-
// segment. This is enforced by Append.
89-
return timeline_.size();
104+
Instant DiscreteTrajectorySegment<Frame>::t_max() const {
105+
return empty() ? InfinitePast : timeline_.crbegin()->first;
106+
}
107+
108+
template<typename Frame>
109+
Position<Frame> DiscreteTrajectorySegment<Frame>::EvaluatePosition(
110+
Instant const& t) const {
111+
auto const it = timeline_.lower_bound(t);
112+
if (it->first == t) {
113+
return it->second.position();
114+
}
115+
CHECK_LT(t_min(), t);
116+
CHECK_GT(t_max(), t);
117+
return GetInterpolation(it).Evaluate(t);
118+
}
119+
120+
template<typename Frame>
121+
Velocity<Frame> DiscreteTrajectorySegment<Frame>::EvaluateVelocity(
122+
Instant const& t) const {
123+
auto const it = timeline_.lower_bound(t);
124+
if (it->first == t) {
125+
return it->second.velocity();
126+
}
127+
CHECK_LT(t_min(), t);
128+
CHECK_GT(t_max(), t);
129+
return GetInterpolation(it).EvaluateDerivative(t);
130+
}
131+
132+
template<typename Frame>
133+
DegreesOfFreedom<Frame>
134+
DiscreteTrajectorySegment<Frame>::EvaluateDegreesOfFreedom(
135+
Instant const& t) const {
136+
auto const it = timeline_.lower_bound(t);
137+
if (it->first == t) {
138+
return it->second;
139+
}
140+
CHECK_LT(t_min(), t);
141+
CHECK_GT(t_max(), t);
142+
auto const interpolation = GetInterpolation(it);
143+
return {interpolation.Evaluate(t), interpolation.EvaluateDerivative(t)};
90144
}
91145

92146
template<typename Frame>
@@ -136,6 +190,22 @@ void DiscreteTrajectorySegment<Frame>::ForgetBefore(
136190
// TODO(phl): Downsampling.
137191
}
138192

193+
template<typename Frame>
194+
Hermite3<Instant, Position<Frame>>
195+
DiscreteTrajectorySegment<Frame>::GetInterpolation(
196+
typename Timeline::const_iterator const upper) const {
197+
CHECK(upper != timeline_.cbegin());
198+
auto const lower = std::prev(upper);
199+
auto const& [lower_time, lower_degrees_of_freedom] = *lower;
200+
auto const& [upper_time, upper_degrees_of_freedom] = *upper;
201+
return Hermite3<Instant, Position<Frame>>{
202+
{lower_time, upper_time},
203+
{lower_degrees_of_freedom.position(),
204+
upper_degrees_of_freedom.position()},
205+
{lower_degrees_of_freedom.velocity(),
206+
upper_degrees_of_freedom.velocity()}};
207+
}
208+
139209
template<typename Frame>
140210
typename DiscreteTrajectorySegment<Frame>::Timeline::const_iterator
141211
DiscreteTrajectorySegment<Frame>::timeline_begin() const {

physics/discrete_trajectory_segment_test.cpp

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
1-
#include "physics/discrete_trajectory_segment.hpp"
1+
#include "physics/discrete_trajectory_segment.hpp"
22

33
#include <memory>
4+
#include <vector>
45

56
#include "base/not_null.hpp"
67
#include "geometry/frame.hpp"
78
#include "geometry/named_quantities.hpp"
89
#include "gmock/gmock.h"
910
#include "gtest/gtest.h"
1011
#include "physics/discrete_trajectory_types.hpp"
12+
#include "quantities/named_quantities.hpp"
13+
#include "quantities/quantities.hpp"
1114
#include "quantities/si.hpp"
15+
#include "testing_utilities/approximate_quantity.hpp"
16+
#include "testing_utilities/discrete_trajectory_factories.hpp"
17+
#include "testing_utilities/is_near.hpp"
1218

1319
namespace principia {
1420
namespace physics {
1521

1622
using base::check_not_null;
1723
using geometry::Frame;
1824
using geometry::Instant;
25+
using quantities::Abs;
26+
using quantities::AngularFrequency;
27+
using quantities::Length;
28+
using quantities::Speed;
29+
using quantities::Time;
30+
using quantities::si::Metre;
31+
using quantities::si::Milli;
32+
using quantities::si::Nano;
33+
using quantities::si::Radian;
1934
using quantities::si::Second;
35+
using testing_utilities::IsNear;
36+
using testing_utilities::NewCircularTrajectorySegment;
37+
using testing_utilities::operator""_⑴;
38+
using ::testing::Eq;
2039

2140
class DiscreteTrajectorySegmentTest : public ::testing::Test {
2241
protected:
@@ -144,5 +163,31 @@ TEST_F(DiscreteTrajectorySegmentTest, ForgetBeforeTheBeginning) {
144163
EXPECT_EQ(t0_ + 2 * Second, segment_->begin()->first);
145164
}
146165

166+
TEST_F(DiscreteTrajectorySegmentTest, Evaluate) {
167+
AngularFrequency const ω = 3 * Radian / Second;
168+
Length const r = 2 * Metre;
169+
Time const Δt = 10 * Milli(Second);
170+
Instant const t1 = t0_;
171+
Instant const t2 = t0_ + 10 * Second;
172+
auto circle = NewCircularTrajectorySegment<World>(ω, r, Δt, t1, t2);
173+
auto& segment = **circle->cbegin();
174+
175+
EXPECT_THAT(segment.size(), Eq(1001));
176+
std::vector<Length> position_errors;
177+
std::vector<Speed> velocity_errors;
178+
for (Instant t = segment.t_min();
179+
t <= segment.t_max();
180+
t += 1 * Milli(Second)) {
181+
position_errors.push_back(
182+
Abs((segment.EvaluatePosition(t) - World::origin).Norm() - r));
183+
velocity_errors.push_back(
184+
Abs(segment.EvaluateVelocity(t).Norm() - r * ω / Radian));
185+
}
186+
EXPECT_THAT(*std::max_element(position_errors.begin(), position_errors.end()),
187+
IsNear(4.2_⑴ * Nano(Metre)));
188+
EXPECT_THAT(*std::max_element(velocity_errors.begin(), velocity_errors.end()),
189+
IsNear(10.4_⑴ * Nano(Metre / Second)));
190+
}
191+
147192
} // namespace physics
148193
} // namespace principia

testing_utilities/discrete_trajectory_factories.hpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ class DiscreteTrajectoryFactoriesFriend {
4646
// TODO(phl): Must return unique_ptr because copying Segments is a bad idea due
4747
// to the pointers within iterators.
4848

49+
// An empty trajectory. Convenient for initializations.
50+
template<typename Frame>
51+
not_null<std::unique_ptr<Segments<Frame>>>
52+
NewEmptyTrajectorySegment();
53+
4954
// A linear trajectory with constant velocity, going through
5055
// |degrees_of_freedom.position()| at t = 0. The first point is at time |t1|,
5156
// the last point at a time < |t2|.
@@ -81,13 +86,14 @@ NewCircularTrajectorySegment(Time const& period,
8186
Instant const& t2);
8287

8388
template<typename Frame>
84-
void AppendTrajectorySegments(Segments<Frame> const& from,
85-
Segments<Frame>& to);
89+
void AppendTrajectorySegment(DiscreteTrajectorySegment<Frame> const& from,
90+
DiscreteTrajectorySegment<Frame>& to);
8691

8792
} // namespace internal_discrete_trajectory_factories
8893

89-
using internal_discrete_trajectory_factories::AppendTrajectorySegments;
94+
using internal_discrete_trajectory_factories::AppendTrajectorySegment;
9095
using internal_discrete_trajectory_factories::NewCircularTrajectorySegment;
96+
using internal_discrete_trajectory_factories::NewEmptyTrajectorySegment;
9197
using internal_discrete_trajectory_factories::NewLinearTrajectorySegment;
9298

9399
} // namespace testing_utilities

testing_utilities/discrete_trajectory_factories_body.hpp

+17-14
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,23 @@ DiscreteTrajectoryFactoriesFriend<Frame>::MakeDiscreteTrajectorySegment(
4444

4545
template<typename Frame>
4646
not_null<std::unique_ptr<Segments<Frame>>>
47-
NewLinearTrajectorySegment(DegreesOfFreedom<Frame> const& degrees_of_freedom,
48-
Time const& Δt,
49-
Instant const& t1,
50-
Instant const& t2) {
51-
static Instant const t0;
47+
NewEmptyTrajectorySegment() {
5248
auto segments = std::make_unique<Segments<Frame>>(1);
5349
auto const it = segments->begin();
5450
*it = std::make_unique<DiscreteTrajectorySegment<Frame>>(
5551
DiscreteTrajectoryFactoriesFriend<Frame>::MakeDiscreteTrajectorySegment(
5652
*segments, it));
53+
return segments;
54+
}
55+
56+
template<typename Frame>
57+
not_null<std::unique_ptr<Segments<Frame>>>
58+
NewLinearTrajectorySegment(DegreesOfFreedom<Frame> const& degrees_of_freedom,
59+
Time const& Δt,
60+
Instant const& t1,
61+
Instant const& t2) {
62+
static Instant const t0;
63+
auto segments = NewEmptyTrajectorySegment<Frame>();
5764
auto& segment = **segments->cbegin();
5865
for (auto t = t1; t < t2; t += Δt) {
5966
auto const velocity = degrees_of_freedom.velocity();
@@ -82,11 +89,7 @@ NewCircularTrajectorySegment(AngularFrequency const& ω,
8289
Instant const& t1,
8390
Instant const& t2) {
8491
static Instant const t0;
85-
auto segments = std::make_unique<Segments<Frame>>(1);
86-
auto const it = segments->begin();
87-
*it = std::make_unique<DiscreteTrajectorySegment<Frame>>(
88-
DiscreteTrajectoryFactoriesFriend<Frame>::MakeDiscreteTrajectorySegment(
89-
*segments, it));
92+
auto segments = NewEmptyTrajectorySegment<Frame>();
9093
auto& segment = **segments->cbegin();
9194
Speed const v = ω * r / Radian;
9295
for (auto t = t1; t < t2; t += Δt) {
@@ -117,10 +120,10 @@ NewCircularTrajectorySegment(Time const& period,
117120
}
118121

119122
template<typename Frame>
120-
void AppendTrajectorySegments(Segments<Frame> const& from,
121-
Segments<Frame>& to) {
122-
for (auto const& [t, dof] : *from.front()) {
123-
DiscreteTrajectoryFactoriesFriend<Frame>::Append(t, dof, to.rbegin());
123+
void AppendTrajectorySegment(DiscreteTrajectorySegment<Frame> const& from,
124+
DiscreteTrajectorySegment<Frame>& to) {
125+
for (auto const& [t, degrees_of_freedom] : from) {
126+
DiscreteTrajectoryFactoriesFriend<Frame>::Append(t, degrees_of_freedom, to);
124127
}
125128
}
126129

testing_utilities/discrete_trajectory_factories_test.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ class DiscreteTrajectoryFactoriesTest : public ::testing::Test {
3939
serialization::Frame::TEST>;
4040
};
4141

42+
TEST_F(DiscreteTrajectoryFactoriesTest, NewEmptyTrajectorySegment) {
43+
auto const segments = NewEmptyTrajectorySegment<World>();
44+
auto const& segment = *segments->front();
45+
EXPECT_TRUE(segment.empty());
46+
}
47+
4248
TEST_F(DiscreteTrajectoryFactoriesTest, NewLinearTrajectorySegment) {
4349
auto const segments = NewLinearTrajectorySegment<World>(
4450
/*degrees_of_freedom=*/

0 commit comments

Comments
 (0)