use num_complex::Complex;
use rand::Rng;
use rand_distr::{Distribution, Normal};
pub trait ChannelType: sealed::Sealed + std::ops::AddAssign + Sized {
#[doc(hidden)]
fn noise<R: Rng>(awgn_channel: &AwgnChannel, rng: &mut R) -> Self;
}
pub trait Channel {
fn add_noise<R: Rng, T: ChannelType>(&self, rng: &mut R, symbols: &mut [T]);
}
#[derive(Debug, Clone)]
pub struct AwgnChannel {
distr: Normal<f64>,
}
impl AwgnChannel {
pub fn new(noise_sigma: f64) -> AwgnChannel {
assert!(noise_sigma >= 0.0);
AwgnChannel {
distr: Normal::new(0.0, noise_sigma).unwrap(),
}
}
}
impl Channel for AwgnChannel {
fn add_noise<R: Rng, T: ChannelType>(&self, rng: &mut R, symbols: &mut [T]) {
for x in symbols.iter_mut() {
*x += T::noise(self, rng);
}
}
}
impl ChannelType for f64 {
fn noise<R: Rng>(awgn_channel: &AwgnChannel, rng: &mut R) -> f64 {
awgn_channel.distr.sample(rng)
}
}
impl ChannelType for Complex<f64> {
fn noise<R: Rng>(awgn_channel: &AwgnChannel, rng: &mut R) -> Complex<f64> {
Complex::new(
awgn_channel.distr.sample(rng),
awgn_channel.distr.sample(rng),
)
}
}
mod sealed {
use num_complex::Complex;
pub trait Sealed {}
impl Sealed for f64 {}
impl Sealed for Complex<f64> {}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn build_awgn() {
let _channel = AwgnChannel::new(0.2);
}
#[test]
#[should_panic]
fn negative_noise_sigma() {
let _channel = AwgnChannel::new(-3.5);
}
#[test]
fn zero_noise_sigma() {
let channel = AwgnChannel::new(0.0);
let mut rng = rand::rng();
let mut symbols = vec![1.0; 1024];
let symbols_orig = symbols.clone();
channel.add_noise(&mut rng, &mut symbols);
assert_eq!(&symbols, &symbols_orig);
}
}