// Check that the small body has the right degrees of freedom in the frame of
// the big body.
TEST_F(BodyCentredNonRotatingDynamicFrameTest, SmallBodyInBigFrame) {
  int const steps = 100;
  Bivector<double, ICRFJ2000Equator> const axis({0, 0, 1});

  RelativeDegreesOfFreedom<ICRFJ2000Equator> const initial_big_to_small =
      small_initial_state_ - big_initial_state_;
  ContinuousTrajectory<ICRFJ2000Equator>::Hint hint;
  for (Instant t = t0_; t < t0_ + 1 * period_; t += period_ / steps) {
    DegreesOfFreedom<ICRFJ2000Equator> const small_in_inertial_frame_at_t =
        solar_system_.trajectory(*ephemeris_, small).
            EvaluateDegreesOfFreedom(t, &hint);

    auto const rotation_in_big_frame_at_t =
        Rotation<ICRFJ2000Equator, Big>(2 * π * (t - t0_) * Radian / period_,
                                        axis,
                                        DefinesFrame<Big>{});
    DegreesOfFreedom<Big> const small_in_big_frame_at_t(
        rotation_in_big_frame_at_t(initial_big_to_small.displacement()) +
            Big::origin,
        rotation_in_big_frame_at_t(initial_big_to_small.velocity()));

    auto const to_big_frame_at_t = big_frame_->ToThisFrameAtTime(t);
    EXPECT_THAT(AbsoluteError(
                    to_big_frame_at_t(small_in_inertial_frame_at_t).position(),
                    small_in_big_frame_at_t.position()),
                Lt(0.3 * Milli(Metre)));
    EXPECT_THAT(AbsoluteError(
                    to_big_frame_at_t(small_in_inertial_frame_at_t).velocity(),
                    small_in_big_frame_at_t.velocity()),
                Lt(4 * Milli(Metre) / Second));
  }
}
TEST_F(JacobiCoordinatesTest, Jacobi) {
  auto const x_positions = [](JacobiCoordinates<Frame> const& system) {
    std::vector<Length> result;
    auto const barycentric_dof = system.BarycentricDegreesOfFreedom();
    std::transform(barycentric_dof.begin(),
                   barycentric_dof.end(),
                   std::back_inserter(result),
                   [](RelativeDegreesOfFreedom<Frame> const& dof) {
                     return dof.displacement().coordinates().x;
                   });
    return result;
  };

  // i, and Ω are 0 by default.
  KeplerianElements<Frame> elements;
  elements.eccentricity = 0;
  elements.argument_of_periapsis = 0 * Radian;
  elements.mean_anomaly = 0 * Radian;

  JacobiCoordinates<Frame> system(m2_);
  EXPECT_EQ(2 * Kilogram, system.System().mass());
  EXPECT_THAT(x_positions(system), ElementsAre(0 * Metre));

  elements.semimajor_axis = 1 * Metre;
  system.Add(m1_, elements);
  // The system now consists of a 2 kg mass and a 1 kg mass, with the barycentre
  // one third of the way, as shown.
  // 2  1
  //  ^ barycentre
  EXPECT_EQ(3 * Kilogram, system.System().mass());
  EXPECT_THAT(
      x_positions(system),
      ElementsAre(AlmostEquals(-1.0 / 3.0 * Metre, 1),
                  AlmostEquals(2.0 / 3.0 * Metre, 0)));

  elements.semimajor_axis = 5.0 / 3.0 * Metre;
  system.Add(m2_, elements);
  // 2  1  2
  //    ^ barycentre
  EXPECT_EQ(5 * Kilogram, system.System().mass());
  EXPECT_THAT(x_positions(system),
              ElementsAre(-1 * Metre, 0 * Metre, 1 * Metre));

  elements.semimajor_axis = 6 * Metre;
  system.Add(m1_, elements);
  // 2  1  2  .  .  .  .  1
  //       ^ barycentre
  EXPECT_THAT(x_positions(system),
              ElementsAre(AlmostEquals(-2 * Metre, 0),
                          AlmostEquals(-1 * Metre, 0),
                          VanishesBefore(1 * Metre, 0),
                          5 * Metre));
}
TEST_F(BodyCentredNonRotatingDynamicFrameTest, Inverse) {
  int const steps = 100;
  for (Instant t = t0_; t < t0_ + 1 * period_; t += period_ / steps) {
    auto const from_big_frame_at_t = big_frame_->FromThisFrameAtTime(t);
    auto const to_big_frame_at_t = big_frame_->ToThisFrameAtTime(t);
    auto const small_initial_state_transformed_and_back =
        from_big_frame_at_t(to_big_frame_at_t(small_initial_state_));
    EXPECT_THAT(small_initial_state_transformed_and_back.position(),
                AlmostEquals(small_initial_state_.position(), 0, 1));
    EXPECT_THAT(small_initial_state_transformed_and_back.velocity(),
                AlmostEquals(small_initial_state_.velocity(), 0, 1));
  }
}
TEST_F(BodyCentredNonRotatingDynamicFrameTest, GeometricAcceleration) {
  int const steps = 10;
  RelativeDegreesOfFreedom<ICRFJ2000Equator> const initial_big_to_small =
      small_initial_state_ - big_initial_state_;
  Length const big_to_small = initial_big_to_small.displacement().Norm();
  Acceleration const small_on_big =
      small_gravitational_parameter_ / (big_to_small * big_to_small);
  for (Length y = big_to_small / steps;
       y < big_to_small;
       y += big_to_small / steps) {
    Position<Big> const position(Big::origin +
                                     Displacement<Big>({0 * Kilo(Metre),
                                                        y,
                                                        0 * Kilo(Metre)}));
    Acceleration const big_on_position =
        -big_gravitational_parameter_ / (y * y);
    Acceleration const small_on_position =
        small_gravitational_parameter_ /
            ((big_to_small - y) * (big_to_small - y));
    Vector<Acceleration, Big> const expected_acceleration(
                  {0 * SIUnit<Acceleration>(),
                   small_on_position + big_on_position - small_on_big,
                   0 * SIUnit<Acceleration>()});
    EXPECT_THAT(AbsoluteError(
                    big_frame_->GeometricAcceleration(
                        t0_,
                        DegreesOfFreedom<Big>(position, Velocity<Big>())),
                    expected_acceleration),
                Lt(1e-10 * SIUnit<Acceleration>()));
  }
}
예제 #5
0
TEST_F(TimeScalesTest, ReferenceDates) {
  EXPECT_THAT("1858-11-17T00:00:00"_TT, Eq(ModifiedJulianDate(0)));
  EXPECT_THAT(j2000_week, Eq(J2000));
  EXPECT_THAT(j2000_from_tt, Eq(J2000));
  EXPECT_THAT(j2000_from_tai, Eq(J2000));
  EXPECT_THAT(j2000_from_utc, Eq(J2000));
  EXPECT_THAT(j2000_tai, Eq(j2000_tai_from_tt));
  EXPECT_THAT(j2000_tai - J2000, Eq(32.184 * Second));

  // Besselian epochs.
  constexpr Instant B1900 = "1899-12-31T00:00:00"_TT + 0.8135 * Day;
  Instant const JD2415020_3135 = JulianDate(2415020.3135);
  EXPECT_THAT(B1900, AlmostEquals(JD2415020_3135, 51));
  EXPECT_THAT(testing_utilities::AbsoluteError(JD2415020_3135, B1900),
              AllOf(Ge(10 * Micro(Second)), Lt(100 * Micro(Second))));

  constexpr Instant B1950 = "1949-12-31T00:00:00"_TT + 0.9235 * Day;
  Instant const JD2433282_4235 = JulianDate(2433282.4235);
  EXPECT_THAT(B1950, AlmostEquals(JD2433282_4235, 26));
  EXPECT_THAT(testing_utilities::AbsoluteError(JD2433282_4235, B1950),
              AllOf(Ge(1 * Micro(Second)), Lt(10 * Micro(Second))));
}
예제 #6
0
TEST_F(PartTest, Serialization) {
  MockFunction<int(not_null<not_null<PileUp const*>>)>
      serialization_index_for_pile_up;
  EXPECT_CALL(serialization_index_for_pile_up, Call(_)).Times(0);

  serialization::Part message;
  part_.WriteToMessage(&message,
                       serialization_index_for_pile_up.AsStdFunction());
  EXPECT_EQ(part_id_, message.part_id());
  EXPECT_TRUE(message.has_mass());
  EXPECT_EQ(7, message.mass().magnitude());
  EXPECT_TRUE(message.has_intrinsic_force());
  EXPECT_TRUE(message.intrinsic_force().has_vector());
  EXPECT_EQ(8, message.intrinsic_force().vector().x().quantity().magnitude());
  EXPECT_EQ(9, message.intrinsic_force().vector().y().quantity().magnitude());
  EXPECT_EQ(10, message.intrinsic_force().vector().z().quantity().magnitude());
  EXPECT_TRUE(message.has_degrees_of_freedom());
  EXPECT_TRUE(message.degrees_of_freedom().t1().has_point());
  EXPECT_TRUE(message.degrees_of_freedom().t1().point().has_multivector());
  EXPECT_TRUE(message.degrees_of_freedom().t1().
                  point().multivector().has_vector());
  EXPECT_EQ(1, message.degrees_of_freedom().t1().
                   point().multivector().vector().x().quantity().magnitude());
  EXPECT_EQ(2, message.degrees_of_freedom().t1().
                   point().multivector().vector().y().quantity().magnitude());
  EXPECT_EQ(3, message.degrees_of_freedom().t1().
                   point().multivector().vector().z().quantity().magnitude());
  EXPECT_TRUE(message.degrees_of_freedom().t2().has_multivector());
  EXPECT_TRUE(message.degrees_of_freedom().t2().multivector().has_vector());
  EXPECT_EQ(4, message.degrees_of_freedom().t2().
                   multivector().vector().x().quantity().magnitude());
  EXPECT_EQ(5, message.degrees_of_freedom().t2().
                   multivector().vector().y().quantity().magnitude());
  EXPECT_EQ(6, message.degrees_of_freedom().t2().
                   multivector().vector().z().quantity().magnitude());
  EXPECT_EQ(1, message.prehistory().timeline_size());
  EXPECT_EQ(1, message.prehistory().children_size());
  EXPECT_EQ(1, message.prehistory().children(0).trajectories_size());
  EXPECT_EQ(1,
            message.prehistory().children(0).trajectories(0).timeline_size());

  auto const p = Part::ReadFromMessage(message, /*deletion_callback=*/nullptr);
  EXPECT_EQ(part_.mass(), p->mass());
  EXPECT_EQ(part_.intrinsic_force(), p->intrinsic_force());
  EXPECT_EQ(part_.degrees_of_freedom(), p->degrees_of_freedom());

  serialization::Part second_message;
  p->WriteToMessage(&second_message,
                    serialization_index_for_pile_up.AsStdFunction());
  EXPECT_THAT(message, EqualsProto(second_message));
}
예제 #7
0
TEST_F(TimeScalesTest, UT1Continuity) {
  // Continuity with TAI.  We have a fairly low resolution for UT1 at that time,
  // as well as high errors (~20 ms), and TAI was synchronized with UT2 anyway,
  // so it's not going to get much better than 100 ms.
  EXPECT_THAT(
      AbsoluteError("1958-01-01T00:00:00"_UT1, "1958-01-01T00:00:00"_TAI),
      Lt(100 * Milli(Second)));

  // Continuity at the beginning of the EOP C02 series.
  EXPECT_THAT(AbsoluteError("1961-12-31T23:59:59,000"_UT1,
                            "1961-12-31T23:59:58,967"_UTC),
              Lt(0.5 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1962-01-01T00:00:00,000"_UT1,
                            "1961-12-31T23:59:59,967"_UTC),
              Lt(0.5 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1962-01-01T00:00:00,033"_UT1,
                            "1962-01-01T00:00:00,000"_UTC),
              Lt(0.5 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1962-01-01T00:00:01,033"_UT1,
                            "1962-01-01T00:00:01,000"_UTC),
              Lt(0.5 * Milli(Second)));

  // Continuity across a stretchy UTC leap.
  EXPECT_THAT(AbsoluteError("1964-03-31T23:59:59,000"_UT1,
                            "1964-03-31T23:59:59,160"_UTC),
              Lt(0.5 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1964-03-31T23:59:59,900"_UT1,
                            "1964-03-31T23:59:60,060"_UTC),
              Lt(0.5 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1964-03-31T23:59:59,940"_UT1,
                            "1964-04-01T00:00:00,000"_UTC),
              Lt(0.5 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1964-04-01T00:00:00,000"_UT1,
                            "1964-04-01T00:00:00,060"_UTC),
              Lt(0.5 * Milli(Second)));
}
예제 #8
0
void ContinuousTrajectory<Frame>::Append(
    Instant const& time,
    DegreesOfFreedom<Frame> const& degrees_of_freedom) {
  // Consistency checks.
  if (first_time_) {
    Instant const t0;
    CHECK_GE(1,
             ULPDistance((last_points_.back().first + step_ - t0) /
                             SIUnit<Time>(),
                         (time - t0) / SIUnit<Time>()))
        << "Append at times that are not equally spaced";
  } else {
    first_time_ = time;
  }

  if (last_points_.size() == divisions) {
    // These vectors are static to avoid deallocation/reallocation each time we
    // go through this code path.
    static std::vector<Displacement<Frame>> q(divisions + 1);
    static std::vector<Velocity<Frame>> v(divisions + 1);
    q.clear();
    v.clear();

    for (auto const& pair : last_points_) {
      DegreesOfFreedom<Frame> const& degrees_of_freedom = pair.second;
      q.push_back(degrees_of_freedom.position() - Frame::origin);
      v.push_back(degrees_of_freedom.velocity());
    }
    q.push_back(degrees_of_freedom.position() - Frame::origin);
    v.push_back(degrees_of_freedom.velocity());

    ComputeBestNewhallApproximation(
        time, q, v, &ЧебышёвSeries<Displacement<Frame>>::NewhallApproximation);

    // Wipe-out the points that have just been incorporated in a series.
    last_points_.clear();
  }

  // Note that we only insert the new point in the map *after* computing the
  // approximation, because clearing the map is much more efficient than erasing
  // every element but one.
  last_points_.emplace_back(time, degrees_of_freedom);
}
예제 #9
0
TEST_F(TimeScalesTest, StretchyRates) {
  // Check that cancellations aren't destroying the test.
  EXPECT_NE("1961-01-01T00:00:00"_UTC + 1 * Minute / (1 - 150e-10),
            "1961-01-01T00:00:00"_UTC + 1 * Minute / (1 - 130e-10));

  quantities::Time utc_minute;
  utc_minute = 1 * Minute / (1 - 150e-10);
  EXPECT_THAT("1961-01-01T00:00:00"_UTC + utc_minute,
              Eq("1961-01-01T00:01:00"_UTC));
  EXPECT_THAT("1961-12-31T23:59:00"_UTC + utc_minute,
              Eq("1961-12-31T24:00:00"_UTC));

  utc_minute = 1 * Minute / (1 - 130e-10);
  EXPECT_THAT("1962-01-01T00:00:00"_UTC + utc_minute,
              Eq("1962-01-01T00:01:00"_UTC));
  EXPECT_THAT("1963-12-31T23:59:00"_UTC + utc_minute,
              Eq("1963-12-31T24:00:00"_UTC));

  utc_minute = 1 * Minute / (1 - 150e-10);
  EXPECT_THAT("1964-01-01T00:00:00"_UTC + utc_minute,
              Eq("1964-01-01T00:01:00"_UTC));
  EXPECT_THAT("1965-12-31T23:59:00"_UTC + utc_minute,
              AlmostEquals("1965-12-31T24:00:00"_UTC, 1));

  utc_minute = 1 * Minute / (1 - 300e-10);
  EXPECT_THAT("1966-01-01T00:00:00"_UTC + utc_minute,
              Eq("1966-01-01T00:01:00"_UTC));
  EXPECT_THAT("1971-12-31T23:58:00"_UTC + utc_minute,
              Eq("1971-12-31T23:59:00"_UTC));

  utc_minute = 1 * Minute;
  EXPECT_THAT("1972-01-01T00:00:00"_UTC + utc_minute,
              Eq("1972-01-01T00:01:00"_UTC));
  EXPECT_THAT("2000-01-01T00:00:00"_UTC + utc_minute,
              Eq("2000-01-01T00:01:00"_UTC));
}
예제 #10
0
// Check the times of the lunar eclipses in LunarEclipseTest.
TEST_F(TimeScalesTest, LunarEclipses) {
  EXPECT_THAT(AbsoluteError("1950-04-02T20:44:34.0"_TT,
                            "1950-04-02T20:44:04.8"_UT1),
              Lt(14 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1950-04-02T20:49:16.7"_TT,
                            "1950-04-02T20:48:47.5"_UT1),
              Lt(14 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1950-09-26T04:17:11.4"_TT,
                            "1950-09-26T04:16:42.1"_UT1),
              Lt(86 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1950-09-26T04:21:55.5"_TT,
                            "1950-09-26T04:21:26.1"_UT1),
              Lt(15 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1951-03-23T10:37:33.2"_TT,
                            "1951-03-23T10:37:03.7"_UT1),
              Lt(92 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1951-03-23T10:50:16.8"_TT,
                            "1951-03-23T10:49:47.3"_UT1),
              Lt(92 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1951-09-15T12:27:06.3"_TT,
                            "1951-09-15T12:26:36.6"_UT1),
              Lt(99 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1951-09-15T12:38:51.5"_TT,
                            "1951-09-15T12:38:21.8"_UT1),
              Lt(99 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1952-02-11T00:28:39.9"_TT,
                            "1952-02-11T00:28:10.0"_UT1),
              Lt(69 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1952-02-11T00:39:47.6"_TT,
                            "1952-02-11T00:39:17.7"_UT1),
              Lt(69 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1952-08-05T19:40:29.4"_TT,
                            "1952-08-05T19:39:59.3"_UT1),
              Lt(57 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("1952-08-05T19:47:54.8"_TT,
                            "1952-08-05T19:47:24.7"_UT1),
              Lt(57 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("2000-01-21T04:41:30.5"_TT,
                            "2000-01-21T04:40:26.7"_UT1),
              Lt(45 * Milli(Second)));
  EXPECT_THAT(AbsoluteError("2000-01-21T04:44:34.5"_TT,
                            "2000-01-21T04:43:30.6"_UT1),
              Lt(56 * Milli(Second)));

  EXPECT_THAT("2048-01-01T06:53:54.8"_TT - "2048-01-01T06:52:23.6"_TT,
              AlmostEquals(91.2 * Second, 3e6, 4e6));
  EXPECT_THAT("2048-01-01T06:58:19.8"_TT - "2048-01-01T06:56:48.6"_TT,
              AlmostEquals(91.2 * Second, 3e6, 4e6));
}
예제 #11
0
// See the list of steps at
// https://hpiers.obspm.fr/iers/bul/bulc/TimeSteps.history.
// Note that while the same file is used to check that the date string is valid
// with respect to positive or negative leap seconds, the actual conversion is
// based exclusively on https://hpiers.obspm.fr/iers/bul/bulc/UTC-TAI.history,
// so this provides some sort of cross-checking.
TEST_F(TimeScalesTest, StretchyLeaps) {
  EXPECT_THAT(AbsoluteError("1961-07-31T24:00:00,000"_UTC - 0.050 * Second,
                            "1961-07-31T23:59:59,900"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1961-08-01T00:00:00"_UTC, "1961-08-01T00:00:01,648"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1963-10-31T24:00:00,000"_UTC - 0.100 * Second,
                            "1963-10-31T23:59:60,000"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1963-11-01T00:00:00"_UTC, "1963-11-01T00:00:02,697"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1964-03-31T24:00:00,000"_UTC - 0.100 * Second,
                            "1964-03-31T23:59:60,000"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1964-04-01T00:00:00"_UTC, "1964-04-01T00:00:02,984"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1964-08-31T24:00:00,000"_UTC - 0.100 * Second,
                            "1964-08-31T23:59:60,000"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1964-09-01T00:00:00"_UTC, "1964-09-01T00:00:03,282"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1964-12-31T24:00:00,000"_UTC - 0.100 * Second,
                            "1964-12-31T23:59:60,000"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1965-01-01T00:00:00"_UTC, "1965-01-01T00:00:03,540"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1965-02-28T24:00:00,000"_UTC - 0.100 * Second,
                            "1965-02-28T23:59:60,000"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1965-03-01T00:00:00"_UTC, "1965-03-01T00:00:03,717"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1965-06-30T24:00:00,000"_UTC - 0.100 * Second,
                            "1965-06-30T23:59:60,000"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1965-07-01T00:00:00"_UTC, "1965-07-01T00:00:03,975"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1965-08-31T24:00:00,000"_UTC - 0.100 * Second,
                            "1965-08-31T23:59:60,000"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1965-09-01T00:00:00"_UTC, "1965-09-01T00:00:04,155"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1968-01-31T24:00:00,000"_UTC - 0.100 * Second,
                            "1968-01-31T23:59:59,800"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1968-02-01T00:00:00"_UTC, "1968-02-01T00:00:06,186"_TAI),
      Lt(0.5 * Milli(Second)));

  EXPECT_THAT(AbsoluteError("1971-12-31T24:00:00,000"_UTC - 0.107'7580 * Second,
                            "1971-12-31T23:59:60,000"_UTC),
              Lt(1 * Micro(Second)));
  EXPECT_THAT(
      AbsoluteError("1972-01-01T00:00:00"_UTC, "1972-01-01T00:00:10,000"_TAI),
      Lt(0.5 * Milli(Second)));
}
예제 #12
0
TEST_F(KeplerOrbitTest, EarthMoon) {
  SolarSystem<ICRFJ2000Equator> solar_system;
  solar_system.Initialize(
      SOLUTION_DIR / "astronomy" / "gravity_model.proto.txt",
      SOLUTION_DIR / "astronomy" /
          "initial_state_jd_2433282_500000000.proto.txt");
  auto const earth = SolarSystem<ICRFJ2000Equator>::MakeMassiveBody(
                         solar_system.gravity_model_message("Earth"));
  auto const moon = SolarSystem<ICRFJ2000Equator>::MakeMassiveBody(
                        solar_system.gravity_model_message("Moon"));
  // The numbers in the gravity models and those from the query above both come
  // from DE431, so the sums are the same up to round-off.
  EXPECT_THAT(
      earth->gravitational_parameter() + moon->gravitational_parameter(),
      AlmostEquals(
          4.0350323550225975e+05 * (Pow<3>(Kilo(Metre)) / Pow<2>(Second)), 1));
  Instant const date = JulianDate(2457397.500000000);
  KeplerianElements<ICRFJ2000Equator> elements;
  elements.eccentricity                = 4.772161502830355e-02;
  elements.semimajor_axis              = 3.870051955415476e+05 * Kilo(Metre);
  elements.inclination                 = 1.842335956339145e+01 * Degree;
  elements.longitude_of_ascending_node = 1.752118723367974e+00 * Degree;
  elements.argument_of_periapsis       = 3.551364385683149e+02 * Degree;
  elements.mean_anomaly                = 2.963020996150547e+02 * Degree;
  KeplerOrbit<ICRFJ2000Equator> moon_orbit(*earth, *moon, elements, date);
  Displacement<ICRFJ2000Equator> const expected_displacement(
      { 1.177367562036580e+05 * Kilo(Metre),
       -3.419908628150604e+05 * Kilo(Metre),
       -1.150659799281941e+05 * Kilo(Metre)});
  Velocity<ICRFJ2000Equator> const expected_velocity(
      {9.745048087261129e-01 * (Kilo(Metre) / Second),
       3.500672337210811e-01 * (Kilo(Metre) / Second),
       1.066306010215636e-01 * (Kilo(Metre) / Second)});
  EXPECT_THAT(moon_orbit.StateVectors(date).displacement(),
              AlmostEquals(expected_displacement, 13));
  EXPECT_THAT(moon_orbit.StateVectors(date).velocity(),
              AlmostEquals(expected_velocity, 12));
  EXPECT_THAT(*moon_orbit.elements_at_epoch().mean_motion,
              AlmostEquals(1.511718576836574e-04 * (Degree / Second), 2));

  elements.semimajor_axis = std::experimental::nullopt;
  elements.mean_motion = 1.511718576836574e-04 * (Degree / Second);
  KeplerOrbit<ICRFJ2000Equator> moon_orbit_n(*earth, *moon, elements, date);
  EXPECT_THAT(moon_orbit_n.StateVectors(date).displacement(),
              AlmostEquals(expected_displacement, 13, 15));
  EXPECT_THAT(moon_orbit_n.StateVectors(date).velocity(),
              AlmostEquals(expected_velocity, 12));

  KeplerOrbit<ICRFJ2000Equator> moon_orbit_from_state_vectors(
      *earth,
      *moon,
      {expected_displacement, expected_velocity},
      date);
  EXPECT_THAT(moon_orbit_from_state_vectors.elements_at_epoch().eccentricity,
              AlmostEquals(moon_orbit.elements_at_epoch().eccentricity, 8));
  EXPECT_THAT(*moon_orbit_from_state_vectors.elements_at_epoch().semimajor_axis,
              AlmostEquals(*moon_orbit.elements_at_epoch().semimajor_axis, 1));
  EXPECT_THAT(*moon_orbit_from_state_vectors.elements_at_epoch().mean_motion,
              AlmostEquals(*moon_orbit.elements_at_epoch().mean_motion, 1));
  EXPECT_THAT(moon_orbit_from_state_vectors.elements_at_epoch().inclination,
              AlmostEquals(moon_orbit.elements_at_epoch().inclination, 1));
  EXPECT_THAT(
      moon_orbit_from_state_vectors.elements_at_epoch()
          .longitude_of_ascending_node,
      AlmostEquals(moon_orbit.elements_at_epoch().longitude_of_ascending_node,
                   28));
  EXPECT_THAT(
      moon_orbit_from_state_vectors.elements_at_epoch().argument_of_periapsis,
      AlmostEquals(moon_orbit.elements_at_epoch().argument_of_periapsis, 6));
  EXPECT_THAT(moon_orbit_from_state_vectors.elements_at_epoch().mean_anomaly,
              AlmostEquals(moon_orbit.elements_at_epoch().mean_anomaly, 6));
}