193 lines
5.2 KiB
Rust
193 lines
5.2 KiB
Rust
use crate::{Gf2, LdpcError, Llr, Result};
|
|
|
|
// Trait Channel
|
|
|
|
pub trait Channel: Send + Sync {
|
|
fn transmit(&self, codeword: &[Gf2], rng: &mut impl rand::Rng) -> Vec<Llr>;
|
|
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<Self> {
|
|
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<Llr> {
|
|
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<Self> {
|
|
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<Llr> {
|
|
codeword
|
|
.iter()
|
|
.map(|&b| {
|
|
let rcv = if rng.gen::<f64>() < 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<Self> {
|
|
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<Llr> {
|
|
codeword
|
|
.iter()
|
|
.map(|&b| {
|
|
if rng.gen::<f64>() < 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);
|
|
// }
|
|
// }
|