mod common;
use std::process::ExitCode;
use photonoxide::circuit::Component;
use photonoxide::circuit::components::{AddDropRing, AllPassRing, Dispersion};
use photonoxide::units::Wavelength;
fn single_pass(length: f64, fixed: f64) -> f64 {
10f64.powf(-(2.7 * length * 1e-4 + fixed) / 20.0)
}
fn peak(f: impl Fn(f64) -> f64, mut lo: f64, mut hi: f64) -> (f64, f64) {
let g = (5f64.sqrt() - 1.0) / 2.0;
let (mut a, mut b) = (hi - g * (hi - lo), lo + g * (hi - lo));
let (mut fa, mut fb) = (f(a), f(b));
while hi - lo > 1e-6 * hi {
if fa > fb {
hi = b;
(b, fb) = (a, fa);
a = hi - g * (hi - lo);
fa = f(a);
} else {
lo = a;
(a, fa) = (b, fb);
b = lo + g * (hi - lo);
fb = f(b);
}
}
let x = (lo + hi) / 2.0;
(x, f(x))
}
fn measured_fwhm(
through: impl Fn(f64) -> photonoxide::Result<f64>,
centre: f64,
top: f64,
fsr: f64,
) -> photonoxide::Result<f64> {
let bottom = through(centre)?;
let half = (top + bottom) / 2.0;
let mut edges = [0.0; 2];
for (k, sign) in [-1.0, 1.0].into_iter().enumerate() {
let (mut near, mut far) = (0.0, fsr / 4.0);
for _ in 0..60 {
let mid = (near + far) / 2.0;
if through(centre + sign * mid)? < half {
near = mid;
} else {
far = mid;
}
}
edges[k] = (near + far) / 2.0;
}
Ok(edges[0] + edges[1])
}
pub fn main() -> photonoxide::Result<ExitCode> {
let l0 = Wavelength::um(1.55)?;
let guide = Dispersion::new(l0, 2.4, 4.30).with_loss(2.7);
let all_pass = AllPassRing::new(guide)?.with_round_trip_loss(0.075)?;
let add_drop = AddDropRing::new(guide)?.with_round_trip_loss(0.11)?;
let r2: f64 = 0.99;
let q_all_pass = |l: f64| {
let a = single_pass(l, 0.075);
all_pass.q_factor(l0, &[l, 1.0 - a * a])
};
let q_add_drop = |l: f64| {
let a = single_pass(l, 0.11);
add_drop.q_factor(l0, &[l, 1.0 - (r2 * a).powi(2), 1.0 - r2 * r2])
};
let (la, qa) = peak(q_all_pass, 1e3, 3e4);
let (ld, qd) = peak(q_add_drop, 1e3, 3e4);
println!("Bogaerts's Eqs. 19-22 at critical coupling, 2.7 dB/cm, exact: no grid");
println!(
" all-pass: Q peaks at {:.3} mm, {qa:.4e} with n_g 4.30",
la / 1e3
);
println!(
" add-drop: Q peaks at {:.3} mm, {qd:.4e} with n_g 4.30",
ld / 1e3
);
let mut checks = common::Checks::default();
checks.compare("all-pass, best length (mm)", la / 1e3, 10.0, 1.0);
checks.compare("add-drop, best length (mm)", ld / 1e3, 12.5, 0.5);
checks.compare("peak Q, all-pass over add-drop", qa / qd, 1.0441, 0.0075);
let kappa2 = 1.0 - single_pass(la, 0.075).powi(2);
let values = [la, kappa2];
let centre = all_pass.resonance(l0, la)?;
let fsr = all_pass.fsr(centre, la);
let through = |w: f64| -> photonoxide::Result<f64> {
Ok(all_pass.s_matrix(Wavelength::um(w)?, &values)?.power(1, 0))
};
let (top, _) = all_pass.extremes(centre, &values);
let fwhm = measured_fwhm(through, centre.to_um(), top, fsr)?;
println!(
" all-pass at {:.3} mm, its spectrum: FSR {:.2} pm, FWHM {:.3} pm (Eq. 7: {:.3}), Q {:.4e} (Eq. 20: {:.4e}), finesse {:.2}",
la / 1e3,
fsr * 1e6,
fwhm * 1e6,
all_pass.fwhm(centre, &values) * 1e6,
centre.to_um() / fwhm,
all_pass.q_factor(centre, &values),
fsr / fwhm
);
Ok(checks.finish())
}