<commit_before>﻿
#include "ksp_plugin/interface.hpp"

#include "base/not_null.hpp"
#include "geometry/named_quantities.hpp"
#include "glog/logging.h"
#include "journal/method.hpp"
#include "journal/profiles.hpp"
#include "ksp_plugin/burn.hpp"
#include "ksp_plugin/flight_plan.hpp"
#include "ksp_plugin/vessel.hpp"
#include "quantities/constants.hpp"
#include "quantities/si.hpp"

namespace principia {

using base::not_null;
using geometry::Instant;
using ksp_plugin::Barycentric;
using ksp_plugin::FlightPlan;
using ksp_plugin::Navigation;
using ksp_plugin::NavigationManœuvre;
using ksp_plugin::Vessel;
using ksp_plugin::WorldSun;
using quantities::constants::StandardGravity;
using quantities::si::Kilo;
using quantities::si::Kilogram;
using quantities::si::Metre;
using quantities::si::Newton;
using quantities::si::Second;
using quantities::si::Tonne;

namespace interface {

namespace {

not_null<Vessel*> GetVessel(Plugin const* const plugin,
                            char const* const vessel_guid) {
  // TODO(phl): Should this check if the vessel is synchronized/initialized?
  CHECK(CHECK_NOTNULL(plugin)->HasVessel(vessel_guid)) << vessel_guid;
  return plugin->GetVessel(vessel_guid);
}

FlightPlan& GetFlightPlan(Plugin const* const plugin,
                          char const* const vessel_guid) {
  Vessel const& vessel = *GetVessel(plugin, vessel_guid);
  CHECK(vessel.has_flight_plan()) << vessel_guid;
  return *vessel.flight_plan();
}

Burn GetBurn(NavigationManœuvre const& manœuvre) {
  Velocity<Frenet<NavigationFrame>> const Δv =
      manœuvre.Δv() == Speed() ? Velocity<Frenet<NavigationFrame>>()
                               : manœuvre.Δv() * manœuvre.direction();
  return {manœuvre.thrust() / Kilo(Newton),
          manœuvre.specific_impulse() / (Second * StandardGravity),
          principia__GetNavigationFrameParameters(manœuvre.frame()),
          (manœuvre.initial_time() - Instant()) / Second,
          ToXYZ(Δv.coordinates() / (Metre / Second))};
}

ksp_plugin::Burn ToBurn(Plugin const* const plugin, Burn const& burn) {
  return {burn.thrust_in_kilonewtons * Kilo(Newton),
          burn.specific_impulse_in_seconds_g0 * Second * StandardGravity,
          base::check_not_null(std::unique_ptr<NavigationFrame>(
              principia__NewNavigationFrame(plugin, burn.frame))),
          Instant() + burn.initial_time * Second,
          Velocity<Frenet<Navigation>>(
              ToR3Element(burn.delta_v) * (Metre / Second))};
}

NavigationManoeuvre ToNavigationManoeuvre(Plugin const* const plugin,
                                          NavigationManœuvre const& manœuvre) {
  OrthogonalMap<Barycentric, World> const barycentric_to_world =
      OrthogonalMap<WorldSun, World>::Identity() *
      plugin->BarycentricToWorldSun();
  NavigationManoeuvre result;
  result.burn = GetBurn(manœuvre);
  result.initial_mass_in_tonnes = manœuvre.initial_mass() / Tonne;
  result.final_mass_in_tonnes = manœuvre.final_mass() / Tonne;
  result.mass_flow = manœuvre.mass_flow() / (Kilogram / Second);
  result.duration = manœuvre.duration() / Second;
  result.final_time = (manœuvre.final_time() - Instant()) / Second;
  result.time_of_half_delta_v =
      (manœuvre.time_of_half_Δv() - Instant()) / Second;
  result.time_to_half_delta_v = manœuvre.time_to_half_Δv() / Second;
  Vector<double, Barycentric> const barycentric_inertial_direction =
      manœuvre.InertialDirection();
  Vector<double, World> const world_inertial_direction =
      barycentric_to_world(barycentric_inertial_direction);
  result.inertial_direction = ToXYZ(world_inertial_direction.coordinates());
  OrthogonalMap<Frenet<Navigation>, Barycentric> barycentric_frenet_frame =
      manœuvre.FrenetFrame();
  OrthogonalMap<Frenet<Navigation>, World> world_frenet_frame =
      barycentric_to_world * barycentric_frenet_frame;
  result.tangent =
      ToXYZ(world_frenet_frame(Vector<double, Frenet<Navigation>>({1, 0, 0}))
                .coordinates());
  result.normal =
      ToXYZ(world_frenet_frame(Vector<double, Frenet<Navigation>>({0, 1, 0}))
                .coordinates());
  result.binormal =
      ToXYZ(world_frenet_frame(Vector<double, Frenet<Navigation>>({0, 0, 1}))
                .coordinates());
  return result;
}

}  // namespace

bool principia__FlightPlanAppend(Plugin const* const plugin,
                                 char const* const vessel_guid,
                                 Burn const burn) {
  journal::Method<journal::FlightPlanAppend> m({plugin, vessel_guid, burn});
  return m.Return(GetFlightPlan(plugin, vessel_guid).
                      Append(ToBurn(plugin, burn)));
}

void principia__FlightPlanCreate(Plugin const* const plugin,
                                 char const* const vessel_guid,
                                 double const final_time,
                                 double const mass_in_tonnes) {
  journal::Method<journal::FlightPlanCreate> m({plugin,
                                                vessel_guid,<commit_msg>Plotted Frenet frame. Perhaps we should distinguish PlottedWorld and World on the C++ side, this sort of confusion *should not* happen<commit_after>﻿
#include "ksp_plugin/interface.hpp"

#include "base/not_null.hpp"
#include "geometry/named_quantities.hpp"
#include "glog/logging.h"
#include "journal/method.hpp"
#include "journal/profiles.hpp"
#include "ksp_plugin/burn.hpp"
#include "ksp_plugin/flight_plan.hpp"
#include "ksp_plugin/vessel.hpp"
#include "quantities/constants.hpp"
#include "quantities/si.hpp"

namespace principia {

using base::not_null;
using geometry::Instant;
using ksp_plugin::Barycentric;
using ksp_plugin::FlightPlan;
using ksp_plugin::Navigation;
using ksp_plugin::NavigationManœuvre;
using ksp_plugin::Vessel;
using ksp_plugin::WorldSun;
using quantities::constants::StandardGravity;
using quantities::si::Kilo;
using quantities::si::Kilogram;
using quantities::si::Metre;
using quantities::si::Newton;
using quantities::si::Second;
using quantities::si::Tonne;

namespace interface {

namespace {

not_null<Vessel*> GetVessel(Plugin const* const plugin,
                            char const* const vessel_guid) {
  // TODO(phl): Should this check if the vessel is synchronized/initialized?
  CHECK(CHECK_NOTNULL(plugin)->HasVessel(vessel_guid)) << vessel_guid;
  return plugin->GetVessel(vessel_guid);
}

FlightPlan& GetFlightPlan(Plugin const* const plugin,
                          char const* const vessel_guid) {
  Vessel const& vessel = *GetVessel(plugin, vessel_guid);
  CHECK(vessel.has_flight_plan()) << vessel_guid;
  return *vessel.flight_plan();
}

Burn GetBurn(NavigationManœuvre const& manœuvre) {
  Velocity<Frenet<NavigationFrame>> const Δv =
      manœuvre.Δv() == Speed() ? Velocity<Frenet<NavigationFrame>>()
                               : manœuvre.Δv() * manœuvre.direction();
  return {manœuvre.thrust() / Kilo(Newton),
          manœuvre.specific_impulse() / (Second * StandardGravity),
          principia__GetNavigationFrameParameters(manœuvre.frame()),
          (manœuvre.initial_time() - Instant()) / Second,
          ToXYZ(Δv.coordinates() / (Metre / Second))};
}

ksp_plugin::Burn ToBurn(Plugin const* const plugin, Burn const& burn) {
  return {burn.thrust_in_kilonewtons * Kilo(Newton),
          burn.specific_impulse_in_seconds_g0 * Second * StandardGravity,
          base::check_not_null(std::unique_ptr<NavigationFrame>(
              principia__NewNavigationFrame(plugin, burn.frame))),
          Instant() + burn.initial_time * Second,
          Velocity<Frenet<Navigation>>(
              ToR3Element(burn.delta_v) * (Metre / Second))};
}

NavigationManoeuvre ToNavigationManoeuvre(Plugin const* const plugin,
                                          NavigationManœuvre const& manœuvre) {
  OrthogonalMap<Barycentric, World> const barycentric_to_world =
      OrthogonalMap<WorldSun, World>::Identity() *
      plugin->BarycentricToWorldSun();
  NavigationManoeuvre result;
  result.burn = GetBurn(manœuvre);
  result.initial_mass_in_tonnes = manœuvre.initial_mass() / Tonne;
  result.final_mass_in_tonnes = manœuvre.final_mass() / Tonne;
  result.mass_flow = manœuvre.mass_flow() / (Kilogram / Second);
  result.duration = manœuvre.duration() / Second;
  result.final_time = (manœuvre.final_time() - Instant()) / Second;
  result.time_of_half_delta_v =
      (manœuvre.time_of_half_Δv() - Instant()) / Second;
  result.time_to_half_delta_v = manœuvre.time_to_half_Δv() / Second;
  Vector<double, Barycentric> const barycentric_inertial_direction =
      manœuvre.InertialDirection();
  Vector<double, World> const world_inertial_direction =
      barycentric_to_world(barycentric_inertial_direction);
  result.inertial_direction = ToXYZ(world_inertial_direction.coordinates());

  OrthogonalMap<Frenet<Navigation>, Barycentric> frenet_to_barycentric =
      manœuvre.FrenetFrame();
  Instant const current_time = plugin->CurrentTime();
  Instant const initial_time = manœuvre.initial_time();
  auto const plotting_frame = plugin->GetPlottingFrame();
  OrthogonalMap<Frenet<Navigation>, World> frenet_to_plotted_world =
      barycentric_to_world *
      plotting_frame->FromThisFrameAtTime(current_time).orthogonal_map() *
      plotting_frame->ToThisFrameAtTime(initial_time).orthogonal_map() *
      frenet_to_barycentric;
  result.tangent = ToXYZ(
      frenet_to_plotted_world(Vector<double, Frenet<Navigation>>({1, 0, 0}))
          .coordinates());
  result.normal = ToXYZ(
      frenet_to_plotted_world(Vector<double, Frenet<Navigation>>({0, 1, 0}))
          .coordinates());
  result.binormal = ToXYZ(
      frenet_to_plotted_world(Vector<double, Frenet<Navigation>>({0, 0, 1}))
          .coordinates());
  return result;
}

}  // namespace

bool principia__FlightPlanAppend(Plugin const* const plugin,
                                 char const* const vessel_guid,
                                 Burn const burn) {
  journal::Method<journal::FlightPlanAppend> m({plugin, vessel_guid, burn});
  return m.Return(GetFlightPlan(plugin, vessel_guid).
                      Append(ToBurn(plugin, burn)));
}

void principia__FlightPlanCreate(Plugin const* const plugin,
                                 char const* const vessel_guid,
                                 double const final_time,
                                 double const mass_in_tonnes) {
  journal::Method<journal::FlightPlanCreate> m({plugin,
                                                vessel_guid,
{'fraction_non_alphanumeric': 0.1117056856187291, 'fraction_numerical': 0.00210224558050645, 'mean_word_length': 3.6806797853309483, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5199481', 'n_tokens_mistral': 3296, 'n_tokens_neox': 3230, 'n_words': 578}