use crate::{bias::BiasRuntime, prelude::TimeScale};
use std::f64::consts::PI;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy)]
pub enum IonosphereModel {
KbModel(KbModel),
}
impl IonosphereModel {
pub fn bias_m(&self, rtm: &BiasRuntime) -> f64 {
match self {
Self::KbModel(kb) => kb.bias_m(rtm),
}
}
}
#[derive(Clone, Copy, Default, Debug)]
pub struct KbModel {
pub alpha: (f64, f64, f64, f64),
pub beta: (f64, f64, f64, f64),
pub h_km: f64,
}
impl KbModel {
fn bias_m(&self, rtm: &BiasRuntime) -> f64 {
const PHI_P: f64 = 78.3;
const R_EARTH: f64 = 6378.0;
const LAMBDA_P: f64 = 291.0;
const L1_F: f64 = 1575.42E6;
let (phi_u, lambda_u) = (
rtm.rcvr_lat_long_alt_deg_deg_km.0.to_radians(),
rtm.rcvr_lat_long_alt_deg_deg_km.1.to_radians(),
);
let fract = R_EARTH / (R_EARTH + self.h_km);
let (elev_rad, azim_rad) = (
rtm.sv_elevation_azimuth_deg_deg.0.to_radians(),
rtm.sv_elevation_azimuth_deg_deg.1.to_radians(),
);
let t_gpst = rtm
.epoch
.to_duration_in_time_scale(TimeScale::GPST)
.to_seconds();
let psi = PI / 2.0 - elev_rad - (fract * elev_rad.cos()).asin();
let phi_i = (phi_u.sin() * psi.cos() + phi_u.cos() * psi.sin() * azim_rad.cos()).asin();
let lambda_i = lambda_u + azim_rad.sin() * psi / phi_i.cos();
let phi_m = (phi_i.sin() * PHI_P.sin()
+ phi_i.cos() * PHI_P.cos() * (lambda_i - LAMBDA_P).cos())
.asin();
let mut t_s = 43.2E3 * lambda_i / PI + t_gpst;
if t_s > 86.4E3 {
t_s -= 86.4E3;
} else if t_s < 0.0 {
t_s += 86.4E3;
}
let mut a_i = self.alpha.0 * (phi_m / PI).powi(0)
+ self.alpha.1 * (phi_m / PI).powi(1)
+ self.alpha.2 * (phi_m / PI).powi(2)
+ self.alpha.3 * (phi_m / PI).powi(3);
if a_i < 0.0 {
a_i = 0.0_f64;
}
let mut p_i = self.beta.0 * (phi_m / PI).powi(0)
+ self.beta.1 * (phi_m / PI).powi(1)
+ self.beta.2 * (phi_m / PI).powi(2)
+ self.beta.3 * (phi_m / PI).powi(3);
if p_i < 72.0E3 {
p_i = 72.0E3;
}
let x_i = 2.0 * PI * (t_s - 50400.0) / p_i;
let f = 1.0 / ((1.0 - fract * elev_rad.cos()).powi(2)).sqrt();
let i_1 = match x_i < PI / 2.0 {
true => 5.0 * 10E-9 + a_i * x_i.cos(),
false => f * 5.0 * 10E-9,
};
i_1 * (L1_F / rtm.frequency_hz).powi(2)
}
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum IonosphereBias {
Measured(f64),
Modeled(f64),
}
impl Default for IonosphereBias {
fn default() -> Self {
Self::Modeled(0.0)
}
}
impl IonosphereBias {
pub fn value(&self) -> f64 {
match self {
Self::Modeled(bias) => *bias,
Self::Measured(bias) => *bias,
}
}
}