// Compute and log the measured periods of the moons. void LogPeriods(Ephemeris<KSP> const& ephemeris) { auto const position = [this, &ephemeris]( not_null<MassiveBody const*> body, Instant const& t) { return ephemeris.trajectory(body)->EvaluatePosition(t, nullptr); }; auto const barycentre = [this, &position](Instant const& t) { BarycentreCalculator<Position<KSP>, Mass> result; for (auto const body : jool_system_) { result.Add(position(body, t), body->mass()); } return result.Get(); }; auto const barycentric_position = [this, &barycentre, &ephemeris, &position]( not_null<MassiveBody const*> body, Instant const& t) { return position(body, t) - barycentre(t); }; for (auto const moon : {laythe_, vall_, tylo_}) { auto const moon_y = [this, &barycentric_position, moon](Instant const& t) { return barycentric_position(moon, t).coordinates().y; }; LOG(INFO) << (moon == laythe_ ? "Laythe" : moon == vall_ ? "Vall" : "Tylo"); Sign const s0(moon_y(game_epoch_)); Instant t0 = game_epoch_; Time const Δt = 45 * Minute; while (Sign(moon_y(t0)) == s0) { t0 += Δt; } // The moon crosses the xz plane between t0 and t0 - Δt. Instant t1 = t0; int const orbits = moon == laythe_ ? 8 : moon == vall_ ? 4 : 2; for (int i = 0; i < orbits; ++i) { while (Sign(moon_y(t1)) != s0) { t1 += Δt; } // The crossing of the xz plane halfway through the orbit occurs between // t1 and t1 - Δt. while (Sign(moon_y(t1)) == s0) { t1 += Δt; } // The |i|th orbit ends between t1 and t1 - Δt. } Time const actual_period = (Bisect(moon_y, t1 - Δt, t1) - Bisect(moon_y, t0 - Δt, t0)) / orbits; Time const expected_period = (2 * π * Radian) / *elements_[moon].mean_motion; LOG(INFO) << "actual period : " << actual_period; LOG(INFO) << "expected period : " << expected_period; LOG(INFO) << "error : " << RelativeError(expected_period, actual_period); } }
void LogEphemeris(Ephemeris<KSP> const& ephemeris, bool const reference, std::string const& name) { Instant const begin = reference ? game_epoch_ : long_time_; Instant const end = reference ? reference_ : comparison_; std::string const purpose = reference ? "reference" : "comparison"; // Mathematica tends to be slow when dealing with quantities, so we give // everything in SI units. std::vector<double> times; // Indexed chronologically, then by body. std::vector<std::vector<Vector<double, KSP>>> barycentric_positions; for (Instant t = begin; t < end; t += 45 * Minute) { auto const position = [&ephemeris, t](not_null<MassiveBody const*> body) { return ephemeris.trajectory(body)-> EvaluatePosition(t, /*hint=*/nullptr); }; times.emplace_back((t - game_epoch_) / Second); BarycentreCalculator<Position<KSP>, GravitationalParameter> jool_system_barycentre; for (auto const body : jool_system_) { jool_system_barycentre.Add(position(body), body->gravitational_parameter()); } barycentric_positions.emplace_back(); for (auto const body : jool_system_) { // TODO(egg): when our dynamic frames support that, it would make sense // to use a nonrotating dynamic frame centred at the barycentre of the // Jool system, instead of computing the barycentre and difference // ourselves. barycentric_positions.back().emplace_back( (position(body) - jool_system_barycentre.Get()) / Metre); } } std::ofstream file; file.open(name + "_" + purpose + ".generated.wl"); file << mathematica::Assign(name + purpose + "q", barycentric_positions); file << mathematica::Assign(name + purpose + "t", times); file.close(); }