#[cfg(test)]
mod tests;
use crate::astro::constants::time::{DAYS_PER_JULIAN_YEAR, J2000_JD};
use crate::astro::constants::units::MM_PER_M;
use crate::astro::math::vec3::norm3_ref as norm;
use crate::validate;
use super::{cal2jd, invalid_tide_input, TideError};
const MEAN_POLE_X0_MAS: f64 = 55.0;
const MEAN_POLE_X_RATE_MAS_PER_YR: f64 = 1.677;
const MEAN_POLE_Y0_MAS: f64 = 320.5;
const MEAN_POLE_Y_RATE_MAS_PER_YR: f64 = 3.460;
const RADIAL_MM_PER_ARCSEC: f64 = -33.0;
const COLATITUDE_MM_PER_ARCSEC: f64 = -9.0;
const LONGITUDE_MM_PER_ARCSEC: f64 = 9.0;
pub fn solid_earth_pole_tide(
xsta: &[f64; 3],
year: i32,
month: i32,
day: i32,
fhr: f64,
xp_arcsec: f64,
yp_arcsec: f64,
) -> Result<[f64; 3], TideError> {
validate_pole_tide_domain(xsta, year, month, day, fhr, xp_arcsec, yp_arcsec)?;
Ok(solid_earth_pole_tide_unchecked(
xsta, year, month, day, fhr, xp_arcsec, yp_arcsec,
))
}
fn validate_pole_tide_domain(
xsta: &[f64; 3],
year: i32,
month: i32,
day: i32,
fhr: f64,
xp_arcsec: f64,
yp_arcsec: f64,
) -> Result<(), TideError> {
validate::finite_vec3(*xsta, "station position").map_err(invalid_tide_input)?;
validate::civil_datetime_with_second_policy(
i64::from(year),
i64::from(month),
i64::from(day),
0,
0,
0.0,
validate::CivilSecondPolicy::Continuous,
)
.map_err(invalid_tide_input)?;
validate::finite_in_range_exclusive_upper(fhr, 0.0, 24.0, "fractional hour")
.map_err(invalid_tide_input)?;
validate::finite(xp_arcsec, "polar motion xp").map_err(invalid_tide_input)?;
validate::finite(yp_arcsec, "polar motion yp").map_err(invalid_tide_input)?;
validate::finite_positive(norm(xsta), "station radius").map_err(invalid_tide_input)?;
let station_horizontal_radius = (xsta[0] * xsta[0] + xsta[1] * xsta[1]).sqrt();
validate::finite_positive(station_horizontal_radius, "station horizontal radius")
.map_err(invalid_tide_input)?;
Ok(())
}
fn solid_earth_pole_tide_unchecked(
xsta: &[f64; 3],
year: i32,
month: i32,
day: i32,
fhr: f64,
xp_arcsec: f64,
yp_arcsec: f64,
) -> [f64; 3] {
let (x_bar_arcsec, y_bar_arcsec) = mean_pole_arcsec(year, month, day, fhr);
let m1 = xp_arcsec - x_bar_arcsec;
let m2 = -(yp_arcsec - y_bar_arcsec);
let rsta = norm(xsta);
let sinphi = xsta[2] / rsta; let cosphi = (xsta[0] * xsta[0] + xsta[1] * xsta[1]).sqrt() / rsta; let sinla = xsta[1] / cosphi / rsta;
let cosla = xsta[0] / cosphi / rsta;
let sin2theta = 2.0 * cosphi * sinphi;
let cos2theta = sinphi * sinphi - cosphi * cosphi;
let costheta = sinphi;
let radial_factor = m1 * cosla + m2 * sinla;
let longitude_factor = m1 * sinla - m2 * cosla;
let s_r = RADIAL_MM_PER_ARCSEC * sin2theta * radial_factor;
let s_theta = COLATITUDE_MM_PER_ARCSEC * cos2theta * radial_factor;
let s_lambda = LONGITUDE_MM_PER_ARCSEC * costheta * longitude_factor;
let up = s_r / MM_PER_M;
let north = -s_theta / MM_PER_M;
let east = s_lambda / MM_PER_M;
[
up * cosla * cosphi - east * sinla - north * sinphi * cosla,
up * sinla * cosphi + east * cosla - north * sinphi * sinla,
up * sinphi + north * cosphi,
]
}
fn mean_pole_arcsec(year: i32, month: i32, day: i32, fhr: f64) -> (f64, f64) {
let (jd0, jd1) = cal2jd(year, month, day);
let years = ((jd0 - J2000_JD) + jd1 + fhr / 24.0) / DAYS_PER_JULIAN_YEAR;
let x_bar_mas = MEAN_POLE_X0_MAS + MEAN_POLE_X_RATE_MAS_PER_YR * years;
let y_bar_mas = MEAN_POLE_Y0_MAS + MEAN_POLE_Y_RATE_MAS_PER_YR * years;
(x_bar_mas / 1000.0, y_bar_mas / 1000.0)
}