Program Listing for File occultation.cc

Return to documentation for file (environment/occultation.cc)

#include "lupnt/environment/occultation.h"

#include "lupnt/conversions/frame_converter.h"
#include "lupnt/core/constants.h"
#include "lupnt/data/kernels.h"
#include "lupnt/environment/body.h"

namespace lupnt {

  std::map<std::string, bool> Occultation::ComputeOccultation(
      Real epoch, const Vec3& r1, const Vec3& r2, Frame frame1, Frame frame2,
      const std::vector<BodyId>& bodies, const VecXd& height_atm, const VecXd& min_elevation,
      const bool use_elev_mask1 = false, const bool use_elev_mask2 = false) {
    // Check if the input vectors have the same size
    if (bodies.size() != height_atm.size())
      throw std::runtime_error("bodies and height_atm must have the same size");

    Vec3 r1_icrf = ConvertFrame(epoch, r1, frame1, Frame::ICRF);
    Vec3 r2_icrf = ConvertFrame(epoch, r2, frame2, Frame::ICRF);

    // Compute distance between line of sight and center of each body
    std::map<std::string, bool> vis;
    for (auto i = 0; i < bodies.size(); i++) {
      BodyData body_data = GetBodyData(bodies[i]);

      Vec3d rb = GetBodyPosVel(epoch, BodyId::SOLAR_SYSTEM_BARYCENTER, bodies[i], Frame::GCRF)
                     .head(3)
                     .cast<double>();
      Vec3d r12 = (r2_icrf - r1_icrf).cast<double>();  // 1->2
      Vec3d r1b = (rb - r1_icrf).cast<double>();       // 1->body
      Vec3d r2b = (rb - r2_icrf).cast<double>();       // 2->body

      // If the transmitter or receiver is inside the atmosphere, continue to elevation mask check
      // (Likely they are ground users)

      // Compute angle between (tx->Body center) and (tx->rx)
      double r1b_norm = r1b.norm();
      double r12_norm = r12.norm();
      double alpha_body = acos(r12.dot(r1b) / (r1b_norm * r12_norm));

      // Agent 1 or Agent 2 is ground user
      if ((r1b.norm() < height_atm(i)) || (r2b.norm() < height_atm(i))) {
        vis[body_data.name] = true;

        // Case 1: Agent 1 is ground user
        if ((use_elev_mask1) && (r1b.norm() < height_atm(i))) {
          // Angle between body->1->2
          if ((alpha_body - PI / 2.0) < min_elevation[i]) {
            vis[body_data.name] = false;
          }
        }
        // Case 2: Agent 2 is ground user
        else if ((use_elev_mask2) && (r2b.norm() < height_atm(i))) {
          // Angle between body->2->1
          // double b21 = acos(r2b.dot(-r12) / (r2b.norm() * r12.norm()));
          if ((alpha_body - PI / 2.0) < min_elevation[i]) {
            vis[body_data.name] = false;
          }
        }

        // Then, skip the rest of the occultation check for this body
        continue;
      }

      // Compute angle between (tx>Body center) and (tx->horizon)
      double R_body = GetBodyRadius(bodies[i]) + height_atm(i);
      double beta_body = asin(R_body / r1b_norm);

      // Compute occultation (alpha_body < beta and r12_norm > r1b_norm *
      // cos(beta))
      bool is_occult = (alpha_body < beta_body) && (r12_norm > r1b_norm * cos(beta_body));
      bool is_vis = !is_occult;

      vis[body_data.name] = is_vis;
    }

    vis["all"] = std::all_of(vis.begin(), vis.end(),
                             [](const std::pair<std::string, bool>& p) { return p.second; });
    return vis;
  }

  // MatX = func(real, Mat<-1, 3>, Mat<-1, 3>)
  std::vector<std::map<std::string, bool>> Occultation::ComputeOccultation(
      Real epoch, const Mat<-1, 3>& r1, const Mat<-1, 3>& r2, Frame frame1, Frame frame2,
      const std::vector<BodyId>& bodies, const VecXd& height_atm, const VecXd& min_elev,
      const bool use_elev_mask1 = false, const bool use_elev_mask2 = false) {
    if (!(r1.rows() == r2.rows() || r1.rows() == 1 || r2.rows() == 1))
      throw std::runtime_error(
          "r1 and r2 must have the same number of rows or one of them must have only one row");

    std::vector<std::map<std::string, bool>> vis;
    for (int i = 0; i < std::max(r1.rows(), r2.rows()); i++) {
      Vec3 r1_vec = r1.rows() == 1 ? r1.row(0) : r1.row(i);
      Vec3 r2_vec = r2.rows() == 1 ? r2.row(0) : r2.row(i);
      vis.push_back(ComputeOccultation(epoch, r1_vec, r2_vec, frame1, frame2, bodies, height_atm,
                                       min_elev, use_elev_mask1, use_elev_mask2));
    }
    return vis;
  }

  std::vector<std::map<std::string, bool>> Occultation::ComputeOccultation(
      const VecX& epoch, const Mat<-1, 3>& r1, const Mat<-1, 3>& r2, Frame frame1, Frame frame2,
      const std::vector<BodyId>& bodies, const VecXd& height_atm, const VecXd& min_elev,
      const bool use_elev_mask1 = false, const bool use_elev_mask2 = false) {
    if (!(epoch.size() == r1.rows() || r1.rows() == 1))
      throw std::runtime_error("epoch and r1 must have the same size or r1 must have only one row");
    if (!(epoch.size() == r2.rows() || r2.rows() == 1))
      throw std::runtime_error("epoch and r2 must have the same size or r2 must have only one row");

    std::vector<std::map<std::string, bool>> vis;
    for (int i = 0; i < epoch.size(); i++) {
      Vec3 r1_vec = r1.rows() == 1 ? r1.row(0) : r1.row(i);
      Vec3 r2_vec = r2.rows() == 1 ? r2.row(0) : r2.row(i);
      vis.push_back(ComputeOccultation(epoch(i), r1_vec, r2_vec, frame1, frame2, bodies, height_atm,
                                       min_elev, use_elev_mask1, use_elev_mask2));
    }
    return vis;
  }

}  // namespace lupnt