use std::f64::consts::PI;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModulationFormat {
Nrz,
Pam4,
Pam8,
Qpsk,
}
impl ModulationFormat {
pub fn bits_per_symbol(&self) -> u32 {
match self {
ModulationFormat::Nrz => 1,
ModulationFormat::Pam4 => 2,
ModulationFormat::Pam8 => 3,
ModulationFormat::Qpsk => 2,
}
}
pub fn symbol_rate_gbaud(&self, data_rate_gbps: f64) -> f64 {
data_rate_gbps / self.bits_per_symbol() as f64
}
pub fn required_snr_db(&self) -> f64 {
match self {
ModulationFormat::Nrz => 17.0, ModulationFormat::Pam4 => 24.0, ModulationFormat::Pam8 => 30.0, ModulationFormat::Qpsk => 16.0, }
}
}
#[derive(Debug, Clone, Copy)]
pub struct OpticalTransmitter {
pub tx_power_dbm: f64,
pub extinction_ratio_db: f64,
pub chirp_alpha: f64,
pub bandwidth_ghz: f64,
pub format: ModulationFormat,
pub power_mw: f64,
}
impl OpticalTransmitter {
pub fn si_ring_nrz_50g() -> Self {
Self {
tx_power_dbm: 0.0,
extinction_ratio_db: 7.0,
chirp_alpha: 0.0, bandwidth_ghz: 35.0,
format: ModulationFormat::Nrz,
power_mw: 5.0,
}
}
pub fn mzm_pam4_100g() -> Self {
Self {
tx_power_dbm: 3.0,
extinction_ratio_db: 10.0,
chirp_alpha: -0.5,
bandwidth_ghz: 30.0,
format: ModulationFormat::Pam4,
power_mw: 25.0,
}
}
pub fn oma_db(&self) -> f64 {
let p_tx = 10.0_f64.powf(self.tx_power_dbm / 10.0);
let er = 10.0_f64.powf(self.extinction_ratio_db / 10.0);
let p1 = p_tx;
let p0 = p_tx / er;
10.0 * (p1 - p0).log10()
}
pub fn energy_per_bit_pj(&self, data_rate_gbps: f64) -> f64 {
self.power_mw / data_rate_gbps }
pub fn dispersion_penalty_db(
&self,
data_rate_gbps: f64,
fiber_length_km: f64,
d_ps_nm_km: f64,
wavelength_nm: f64,
) -> f64 {
let b = data_rate_gbps * 1e9;
let d_total = d_ps_nm_km * fiber_length_km * 1e-12; let lambda = wavelength_nm * 1e-9;
let c = 3e8;
let dl = lambda * lambda * b / c;
let pp = 2.0 * (PI * d_total * dl * b).powi(2);
10.0 * (1.0 + pp).log10() }
}
#[derive(Debug, Clone, Copy)]
pub struct OpticalReceiver {
pub sensitivity_dbm: f64,
pub bandwidth_ghz: f64,
pub format: ModulationFormat,
pub power_mw: f64,
pub responsivity: f64,
pub tia_gain_ohm: f64,
}
impl OpticalReceiver {
pub fn ingaas_pam4_100g() -> Self {
Self {
sensitivity_dbm: -18.0,
bandwidth_ghz: 65.0,
format: ModulationFormat::Pam4,
power_mw: 150.0,
responsivity: 0.9,
tia_gain_ohm: 500.0,
}
}
pub fn sige_nrz_50g() -> Self {
Self {
sensitivity_dbm: -15.0,
bandwidth_ghz: 35.0,
format: ModulationFormat::Nrz,
power_mw: 50.0,
responsivity: 0.8,
tia_gain_ohm: 200.0,
}
}
pub fn sensitivity_mw(&self) -> f64 {
10.0_f64.powf(self.sensitivity_dbm / 10.0)
}
pub fn energy_per_bit_pj(&self, data_rate_gbps: f64) -> f64 {
self.power_mw / data_rate_gbps
}
pub fn tia_output_mv(&self, p_opt_w: f64) -> f64 {
self.responsivity * p_opt_w * self.tia_gain_ohm * 1e3
}
}
#[derive(Debug, Clone)]
pub struct Transceiver {
pub tx: OpticalTransmitter,
pub rx: OpticalReceiver,
pub data_rate_gbps: f64,
}
impl Transceiver {
pub fn link_budget_db(&self) -> f64 {
self.tx.tx_power_dbm - self.rx.sensitivity_dbm
}
pub fn total_power_mw(&self) -> f64 {
self.tx.power_mw + self.rx.power_mw
}
pub fn energy_efficiency_pj_per_bit(&self) -> f64 {
self.total_power_mw() / self.data_rate_gbps
}
pub fn max_reach_km(&self, d_ps_nm_km: f64, wavelength_nm: f64, penalty_budget_db: f64) -> f64 {
let mut lo = 0.0f64;
let mut hi = 1e4f64;
for _ in 0..50 {
let mid = (lo + hi) / 2.0;
let pp =
self.tx
.dispersion_penalty_db(self.data_rate_gbps, mid, d_ps_nm_km, wavelength_nm);
if pp < penalty_budget_db {
lo = mid;
} else {
hi = mid;
}
}
(lo + hi) / 2.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn modulation_pam4_bits_per_symbol() {
assert_eq!(ModulationFormat::Pam4.bits_per_symbol(), 2);
}
#[test]
fn tx_oma_db_finite() {
let tx = OpticalTransmitter::si_ring_nrz_50g();
assert!(tx.oma_db().is_finite());
assert!(tx.oma_db() > -20.0, "OMA={:.2} dBm", tx.oma_db());
}
#[test]
fn tx_energy_per_bit_positive() {
let tx = OpticalTransmitter::si_ring_nrz_50g();
assert!(tx.energy_per_bit_pj(50.0) > 0.0);
}
#[test]
fn rx_sensitivity_mw_positive() {
let rx = OpticalReceiver::ingaas_pam4_100g();
assert!(rx.sensitivity_mw() > 0.0);
}
#[test]
fn transceiver_link_budget_positive() {
let xcvr = Transceiver {
tx: OpticalTransmitter::mzm_pam4_100g(),
rx: OpticalReceiver::ingaas_pam4_100g(),
data_rate_gbps: 100.0,
};
assert!(xcvr.link_budget_db() > 0.0);
}
#[test]
fn transceiver_energy_efficiency_positive() {
let xcvr = Transceiver {
tx: OpticalTransmitter::si_ring_nrz_50g(),
rx: OpticalReceiver::sige_nrz_50g(),
data_rate_gbps: 50.0,
};
assert!(xcvr.energy_efficiency_pj_per_bit() > 0.0);
}
#[test]
fn symbol_rate_pam4_half_data_rate() {
let fmt = ModulationFormat::Pam4;
assert!((fmt.symbol_rate_gbaud(100.0) - 50.0).abs() < 1e-10);
}
}