pub fn cdt_level(l1_db: f64, l2_db: f64) -> f64 {
2.0 * l1_db - l2_db - 63.0
}
pub fn cdt_protection_envelope(min_freq: f64, max_freq: f64) -> Vec<(f64, f64)> {
let mut points: Vec<(f64, f64)> = Vec::new();
let intervals: &[f64] = &[
1.2, 1.25, 1.5, ];
let num_fundamentals = 20;
let log_min = min_freq.max(30.0).ln();
let log_max = 300.0_f64.min(max_freq).ln();
if log_max <= log_min {
return vec![(min_freq, -18.0), (max_freq, -18.0)];
}
for i in 0..num_fundamentals {
let t = i as f64 / (num_fundamentals - 1) as f64;
let f1 = (log_min + t * (log_max - log_min)).exp();
for &ratio in intervals {
let f2 = f1 * ratio;
let cdt_freq = 2.0 * f1 - f2;
if cdt_freq >= min_freq && cdt_freq <= max_freq {
let cdt_db = cdt_level(75.0, 75.0); let protection = if cdt_db > -20.0 {
-6.0 } else {
-12.0 };
points.push((cdt_freq, protection));
}
}
}
if points.is_empty() {
return vec![(min_freq, -18.0), (max_freq, -18.0)];
}
points.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
let mut envelope = Vec::with_capacity(points.len() + 2);
if points[0].0 > min_freq {
envelope.push((min_freq, -18.0));
}
envelope.extend_from_slice(&points);
if points.last().unwrap().0 < max_freq {
envelope.push((max_freq, -18.0));
}
envelope
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cdt_level_equal_tones() {
let level = cdt_level(75.0, 75.0);
assert!(
(level - 12.0).abs() < 0.01,
"Expected CDT level ~12 dB, got {}",
level
);
}
#[test]
fn test_cdt_level_unequal_tones() {
let level = cdt_level(80.0, 70.0);
assert!(
(level - 27.0).abs() < 0.01,
"Expected CDT level ~27 dB, got {}",
level
);
}
#[test]
fn test_cdt_protection_envelope_has_entries() {
let envelope = cdt_protection_envelope(20.0, 500.0);
assert!(
envelope.len() >= 3,
"Envelope should have multiple points, got {}",
envelope.len()
);
for w in envelope.windows(2) {
assert!(w[0].0 <= w[1].0, "Envelope not sorted: {} > {}", w[0].0, w[1].0);
}
}
#[test]
fn test_cdt_protection_envelope_limits() {
let envelope = cdt_protection_envelope(20.0, 500.0);
for &(freq, max_cut) in &envelope {
assert!(freq >= 20.0 && freq <= 500.0, "Freq {} out of range", freq);
assert!(max_cut <= 0.0, "Max cut should be negative, got {}", max_cut);
assert!(max_cut >= -18.0, "Max cut too deep: {}", max_cut);
}
}
#[test]
fn test_cdt_protection_envelope_empty_range() {
let envelope = cdt_protection_envelope(10000.0, 11000.0);
assert!(envelope.len() >= 2, "Should have at least boundary points");
}
}