use crate::{Gf2, LdpcError, Llr, Result}; // Trait Channel pub trait Channel: Send + Sync { fn transmit(&self, codeword: &[Gf2], rng: &mut impl rand::Rng) -> Vec; fn capacity(&self) -> f64; } // Canal AWGN // Modulation BPSK : 0 -> +1.0, 1 -> -1.0 // Signal reçu : y = x + n, n eq N(0, sig²) // LLR optimal : L(y) = 2y/sig² // sig² = N_0/2 = 1/(2 R SNR_lin) #[derive(Debug, Clone)] pub struct AwgnChannel { pub snr_db: f64, sigma: f64, } impl AwgnChannel { pub fn new(snr_db: f64, code_rate: f64) -> Result { if !(0.0..1.0).contains(&code_rate) { return Err(LdpcError::OutOfRange("code_rate ∈ ]0, 1[".into())); } let snr_lin = 10.0_f64.powf(snr_db / 10.0); let sigma = (1.0 / (2.0 * code_rate * snr_lin)).sqrt(); Ok(Self { snr_db, sigma }) } pub fn sigma(&self) -> f64 { self.sigma } pub fn snr_linear(&self) -> f64 { 10.0_f64.powf(self.snr_db / 10.0) } #[inline] pub fn llr_from_received(y: f64, sigma: f64) -> Llr { 2.0 * y / (sigma * sigma) } } impl Channel for AwgnChannel { fn transmit(&self, codeword: &[Gf2], rng: &mut impl rand::Rng) -> Vec { use rand_distr::{Distribution, Normal}; let normal = Normal::new(0.0, self.sigma).unwrap(); codeword .iter() .map(|&b| { let x = if b == 0 { 1.0_f64 } else { -1.0_f64 }; let y = x + normal.sample(rng); Self::llr_from_received(y, self.sigma) }) .collect() } fn capacity(&self) -> f64 { // Capacité BPSK-AWGN par Monte-Carlo use rand_distr::{Distribution, Normal}; let mut rng = rand::thread_rng(); let normal = Normal::new(0.0, self.sigma).unwrap(); let n_samples = 10_000usize; let mut sum = 0.0f64; for _ in 0..n_samples { let n: f64 = normal.sample(&mut rng); let y = 1.0 + n; // bit 0 transmis (x=+1) let llr = Self::llr_from_received(y, self.sigma); // I = 1 - E[log2(1 + exp(-2y/sig²))] sum += (1.0 + (-llr).exp()).log2(); } 1.0 - sum / n_samples as f64 } } // Canal BSC // Chaque bit inversé avec probabilité p // LLR : +-log((1-p)/p) selon le bit reçu #[derive(Debug, Clone)] pub struct BscChannel { pub crossover_prob: f64, llr_magnitude: Llr, } impl BscChannel { pub fn new(crossover_prob: f64) -> Result { if crossover_prob <= 0.0 || crossover_prob >= 0.5 { return Err(LdpcError::OutOfRange("p ∈ ]0, 0.5[".into())); } let llr_magnitude = ((1.0 - crossover_prob) / crossover_prob).ln(); Ok(Self { crossover_prob, llr_magnitude, }) } } impl Channel for BscChannel { fn transmit(&self, codeword: &[Gf2], rng: &mut impl rand::Rng) -> Vec { codeword .iter() .map(|&b| { let rcv = if rng.gen::() < self.crossover_prob { b ^ 1 } else { b }; if rcv == 0 { self.llr_magnitude } else { -self.llr_magnitude } }) .collect() } // C_BSC(p) = 1 - Hb(p) avec Hb = entropie binaire fn capacity(&self) -> f64 { let p = self.crossover_prob; let hb = -p * p.log2() - (1.0 - p) * (1.0 - p).log2(); 1.0 - hb } } // Canal BEC // Bit effacé avec probabilité ε -> LLR = 0 (incertitude totale) // Bit reçu correctement → LLR = +-CERTAIN_LLR (grand mais fini) #[derive(Debug, Clone)] pub struct BecChannel { pub erasure_prob: f64, } impl BecChannel { pub fn new(erasure_prob: f64) -> Result { if erasure_prob <= 0.0 || erasure_prob >= 1.0 { return Err(LdpcError::OutOfRange("ε ∈ ]0, 1[".into())); } Ok(Self { erasure_prob }) } // Grand mais fini pour stabilité numérique const CERTAIN_LLR: Llr = 100.0; } impl Channel for BecChannel { fn transmit(&self, codeword: &[Gf2], rng: &mut impl rand::Rng) -> Vec { codeword .iter() .map(|&b| { if rng.gen::() < self.erasure_prob { 0.0 // Effacement } else if b == 0 { Self::CERTAIN_LLR } else { -Self::CERTAIN_LLR } }) .collect() } /// C_BEC(ε) = 1 - ε fn capacity(&self) -> f64 { 1.0 - self.erasure_prob } } // #[cfg(test)] // mod tests { // use super::*; // // #[test] // fn test_awgn_llr_formula() { // assert!((AwgnChannel::llr_from_received(1.0, 1.0) - 2.0).abs() < 1e-12); // assert!((AwgnChannel::llr_from_received(-1.0, 1.0) + 2.0).abs() < 1e-12); // } // // #[test] // fn test_bec_capacity() { // let bec = BecChannel::new(0.3).unwrap(); // assert!((bec.capacity() - 0.7).abs() < 1e-12); // } // // #[test] // fn test_bsc_capacity_bounds() { // let bsc = BscChannel::new(0.1).unwrap(); // let cap = bsc.capacity(); // assert!(cap > 0.0 && cap < 1.0); // } // }