Program Listing for File joint_orbit_clock_dynamics.cc

Return to documentation for file (dynamics/joint_orbit_clock_dynamics.cc)

#include "lupnt/dynamics/joint_orbit_clock_dynamics.h"

#include <algorithm>
#include <limits>

#include "lupnt/core/asset_factory.h"
#include "lupnt/core/constants.h"
#include "lupnt/core/error.h"
#include "lupnt/data/kernels.h"
#include "lupnt/devices/clock.h"
#include "lupnt/environment/solar_system.h"

namespace lupnt {

  namespace {
    Ptr<NumericalDynamics> CreateNumericalDynamics(Config& config) {
      std::string dyn_class = config["class"].as<std::string>();
      Ptr<Dynamics> dynamics = AssetFactory<Dynamics, Config&>::Create(dyn_class, config);
      Ptr<NumericalDynamics> numerical = std::dynamic_pointer_cast<NumericalDynamics>(dynamics);
      LUPNT_CHECK(numerical != nullptr, "Orbit dynamics must derive from NumericalDynamics",
                  "JointOrbitClockDynamics");
      return numerical;
    }
  }  // namespace

  JointOrbitClockDynamics::JointOrbitClockDynamics() : NumericalDynamics() {
    SetODE([this](Real t, const VecX& x) { return ComputeRates(t, State(x)); });
    clock_dynamics_ = MakePtr<ClockDynamics>();
  }

  JointOrbitClockDynamics::JointOrbitClockDynamics(Config& config) : NumericalDynamics(config) {
    SetODE([this](Real t, const VecX& x) { return ComputeRates(t, State(x)); });
    clock_dynamics_ = MakePtr<ClockDynamics>();

    if (config["orbit_dynamics"]) {
      Config orbit_config(config["orbit_dynamics"]);
      SetOrbitDynamics(CreateNumericalDynamics(orbit_config));
    }
    if (config["clock_dynamics"]) {
      Config clock_config(config["clock_dynamics"]);
      SetClockDynamics(MakePtr<ClockDynamics>(clock_config));
    }
    if (config["clock_bias_unit"]) {
      clock_dynamics_->SetClockBiasUnit(
          enum_cast<ClockBiasUnit>(config["clock_bias_unit"].as<std::string>()).value());
    } else if (config["bias_unit"]) {
      clock_dynamics_->SetClockBiasUnit(
          enum_cast<ClockBiasUnit>(config["bias_unit"].as<std::string>()).value());
    }
    if (config["use_clock_relativity"]) {
      use_clock_relativity_ = config["use_clock_relativity"].as<bool>();
    } else if (config["use_relativity"]) {
      use_clock_relativity_ = config["use_relativity"].as<bool>();
    }
    if (config["relativity_center_body"]) {
      SetRelativityCenterBody(
          enum_cast<BodyId>(config["relativity_center_body"].as<std::string>()).value());
    }
    if (config["reference_rate_offset"]) {
      reference_rate_offset_ = config["reference_rate_offset"].as<double>();
    }
    if (config["add_clock_noise"]) add_clock_noise_ = config["add_clock_noise"].as<bool>();
    if (config["frame"]) frame_ = enum_cast<Frame>(config["frame"].as<std::string>()).value();
  }

  void JointOrbitClockDynamics::SetOrbitDynamics(Ptr<NumericalDynamics> orbit_dynamics) {
    LUPNT_CHECK(orbit_dynamics != nullptr, "Orbit dynamics cannot be null",
                "JointOrbitClockDynamics");
    orbit_dynamics_ = std::move(orbit_dynamics);
    SetParams(orbit_dynamics_->GetParams());

    if (auto nbody = std::dynamic_pointer_cast<NBodyDynamics>(orbit_dynamics_)) {
      frame_ = nbody->GetFrame();
      units_ = nbody->GetUnits();
    }
  }

  void JointOrbitClockDynamics::SetClockDynamics(Ptr<ClockDynamics> clock_dynamics) {
    LUPNT_CHECK(clock_dynamics != nullptr, "Clock dynamics cannot be null",
                "JointOrbitClockDynamics");
    clock_dynamics_ = std::move(clock_dynamics);
  }

  BodyId JointOrbitClockDynamics::SelectRelativityCenter(Real t, const State& orbit_state) const {
    if (relativity_center_body_set_) return relativity_center_body_;
    if (frame_ != Frame::UNDEFINED) {
      BodyId frame_center = GetFrameCenter(frame_);
      if (frame_center != BodyId::SSB && frame_center != BodyId::SUN) return frame_center;
    }

    auto nbody = std::dynamic_pointer_cast<NBodyDynamics>(orbit_dynamics_);
    if (!nbody) return BodyId::EARTH;

    BodyId closest = BodyId::EARTH;
    Real closest_distance = std::numeric_limits<double>::infinity();
    for (const Body& body : nbody->GetBodies()) {
      if (body.id == BodyId::SSB || body.id == BodyId::SUN) continue;
      Vec6 rv_center = GetCenterState(t, body.id);
      Real distance = (orbit_state.head(3) - rv_center.head(3)).norm();
      if (distance < closest_distance) {
        closest_distance = distance;
        closest = body.id;
      }
    }
    return closest;
  }

  Vec6 JointOrbitClockDynamics::GetCenterState(Real t, BodyId center_body) const {
    LUPNT_CHECK(frame_ != Frame::UNDEFINED, "Frame must be set for relativistic clock coupling",
                "JointOrbitClockDynamics");
    BodyId frame_center = GetFrameCenter(frame_);
    if (frame_center == center_body) return Vec6::Zero();
    Real t_tdb = t + GetLupntEpoch();
    return GetBodyPosVel(t_tdb, center_body, frame_, units_);
  }

  Real JointOrbitClockDynamics::RelativisticRate(Real t, const State& orbit_state) const {
    BodyId center_body = SelectRelativityCenter(t, orbit_state);
    BodyData center_data = GetBodyData(center_body, units_);
    PhysicalConstants constants = GetPhysicalConstants(units_);
    Vec6 rv_center = GetCenterState(t, center_body);
    return ClockDynamics::RelativisticRateCorrection(
        orbit_state.head(3) - rv_center.head(3), orbit_state.tail(3) - rv_center.tail(3),
        center_data.GM, constants.C, reference_rate_offset_);
  }

  State JointOrbitClockDynamics::BuildState(const State& x0, const VecX& values) const {
    JointOrbitClockState xf(x0);
    xf.head(values.size()) = values;
    std::vector<std::string> units = xf.GetUnits();
    std::vector<std::string> clock_units = ClockDynamics::GetStateUnits(
        values.size() - ORBIT_STATE_SIZE, clock_dynamics_->GetClockBiasUnit());
    std::copy(clock_units.begin(), clock_units.end(), units.begin() + ORBIT_STATE_SIZE);
    xf.SetUnits(units);
    if (frame_ != Frame::UNDEFINED) xf.SetFrame(frame_);
    return xf;
  }

  State JointOrbitClockDynamics::Propagate(const State& x0, Real t0, Real tf, const State* u) {
    LUPNT_CHECK(orbit_dynamics_ != nullptr, "Orbit dynamics must be set",
                "JointOrbitClockDynamics");
    LUPNT_CHECK(clock_dynamics_ != nullptr, "Clock dynamics must be set",
                "JointOrbitClockDynamics");
    LUPNT_CHECK(u == nullptr, "JointOrbitClockDynamics does not support control inputs",
                "JointOrbitClockDynamics");
    if (std::abs((tf - t0).val()) < EPS) return BuildState(x0, x0);

    State x_prop = NumericalDynamics::Propagate(x0, t0, tf, nullptr);
    VecX values = x_prop;
    int clock_state_size = x0.size() - ORBIT_STATE_SIZE;
    if (add_clock_noise_ && clock_dynamics_->GetModel() != ClockModel::UNDEFINED) {
      values.tail(clock_state_size) += clock_dynamics_->GetProcessNoise(tf - t0, clock_state_size);
    }
    return BuildState(x0, values);
  }

  State JointOrbitClockDynamics::PropagateWithParams(const State& x0, Real t0, Real tf,
                                                     const ParamState& params, const State* u) {
    LUPNT_CHECK(orbit_dynamics_ != nullptr, "Orbit dynamics must be set",
                "JointOrbitClockDynamics");
    orbit_dynamics_->SetParams(params);
    SetParams(params);
    return Propagate(x0, t0, tf, u);
  }

  VecX JointOrbitClockDynamics::ComputeRates(Real t, const State& x) const {
    LUPNT_CHECK(orbit_dynamics_ != nullptr, "Orbit dynamics must be set",
                "JointOrbitClockDynamics");
    LUPNT_CHECK(clock_dynamics_ != nullptr, "Clock dynamics must be set",
                "JointOrbitClockDynamics");
    LUPNT_CHECK(x.size() == 8 || x.size() == 9, "State size must be 8 or 9",
                "JointOrbitClockDynamics");

    int clock_state_size = x.size() - ORBIT_STATE_SIZE;
    State orbit_state = x.head(ORBIT_STATE_SIZE);
    orbit_state.SetFrame(frame_ == Frame::UNDEFINED ? x.GetFrame() : frame_);

    VecX orbit_rates = orbit_dynamics_->ComputeRates(t, orbit_state);
    Real rel_rate = 0.0;
    if (use_clock_relativity_) {
      rel_rate = RelativisticRate(t, orbit_state);
    }
    Real rel_bias_rate
        = ClockDynamics::SecondsToBiasUnits(rel_rate, clock_dynamics_->GetClockBiasUnit());

    VecX clock_rates(clock_state_size);
    clock_rates(0) = x(ORBIT_STATE_SIZE + 1) + rel_bias_rate;
    if (clock_state_size == 2) {
      clock_rates(1) = 0.0;
    } else {
      clock_rates(1) = x(ORBIT_STATE_SIZE + 2);
      clock_rates(2) = 0.0;
    }

    VecX rates(x.size());
    rates.head(ORBIT_STATE_SIZE) = orbit_rates;
    rates.tail(clock_state_size) = clock_rates;
    return rates;
  }

  REGISTER_FACTORY_CLASS(Dynamics, JointOrbitClockDynamics)

}  // namespace lupnt