Program Listing for File time_utils.cc

Return to documentation for file (environment/plasma/env/time_utils.cc)

#include "lupnt/environment/plasma/env/time_utils.h"

#include <array>
#include <cmath>
#include <stdexcept>
#include <vector>

#include "lupnt/environment/plasma/core/math_utils.h"

namespace pecsim {

  void doy_to_mmdd(int year, int doy, int& mm, int& dd) {
    if (doy < 1 || doy > 366) {
      throw std::runtime_error("Day of year must be between 1 and 366.");
    }

    int month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    // Proper leap year check
    bool is_leap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
    if (is_leap) {
      month_days[1] = 29;
    }

    int i = 0;
    while (i < 12 && doy > month_days[i]) {
      doy -= month_days[i];
      ++i;
    }

    if (i >= 12) {
      throw std::runtime_error("Day of year exceeds total days in year.");
    }

    mm = i + 1;
    dd = doy;
  }

  void mmdd_to_doy(int year, int mm, int dd, int& doy) {
    if (mm < 1 || mm > 12) {
      throw std::runtime_error("Invalid month.");
    }

    int month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    bool is_leap = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
    if (is_leap) {
      month_days[1] = 29;
    }

    if (dd < 1 || dd > month_days[mm - 1]) {
      throw std::runtime_error("Invalid day for the given month.");
    }

    doy = dd;
    for (int i = 0; i < mm - 1; ++i) {
      doy += month_days[i];
    }
  }

  std::array<int, 2> datetime_to_itime(DateTime datetime) {
    std::array<int, 2> itime;
    itime[0] = datetime.year * 1000 + datetime.doy;  // Year and day of the year
    int ihr = datetime.hour, imin = datetime.min, isec = datetime.sec;
    itime[1] = (ihr * 3600 + imin * 60 + isec) * 1000;  // Milliseconds of the day
    return itime;
  }

  DateTime itime_to_datetime(const std::array<int, 2>& itime) {
    DateTime datetime;
    datetime.year = itime[0] / 1000;  // Year
    datetime.doy = itime[0] % 1000;   // Day of the year

    int total_seconds = itime[1] / 1000;  // Convert milliseconds to seconds
    datetime.hour = total_seconds / 3600;
    total_seconds %= 3600;
    datetime.min = total_seconds / 60;
    datetime.sec = total_seconds % 60;

    return datetime;
  }

  double datetime_to_mjd(const DateTime& dt) {
    // Convert (year + doy) to month and day
    static const int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int month = 1, day = dt.doy;
    bool leap = (dt.year % 4 == 0 && (dt.year % 100 != 0 || dt.year % 400 == 0));
    int dim[12];
    std::copy(std::begin(days_in_month), std::end(days_in_month), dim);
    if (leap) dim[1] = 29;

    for (int i = 0; i < 12 && day > dim[i]; ++i) {
      day -= dim[i];
      ++month;
    }

    // Julian Date calculation
    int Y = dt.year;
    int M = month;
    if (M <= 2) {
      Y -= 1;
      M += 12;
    }
    int A = Y / 100;
    int B = 2 - A + A / 4;

    double D = day + (dt.hour + (dt.min + dt.sec / 60.0) / 60.0) / 24.0;

    double JD = std::floor(365.25 * (Y + 4716)) + std::floor(30.6001 * (M + 1)) + D + B - 1524.5;

    return JD - 2400000.5;
  }

  DateTime mjd_to_datetime(double mjd) {
    double jd = mjd + 2400000.5;
    double z = std::floor(jd + 0.5);
    double f = jd + 0.5 - z;

    double A = z;
    if (z >= 2299161) {
      int alpha = static_cast<int>((z - 1867216.25) / 36524.25);
      A = A + 1 + alpha - alpha / 4;
    }

    double B = A + 1524;
    int C = static_cast<int>((B - 122.1) / 365.25);
    int D = static_cast<int>(365.25 * C);
    int E = static_cast<int>((B - D) / 30.6001);

    double day = B - D - static_cast<int>(30.6001 * E) + f;
    int day_int = static_cast<int>(day);

    int month = (E < 14) ? E - 1 : E - 13;
    int year = (month > 2) ? C - 4716 : C - 4715;

    // Compute doy
    static const int days_before_month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
    int doy = days_before_month[month - 1] + day_int;
    if (month > 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) {
      doy += 1;  // leap year
    }

    double day_frac = day - day_int;
    int hour = static_cast<int>(day_frac * 24);
    int min = static_cast<int>((day_frac * 24 - hour) * 60);
    double sec = static_cast<double>(((day_frac * 24 - hour) * 60 - min) * 60);

    return DateTime{year, doy, hour, min, sec};
  }

  double long_to_lt(double along) {
    // Convert longitude in radians to magnetic local time (MLT) in hours
    double amlt = along / AMLTRAD;   // Convert radians to hours
    amlt = amod(amlt - 12.0, 24.0);  // Normalize to [0, 24) hours
    return amlt;
  }

  double lt_to_long(double amlt) {
    // Convert magnetic local time (MLT) to longitude in radians
    // MLT is in hours, so we convert it to radians
    double along = (amlt - 12.0) * AMLTRAD;
    return along;
  }

  double tj2000_to_mjd(double t_j2000) {
    // Convert TJD2000 to MJD
    double mjd = 51544.4996875 + t_j2000 / 86400.0;
    return mjd;
  }

  double mjd_to_tj2000(double mjd) {
    // Convert MJD to TJD2000
    double t_j2000 = (mjd - 51544.4996875) * 86400.0;
    return t_j2000;
  }

  double gregorian_to_mjd(int year, int month, int day, int hour, int min, double sec) {
    if (month <= 2) {
      month += 12;
      year--;
    }
    int b;
    if ((10000L * year + 100L * month + day) <= 15821004L)
      b = -2 + ((year + 4716) / 4) - 1179;  // Julian calendar
    else
      b = (year / 400) - (year / 100) + (year / 4);  // Gregorian calendar

    double mjd_midnight = 365L * year - 679004L + b + int(30.6001 * (month + 1)) + day;
    double frac_of_day = (hour + min / 60.0 + sec / 3600.0) / 24.0;
    return mjd_midnight + frac_of_day;
  }

}  // namespace pecsim