use nalgebra::{Vector3, Vector6};
use crate::constants::AngleFormat;
use crate::constants::OMEGA_EARTH;
use crate::coordinates::{
EllipsoidalConversionType, position_enz_to_azel, relative_position_ecef_to_enz,
};
use crate::time::Epoch;
use crate::time::TimeSystem;
use super::constraints::{AscDsc, LookDirection};
pub fn compute_azimuth(sat_ecef: &Vector3<f64>, loc_ecef: &Vector3<f64>) -> f64 {
let enz =
relative_position_ecef_to_enz(*loc_ecef, *sat_ecef, EllipsoidalConversionType::Geodetic);
let azel = position_enz_to_azel(enz, AngleFormat::Radians);
azel[0].to_degrees()
}
pub fn compute_elevation(sat_ecef: &Vector3<f64>, loc_ecef: &Vector3<f64>) -> f64 {
let enz =
relative_position_ecef_to_enz(*loc_ecef, *sat_ecef, EllipsoidalConversionType::Geodetic);
let azel = position_enz_to_azel(enz, AngleFormat::Radians);
azel[1].to_degrees()
}
pub fn compute_off_nadir(sat_ecef: &Vector3<f64>, loc_ecef: &Vector3<f64>) -> f64 {
let nadir = -sat_ecef.normalize();
let los = (loc_ecef - sat_ecef).normalize();
let cos_angle = nadir.dot(&los);
cos_angle.acos().to_degrees()
}
pub fn compute_local_time(epoch: &Epoch, loc_geodetic: &Vector3<f64>) -> f64 {
use std::f64::consts::PI;
let jd_ut1 = epoch.jd_as_time_system(TimeSystem::UT1);
let t = jd_ut1 - 2451545.0; let tau = 2.0 * PI;
let gmst = t * 1.00273790935 * tau;
let lst = gmst + loc_geodetic[0];
let lst_hours = (lst.rem_euclid(tau) / tau) * 24.0;
lst_hours * 3600.0
}
pub fn compute_look_direction(
sat_state_ecef: &Vector6<f64>,
loc_ecef: &Vector3<f64>,
) -> LookDirection {
let sat_pos = sat_state_ecef.fixed_rows::<3>(0);
let sat_vel = sat_state_ecef.fixed_rows::<3>(3);
let los = loc_ecef - sat_pos;
let cross = sat_vel.cross(&los);
if cross.dot(&sat_pos.into_owned()) > 0.0 {
LookDirection::Left
} else {
LookDirection::Right
}
}
pub fn compute_asc_dsc(sat_state_ecef: &Vector6<f64>) -> AscDsc {
let sat_pos = sat_state_ecef.fixed_rows::<3>(0);
let sat_vel = sat_state_ecef.fixed_rows::<3>(3);
let omega_vec = Vector3::new(0.0, 0.0, OMEGA_EARTH);
let vel_inertial = sat_vel + omega_vec.cross(&sat_pos.into_owned());
if vel_inertial[2] > 0.0 {
AscDsc::Ascending
} else {
AscDsc::Descending
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::coordinates::position_geodetic_to_ecef;
use crate::utils::testing::setup_global_test_eop;
#[test]
fn test_compute_azimuth_elevation() {
let loc_geodetic = Vector3::new(0.0, 45.0_f64.to_radians(), 0.0);
let loc_ecef = position_geodetic_to_ecef(loc_geodetic, AngleFormat::Radians).unwrap();
let sat_ecef = loc_ecef + Vector3::new(0.0, 500e3, 500e3);
let azimuth = compute_azimuth(&sat_ecef, &loc_ecef);
let elevation = compute_elevation(&sat_ecef, &loc_ecef);
assert!((0.0..=360.0).contains(&azimuth));
assert!(elevation > 0.0);
assert!(elevation < 90.0);
}
#[test]
fn test_compute_off_nadir() {
let sat_ecef = Vector3::new(7000e3, 0.0, 0.0);
let loc_geodetic = Vector3::new(0.0, 0.0, 0.0);
let loc_ecef = position_geodetic_to_ecef(loc_geodetic, AngleFormat::Radians).unwrap();
let off_nadir = compute_off_nadir(&sat_ecef, &loc_ecef);
assert!(off_nadir >= 0.0);
assert!(off_nadir <= 180.0);
}
#[test]
fn test_compute_local_time() {
setup_global_test_eop();
let loc_geodetic = Vector3::new(0.0, 0.0, 0.0);
let epoch = Epoch::from_datetime(2024, 1, 1, 12, 0, 0.0, 0.0, TimeSystem::UTC);
let local_time = compute_local_time(&epoch, &loc_geodetic);
assert!(local_time >= 0.0);
assert!(local_time <= 86400.0);
}
#[test]
fn test_compute_asc_dsc() {
let state_ascending = Vector6::new(
7000e3, 0.0, 0.0, 0.0, 7500.0, 100.0, );
let asc_dsc = compute_asc_dsc(&state_ascending);
assert_eq!(asc_dsc, AscDsc::Ascending);
let state_descending = Vector6::new(
7000e3, 0.0, 0.0, 0.0, 7500.0, -100.0, );
let asc_dsc = compute_asc_dsc(&state_descending);
assert_eq!(asc_dsc, AscDsc::Descending);
}
#[test]
fn test_compute_look_direction() {
let sat_state = Vector6::new(
7000e3, 0.0, 0.0, 0.0, 7500.0, 0.0, );
let loc_right = Vector3::new(6000e3, 0.0, 0.0);
let look_dir = compute_look_direction(&sat_state, &loc_right);
assert!(look_dir == LookDirection::Left || look_dir == LookDirection::Right);
}
}