use super::{Cf64, Stage};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Quantiser {
bits: u32,
pub step: f64,
}
impl Quantiser {
pub fn new(bits: u32, step: f64) -> Self {
assert!((1..=16).contains(&bits), "bits must be 1..=16");
assert!(step > 0.0, "step must be positive");
Self { bits, step }
}
pub fn optimum_for(bits: u32, sigma: f64) -> Self {
Self::new(bits, optimum_step_sigma(bits).0 * sigma)
}
pub fn bits(&self) -> u32 {
self.bits
}
pub fn levels(&self) -> u32 {
1 << self.bits
}
pub fn code(&self, x: f64) -> i32 {
let l = 1i64 << (self.bits - 1);
let k = (x / self.step).floor();
let k = if k.is_nan() {
0
} else {
k.clamp(-(l as f64), (l - 1) as f64) as i64
};
k as i32
}
pub fn quantise(&self, x: f64) -> f64 {
(self.code(x) as f64 + 0.5) * self.step
}
}
impl Stage for Quantiser {
fn process(&mut self, block: &mut [Cf64]) {
for x in block.iter_mut() {
*x = Cf64::new(self.quantise(x.re), self.quantise(x.im));
}
}
}
fn phi(t: f64) -> f64 {
(-0.5 * t * t).exp() / (2.0 * std::f64::consts::PI).sqrt()
}
fn upper_tail(t: f64) -> f64 {
0.5 * libm::erfc(t / std::f64::consts::SQRT_2)
}
pub fn correlation_loss_db(bits: u32, step_sigma: f64) -> f64 {
assert!((1..=16).contains(&bits) && step_sigma > 0.0);
let l = 1usize << (bits - 1);
let t = step_sigma;
let mut slope = phi(0.0);
for k in 1..l {
slope += 2.0 * phi(k as f64 * t);
}
slope *= t;
let mut power = 0.0;
for k in 0..l {
let lo = upper_tail(k as f64 * t);
let hi = if k + 1 < l {
upper_tail((k + 1) as f64 * t)
} else {
0.0
};
let level = (k as f64 + 0.5) * t;
power += 2.0 * level * level * (lo - hi);
}
-10.0 * (slope * slope / power).log10()
}
pub fn optimum_step_sigma(bits: u32) -> (f64, f64) {
if bits == 1 {
return (1.0, correlation_loss_db(1, 1.0));
}
let l = (1u64 << (bits - 1)) as f64;
let (mut a, mut b) = ((1e-4f64).ln(), (4.0 / (l - 1.0)).ln());
let g = 0.5 * (5f64.sqrt() - 1.0);
let f = |x: f64| correlation_loss_db(bits, x.exp());
let (mut c, mut d) = (b - g * (b - a), a + g * (b - a));
let (mut fc, mut fd) = (f(c), f(d));
for _ in 0..200 {
if fc < fd {
b = d;
d = c;
fd = fc;
c = b - g * (b - a);
fc = f(c);
} else {
a = c;
c = d;
fc = fd;
d = a + g * (b - a);
fd = f(d);
}
}
let x = 0.5 * (a + b);
(x.exp(), f(x))
}