use crate::constants::physics::SPEED_OF_LIGHT_M_S;
use std::f64::consts::PI;
const FSPL_KM_MHZ_CONSTANT_DB: f64 = 32.45;
const DB_FIELD_DECADE: f64 = 20.0;
const DB_POWER_DECADE: f64 = 10.0;
const DBM_TO_DBW_OFFSET_DB: f64 = 30.0;
const BOLTZMANN_K_DBW_HZ_K: f64 = 228.6;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LinkBudget {
pub eirp_dbw: f64,
pub fspl_db: f64,
pub receiver_gt_dbk: f64,
pub other_losses_db: f64,
pub required_cn0_dbhz: f64,
}
pub fn fspl(distance_km: f64, frequency_mhz: f64) -> f64 {
FSPL_KM_MHZ_CONSTANT_DB
+ DB_FIELD_DECADE * frequency_mhz.log10()
+ DB_FIELD_DECADE * distance_km.log10()
}
pub fn eirp(tx_power_dbm: f64, tx_antenna_gain_dbi: f64) -> f64 {
tx_power_dbm + tx_antenna_gain_dbi - DBM_TO_DBW_OFFSET_DB
}
pub fn cn0(eirp_dbw: f64, fspl_db: f64, receiver_gt_dbk: f64, other_losses_db: f64) -> f64 {
eirp_dbw + receiver_gt_dbk - fspl_db + BOLTZMANN_K_DBW_HZ_K - other_losses_db
}
pub fn link_margin(budget: &LinkBudget) -> f64 {
cn0(
budget.eirp_dbw,
budget.fspl_db,
budget.receiver_gt_dbk,
budget.other_losses_db,
) - budget.required_cn0_dbhz
}
pub fn wavelength(frequency_hz: f64) -> f64 {
SPEED_OF_LIGHT_M_S / frequency_hz
}
pub fn dish_gain(diameter_m: f64, frequency_hz: f64, efficiency: f64) -> f64 {
let lambda = wavelength(frequency_hz);
DB_POWER_DECADE * (efficiency * (PI * diameter_m / lambda).powf(2.0)).log10()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fspl_matches_frozen_elixir_bits() {
assert_eq!(
fspl(1200.0, 1616.0).to_bits(),
158.20245204972383_f64.to_bits()
);
}
#[test]
fn eirp_matches_frozen_elixir_bits() {
assert_eq!(eirp(27.0, 3.0).to_bits(), 0.0_f64.to_bits());
}
#[test]
fn cn0_matches_frozen_elixir_bits() {
assert_eq!(
cn0(0.0, 165.0, -12.0, 3.0).to_bits(),
48.599999999999994_f64.to_bits()
);
}
#[test]
fn link_margin_matches_frozen_elixir_bits() {
let budget = LinkBudget {
eirp_dbw: 0.0,
fspl_db: 165.0,
receiver_gt_dbk: -12.0,
other_losses_db: 3.0,
required_cn0_dbhz: 35.0,
};
assert_eq!(
link_margin(&budget).to_bits(),
13.599999999999994_f64.to_bits()
);
}
#[test]
fn wavelength_matches_frozen_elixir_bits() {
assert_eq!(
wavelength(1616.0e6).to_bits(),
0.1855151349009901_f64.to_bits()
);
}
#[test]
fn dish_gain_matches_frozen_elixir_bits() {
assert_eq!(
dish_gain(1.0, 1616.0e6, 0.55).to_bits(),
21.97903741903791_f64.to_bits()
);
}
}