use crate::Bit;
use rand::Rng;
use rand_distr::StandardNormal;
#[must_use]
pub fn random_bits(num_bits: usize) -> Vec<Bit> {
let mut rng = rand::rng();
(0 .. num_bits)
.map(|_| {
if rng.random_bool(0.5) {
Bit::One
} else {
Bit::Zero
}
})
.collect()
}
#[must_use]
pub fn bpsk_awgn_channel(bits: &[Bit], es_over_n0_db: f64) -> Vec<f64> {
let mut rng = rand::rng();
let es_over_n0 = 10f64.powf(0.1 * es_over_n0_db);
let noise_var = 0.5 / es_over_n0;
bits.iter()
.map(|b| match b {
Bit::Zero => 1f64,
Bit::One => -1f64,
})
.map(|x| 4.0 * es_over_n0 * (x + noise_var.sqrt() * rng.sample::<f64, _>(StandardNormal)))
.collect()
}
#[must_use]
pub fn bpsk_slicer(syms: &[f64]) -> Vec<Bit> {
syms.iter()
.map(|&x| if x >= 0.0 { Bit::Zero } else { Bit::One })
.collect()
}
pub fn error_count<T: PartialEq>(seq: &[T], ref_seq: &[T]) -> usize {
ref_seq
.iter()
.zip(seq.iter())
.filter(|&(x, y)| x != y)
.count()
}
#[cfg(test)]
mod tests {
use super::*;
use Bit::{One, Zero};
#[test]
#[allow(clippy::cast_possible_truncation)]
fn test_random_bits() {
let num_bits = 0;
assert!(random_bits(num_bits).is_empty());
let num_bits = 10000;
let bits = random_bits(num_bits);
let num_zeros = bits.iter().filter(|&b| *b == Zero).count();
let num_ones = bits.iter().filter(|&b| *b == One).count();
assert!(num_zeros > 9 * num_bits / 20 && num_ones > 9 * num_bits / 20);
}
#[test]
#[allow(clippy::cast_precision_loss)]
#[allow(clippy::cast_possible_truncation)]
fn test_bpsk_awgn_channel() {
assert!(bpsk_awgn_channel(&random_bits(0), 0.0).is_empty());
let es_over_n0_db = 20f64;
let num_bits = 10000;
let bits = random_bits(num_bits);
let bits_llr = bpsk_awgn_channel(&bits, es_over_n0_db);
let es_over_n0 = 10f64.powf(0.1 * es_over_n0_db);
let noise_var_est = bits_llr
.iter()
.zip(bits)
.map(|(y, b)| match b {
Zero => y - 4.0 * es_over_n0,
One => y + 4.0 * es_over_n0,
})
.map(|x| x * x)
.sum::<f64>()
/ f64::from(num_bits as u32);
assert!(noise_var_est > 7.2 * es_over_n0 && noise_var_est < 8.8 * es_over_n0);
}
#[test]
fn test_bpsk_slicer() {
assert!(bpsk_slicer(&[]).is_empty());
assert_eq!(bpsk_slicer(&[0.0, 0.01, -0.01]), [Zero, Zero, One]);
}
#[test]
fn test_error_count() {
assert_eq!(error_count(&[], &[One, Zero]), 0);
assert_eq!(error_count(&[One, Zero], &[]), 0);
let ref_seq = [One, Zero, Zero, One, One, One, Zero, Zero];
let seq = [One, One, Zero, Zero, One, One, Zero, Zero, Zero, One];
assert_eq!(error_count(&seq, &ref_seq), 2);
let ref_seq = [One, Zero, Zero, One, One, One, Zero, Zero, Zero, One];
let seq = [One, One, Zero, Zero, One, One, Zero, Zero];
assert_eq!(error_count(&seq, &ref_seq), 2);
}
}