TEST_F(BodyCentredNonRotatingDynamicFrameTest, GeometricAcceleration) { int const kSteps = 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 / kSteps; y < big_to_small; y += big_to_small / kSteps) { 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>())); } }
// Check that the small body has the right degrees of freedom in the frame of // the big body. TEST_F(BodyCentredNonRotatingDynamicFrameTest, SmallBodyInBigFrame) { int const kSteps = 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_ / kSteps) { DegreesOfFreedom<ICRFJ2000Equator> const small_in_inertial_frame_at_t = solar_system_.trajectory(*ephemeris_, kSmall). EvaluateDegreesOfFreedom(t, &hint); auto const rotation_in_big_frame_at_t = Rotation<ICRFJ2000Equator, Big>(-2 * π * (t - t0_) * Radian / period_, axis); 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)); } }
// Calls |plugin->CelestialFromParent| with the arguments given. // |plugin| must not be null. No transfer of ownership. QP principia__CelestialFromParent(Plugin const* const plugin, int const celestial_index) { journal::Method<journal::CelestialFromParent> m({plugin, celestial_index}); CHECK_NOTNULL(plugin); RelativeDegreesOfFreedom<AliceSun> const result = plugin->CelestialFromParent(celestial_index); return m.Return({ToXYZ(result.displacement().coordinates() / Metre), ToXYZ(result.velocity().coordinates() / (Metre / Second))}); }
// Calls |plugin->VesselFromParent| with the arguments given. // |plugin| must not be null. No transfer of ownership. QP principia__VesselFromParent(Plugin const* const plugin, char const* const vessel_guid) { journal::Method<journal::VesselFromParent> m({plugin, vessel_guid}); CHECK_NOTNULL(plugin); RelativeDegreesOfFreedom<AliceSun> const result = plugin->VesselFromParent(vessel_guid); return m.Return({ToXYZ(result.displacement().coordinates() / Metre), ToXYZ(result.velocity().coordinates() / (Metre / Second))}); }
std::string DebugString( RelativeDegreesOfFreedom<Frame> const& relative_degrees_of_freedom) { return "{" + DebugString(relative_degrees_of_freedom.displacement()) + ", " + DebugString(relative_degrees_of_freedom.velocity()) + "}"; }
void Ephemeris<Frame>::ComputeApsides(not_null<MassiveBody const*> const body1, not_null<MassiveBody const*> const body2, DiscreteTrajectory<Frame>& apoapsides1, DiscreteTrajectory<Frame>& periapsides1, DiscreteTrajectory<Frame>& apoapsides2, DiscreteTrajectory<Frame>& periapsides2) { not_null<ContinuousTrajectory<Frame> const*> const body1_trajectory = trajectory(body1); not_null<ContinuousTrajectory<Frame> const*> const body2_trajectory = trajectory(body2); typename ContinuousTrajectory<Frame>::Hint hint1; typename ContinuousTrajectory<Frame>::Hint hint2; // Computes the derivative of the squared distance between |body1| and |body2| // at time |t|. auto const evaluate_square_distance_derivative = [body1_trajectory, body2_trajectory, &hint1, &hint2]( Instant const& t) -> Variation<Square<Length>> { DegreesOfFreedom<Frame> const body1_degrees_of_freedom = body1_trajectory->EvaluateDegreesOfFreedom(t, &hint1); DegreesOfFreedom<Frame> const body2_degrees_of_freedom = body2_trajectory->EvaluateDegreesOfFreedom(t, &hint2); RelativeDegreesOfFreedom<Frame> const relative = body1_degrees_of_freedom - body2_degrees_of_freedom; return 2.0 * InnerProduct(relative.displacement(), relative.velocity()); }; std::experimental::optional<Instant> previous_time; std::experimental::optional<Variation<Square<Length>>> previous_squared_distance_derivative; for (Instant time = t_min(); time <= t_max(); time += parameters_.step()) { Variation<Square<Length>> const squared_distance_derivative = evaluate_square_distance_derivative(time); if (previous_squared_distance_derivative && Sign(squared_distance_derivative) != Sign(*previous_squared_distance_derivative)) { CHECK(previous_time); // The derivative of |squared_distance| changed sign. Find its zero by // bisection, this is the time of the apsis. Then compute the apsis and // append it to one of the output trajectories. Instant const apsis_time = Bisect(evaluate_square_distance_derivative, *previous_time, time); DegreesOfFreedom<Frame> const apsis1_degrees_of_freedom = body1_trajectory->EvaluateDegreesOfFreedom(apsis_time, &hint1); DegreesOfFreedom<Frame> const apsis2_degrees_of_freedom = body2_trajectory->EvaluateDegreesOfFreedom(apsis_time, &hint2); if (Sign(squared_distance_derivative).Negative()) { apoapsides1.Append(apsis_time, apsis1_degrees_of_freedom); apoapsides2.Append(apsis_time, apsis2_degrees_of_freedom); } else { periapsides1.Append(apsis_time, apsis1_degrees_of_freedom); periapsides2.Append(apsis_time, apsis2_degrees_of_freedom); } } previous_time = time; previous_squared_distance_derivative = squared_distance_derivative; } }
void Ephemeris<Frame>::ComputeApsides( not_null<MassiveBody const*> const body, typename DiscreteTrajectory<Frame>::Iterator const begin, typename DiscreteTrajectory<Frame>::Iterator const end, DiscreteTrajectory<Frame>& apoapsides, DiscreteTrajectory<Frame>& periapsides) { not_null<ContinuousTrajectory<Frame> const*> const body_trajectory = trajectory(body); typename ContinuousTrajectory<Frame>::Hint hint; std::experimental::optional<Instant> previous_time; std::experimental::optional<DegreesOfFreedom<Frame>> previous_degrees_of_freedom; std::experimental::optional<Square<Length>> previous_squared_distance; std::experimental::optional<Variation<Square<Length>>> previous_squared_distance_derivative; for (auto it = begin; it != end; ++it) { Instant const time = it.time(); DegreesOfFreedom<Frame> const degrees_of_freedom = it.degrees_of_freedom(); DegreesOfFreedom<Frame> const body_degrees_of_freedom = body_trajectory->EvaluateDegreesOfFreedom(time, &hint); RelativeDegreesOfFreedom<Frame> const relative = degrees_of_freedom - body_degrees_of_freedom; Square<Length> const squared_distance = InnerProduct(relative.displacement(), relative.displacement()); // This is the derivative of |squared_distance|. Variation<Square<Length>> const squared_distance_derivative = 2.0 * InnerProduct(relative.displacement(), relative.velocity()); if (previous_squared_distance_derivative && Sign(squared_distance_derivative) != Sign(*previous_squared_distance_derivative)) { CHECK(previous_time && previous_degrees_of_freedom && previous_squared_distance); // The derivative of |squared_distance| changed sign. Construct a Hermite // approximation of |squared_distance| and find its extrema. Hermite3<Instant, Square<Length>> const squared_distance_approximation( {*previous_time, time}, {*previous_squared_distance, squared_distance}, {*previous_squared_distance_derivative, squared_distance_derivative}); std::set<Instant> const extrema = squared_distance_approximation.FindExtrema(); // Now look at the extrema and check that exactly one is in the required // time interval. This is normally the case, but it can fail due to // ill-conditioning. Instant apsis_time; int valid_extrema = 0; for (auto const& extremum : extrema) { if (extremum >= *previous_time && extremum <= time) { apsis_time = extremum; ++valid_extrema; } } if (valid_extrema != 1) { // Something went wrong when finding the extrema of // |squared_distance_approximation|. Use a linear interpolation of // |squared_distance_derivative| instead. apsis_time = Barycentre<Instant, Variation<Square<Length>>>( {time, *previous_time}, {*previous_squared_distance_derivative, -squared_distance_derivative}); } // Now that we know the time of the apsis, construct a Hermite // approximation of the position of the body, and use it to derive its // degrees of freedom. Note that an extremum of // |squared_distance_approximation| is in general not an extremum for // |position_approximation|: the distance computed using the latter is a // 6th-degree polynomial. However, approximating this polynomial using a // 3rd-degree polynomial would yield |squared_distance_approximation|, so // we shouldn't be far from the truth. Hermite3<Instant, Position<Frame>> position_approximation( {*previous_time, time}, {previous_degrees_of_freedom->position(), degrees_of_freedom.position()}, {previous_degrees_of_freedom->velocity(), degrees_of_freedom.velocity()}); DegreesOfFreedom<Frame> const apsis_degrees_of_freedom( position_approximation.Evaluate(apsis_time), position_approximation.EvaluateDerivative(apsis_time)); if (Sign(squared_distance_derivative).Negative()) { apoapsides.Append(apsis_time, apsis_degrees_of_freedom); } else { periapsides.Append(apsis_time, apsis_degrees_of_freedom); } } previous_time = time; previous_degrees_of_freedom = degrees_of_freedom; previous_squared_distance = squared_distance; previous_squared_distance_derivative = squared_distance_derivative; } }