use std::f64::consts::PI;
#[derive(Debug, Clone)]
pub struct MmiCoupler {
pub width: f64,
pub n_eff: f64,
pub wavelength: f64,
pub width_eff: f64,
}
impl MmiCoupler {
pub fn new(width: f64, n_eff: f64, wavelength: f64, n_clad: f64) -> Self {
let width_eff = if n_eff > n_clad {
let delta = 2.0 * wavelength / (PI * (n_eff * n_eff - n_clad * n_clad).sqrt());
width + delta
} else {
width
};
Self {
width,
n_eff,
wavelength,
width_eff,
}
}
pub fn beat_length(&self) -> f64 {
PI * self.n_eff * self.width_eff * self.width_eff / self.wavelength
}
pub fn length_1x2(&self) -> f64 {
3.0 * self.beat_length() / 4.0
}
pub fn length_2x2(&self) -> f64 {
self.beat_length() / 2.0
}
pub fn length_nxn(&self, n: usize) -> f64 {
self.beat_length() / (n as f64)
}
pub fn output_power_1xn(&self, n: usize) -> f64 {
1.0 / n as f64
}
pub fn crossing_length(&self) -> f64 {
self.beat_length()
}
}
#[derive(Debug, Clone)]
pub struct Mmi1x2 {
pub coupler: MmiCoupler,
pub output_gap: f64,
}
impl Mmi1x2 {
pub fn new(width: f64, n_eff: f64, wavelength: f64, n_clad: f64, output_gap: f64) -> Self {
Self {
coupler: MmiCoupler::new(width, n_eff, wavelength, n_clad),
output_gap,
}
}
pub fn length(&self) -> f64 {
self.coupler.length_1x2()
}
pub fn output_power(&self) -> f64 {
0.5
}
pub fn output_positions(&self) -> (f64, f64) {
let half_gap = self.output_gap / 2.0;
(-half_gap, half_gap)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mmi_beat_length() {
let mmi = MmiCoupler::new(6e-6, 2.8, 1550e-9, 1.444);
let lpi = mmi.beat_length();
assert!(
lpi > 10e-6 && lpi < 500e-6,
"Beat length={:.2}μm",
lpi * 1e6
);
}
#[test]
fn mmi_1x2_length_positive() {
let mmi = MmiCoupler::new(4e-6, 2.8, 1550e-9, 1.444);
let l = mmi.length_1x2();
assert!(l > 0.0, "1x2 MMI length must be positive");
}
#[test]
fn mmi_power_split() {
let splitter = Mmi1x2::new(4e-6, 2.8, 1550e-9, 1.444, 2e-6);
assert!((splitter.output_power() - 0.5).abs() < 1e-10);
}
}