use crate::modulation::Modulation;
#[doc(alias = "C/N")]
#[doc(alias = "SNR")]
#[must_use]
pub fn snr_to_c_over_no(snr_db: f64, noise_bandwidth_hz: f64) -> f64 {
snr_db + 10.0 * noise_bandwidth_hz.log10()
}
#[doc(alias = "C/N")]
#[must_use]
pub fn c_over_no_to_snr(c_over_no_db_hz: f64, noise_bandwidth_hz: f64) -> f64 {
c_over_no_db_hz - 10.0 * noise_bandwidth_hz.log10()
}
#[must_use]
pub fn c_over_no_to_es_over_no(c_over_no_db_hz: f64, symbol_rate: f64) -> f64 {
c_over_no_db_hz - 10.0 * symbol_rate.log10()
}
#[doc(alias = "Eb/N0")]
#[must_use]
pub fn es_over_no_to_eb_over_no(
es_over_no_db: f64,
modulation: &Modulation,
code_rate: f64,
) -> f64 {
let k = modulation.bits_per_symbol();
es_over_no_db - 10.0 * k.log10() - 10.0 * code_rate.log10()
}
#[doc(alias = "Eb/N0")]
#[must_use]
pub fn eb_over_no_to_es_over_no(
eb_over_no_db: f64,
modulation: &Modulation,
code_rate: f64,
) -> f64 {
let k = modulation.bits_per_symbol();
eb_over_no_db + 10.0 * k.log10() + 10.0 * code_rate.log10()
}
#[doc(alias = "Eb/N0")]
#[must_use]
pub fn c_over_no_to_eb_over_no(c_over_no_db_hz: f64, info_bit_rate_bps: f64) -> f64 {
c_over_no_db_hz - 10.0 * info_bit_rate_bps.log10()
}
#[doc(alias = "Eb/N0")]
#[must_use]
pub fn eb_over_no_to_c_over_no(eb_over_no_db: f64, info_bit_rate_bps: f64) -> f64 {
eb_over_no_db + 10.0 * info_bit_rate_bps.log10()
}
#[must_use]
pub fn es_over_no_to_ec_over_no(es_over_no_db: f64, modulation: &Modulation) -> f64 {
es_over_no_db - 10.0 * modulation.bits_per_symbol().log10()
}
#[doc(alias = "Eb/N0")]
#[must_use]
pub fn ec_over_no_to_eb_over_no(ec_over_no_db: f64, code_rate: f64) -> f64 {
ec_over_no_db - 10.0 * code_rate.log10()
}
#[doc(alias = "Eb/N0")]
#[doc(alias = "SNR")]
#[must_use]
pub fn snr_to_eb_over_no(
snr_db: f64,
noise_bandwidth_hz: f64,
modulation: &Modulation,
symbol_rate: f64,
code_rate: f64,
) -> f64 {
let c_no = snr_to_c_over_no(snr_db, noise_bandwidth_hz);
let es_no = c_over_no_to_es_over_no(c_no, symbol_rate);
es_over_no_to_eb_over_no(es_no, modulation, code_rate)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::modulation::Modulation;
#[test]
fn snr_to_c_over_no_1mhz() {
let c_no = snr_to_c_over_no(10.0, 1e6);
assert!((c_no - 70.0).abs() < 1e-10);
}
#[test]
fn c_over_no_roundtrip() {
let snr = 15.0;
let bw = 36e6;
let c_no = snr_to_c_over_no(snr, bw);
let snr_back = c_over_no_to_snr(c_no, bw);
assert!((snr - snr_back).abs() < 1e-10);
}
#[test]
fn c_over_no_to_eb_over_no_direct() {
let eb_no = c_over_no_to_eb_over_no(70.0, 1e6);
assert!((eb_no - 10.0).abs() < 1e-10);
}
#[test]
fn eb_no_roundtrip_via_c_no() {
let eb_no = 9.6;
let rb = 5e6;
let c_no = eb_over_no_to_c_over_no(eb_no, rb);
let eb_no_back = c_over_no_to_eb_over_no(c_no, rb);
assert!((eb_no - eb_no_back).abs() < 1e-10);
}
#[test]
fn es_no_to_eb_no_qpsk_rate_half() {
let mod_qpsk = Modulation::Qpsk;
let es_no = 10.0;
let eb_no = es_over_no_to_eb_over_no(es_no, &mod_qpsk, 0.5);
assert!(
(eb_no - es_no).abs() < 0.01,
"For QPSK rate-1/2, Eb/No ≈ Es/No"
);
}
#[test]
fn es_no_to_eb_no_bpsk_rate_one() {
let m = Modulation::Bpsk;
let es_no = 12.0;
let eb_no = es_over_no_to_eb_over_no(es_no, &m, 1.0);
assert!((eb_no - 12.0).abs() < 1e-10);
}
#[test]
fn eb_no_es_no_roundtrip() {
let m = Modulation::Mqam(16);
let code_rate = 0.75;
let eb_no = 8.0;
let es_no = eb_over_no_to_es_over_no(eb_no, &m, code_rate);
let eb_no_back = es_over_no_to_eb_over_no(es_no, &m, code_rate);
assert!((eb_no - eb_no_back).abs() < 1e-10);
}
#[test]
fn ec_no_chain() {
let m = Modulation::Qpsk;
let es_no = 12.0;
let ec_no = es_over_no_to_ec_over_no(es_no, &m);
let eb_no = ec_over_no_to_eb_over_no(ec_no, 0.75);
let expected_ec = 12.0 - 10.0 * 2.0_f64.log10();
assert!((ec_no - expected_ec).abs() < 0.01);
let expected_eb = expected_ec - 10.0 * 0.75_f64.log10();
assert!((eb_no - expected_eb).abs() < 0.01);
}
#[test]
fn full_chain_snr_to_eb_no() {
let m = Modulation::Qpsk;
let eb_no = snr_to_eb_over_no(20.0, 10e6, &m, 5e6, 0.75);
let expected = 20.0 + 10.0 * 10e6_f64.log10()
- 10.0 * 5e6_f64.log10()
- 10.0 * 2.0_f64.log10()
- 10.0 * 0.75_f64.log10();
assert!((eb_no - expected).abs() < 0.01);
}
}