use comnum_core::rng::Rng;
use comnum_core::{Error, Result, ensure};
use std::collections::VecDeque;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Interleaver {
perm: Vec<usize>,
}
impl Interleaver {
pub fn from_permutation(perm: Vec<usize>) -> Result<Interleaver> {
ensure_permutation(&perm, || "the interleaver is not a permutation".into())?;
Ok(Interleaver { perm })
}
pub fn perm(&self) -> &[usize] {
&self.perm
}
pub fn block(rows: usize, cols: usize) -> Interleaver {
Interleaver { perm: (0..rows * cols).map(|i| (i % rows) * cols + i / rows).collect() }
}
pub fn random(n: usize, seed: u64) -> Interleaver {
let mut perm: Vec<usize> = (0..n).collect();
Rng::new(seed).shuffle(&mut perm);
Interleaver { perm }
}
pub fn qpp(k: usize, f1: usize, f2: usize) -> Result<Interleaver> {
let perm: Vec<usize> = (0..k).map(|i| ((f1 * i) % k + (f2 * ((i * i) % k)) % k) % k).collect();
ensure_permutation(&perm, || format!("({f1}, {f2}) is not a permutation polynomial modulo {k}"))?;
Ok(Interleaver { perm })
}
pub fn from_name(name: &str, k: usize) -> Result<Interleaver> {
match name.to_ascii_lowercase().as_str() {
"lte" => {
let (f1, f2) = crate::turbo::lte_qpp(k).ok_or_else(|| {
Error::Config(format!("K = {k} is not a block size of LTE (TS 36.212, Table 5.1.3-3)"))
})?;
Interleaver::qpp(k, f1, f2)
}
_ => Err(Error::Config(format!("unknown interleaver {name:?}: lte"))),
}
}
pub fn len(&self) -> usize {
self.perm.len()
}
pub fn is_empty(&self) -> bool {
self.perm.is_empty()
}
pub fn interleave<T: Copy>(&self, x: &[T]) -> Vec<T> {
self.perm.iter().map(|&p| x[p]).collect()
}
pub fn deinterleave<T: Copy + Default>(&self, y: &[T]) -> Vec<T> {
let mut x = vec![T::default(); self.perm.len()];
for (i, &p) in self.perm.iter().enumerate() {
x[p] = y[i];
}
x
}
}
fn ensure_permutation(perm: &[usize], msg: impl FnOnce() -> String) -> Result<()> {
let mut seen = vec![false; perm.len()];
for &p in perm {
if p >= perm.len() || seen[p] {
return ensure(false, msg);
}
seen[p] = true;
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct ConvolutionalInterleaver<T> {
unit: usize,
deinterleave: bool,
fifos: Vec<VecDeque<T>>,
branch: usize,
}
impl<T: Copy + Default> ConvolutionalInterleaver<T> {
pub fn new(branches: usize, unit: usize) -> Result<Self> {
Self::build(branches, unit, false)
}
pub fn deinterleaver(branches: usize, unit: usize) -> Result<Self> {
Self::build(branches, unit, true)
}
fn build(branches: usize, unit: usize, deinterleave: bool) -> Result<Self> {
ensure(branches >= 1, || "a convolutional interleaver needs at least one branch".into())?;
let depth = |j: usize| unit * if deinterleave { branches - 1 - j } else { j };
let fifos = (0..branches).map(|j| std::iter::repeat_n(T::default(), depth(j)).collect()).collect();
Ok(ConvolutionalInterleaver { unit, deinterleave, fifos, branch: 0 })
}
pub fn branches(&self) -> usize {
self.fifos.len()
}
pub fn unit_depth(&self) -> usize {
self.unit
}
pub fn is_deinterleaver(&self) -> bool {
self.deinterleave
}
pub fn delay(&self, branch: usize) -> usize {
self.branches() * self.fifos[branch % self.branches()].len()
}
pub fn latency(&self) -> usize {
let i = self.branches();
i * self.unit * (i - 1)
}
pub fn reset(&mut self) {
for f in &mut self.fifos {
for v in f.iter_mut() {
*v = T::default();
}
}
self.branch = 0;
}
pub fn push(&mut self, x: T) -> T {
let n = self.fifos.len();
let fifo = &mut self.fifos[self.branch];
self.branch = (self.branch + 1) % n;
match fifo.pop_front() {
Some(y) => {
fifo.push_back(x);
y
}
None => x,
}
}
pub fn process_into(&mut self, x: &[T], out: &mut [T]) -> Result<()> {
comnum_core::same_len(out.len(), x.len(), "interleaved symbols")?;
for (o, &v) in out.iter_mut().zip(x) {
*o = self.push(v);
}
Ok(())
}
pub fn process(&mut self, x: &[T]) -> Vec<T> {
x.iter().map(|&v| self.push(v)).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_lte_interleaver_is_read_by_name() {
assert_eq!(Interleaver::from_name("LTE", 40).unwrap(), Interleaver::qpp(40, 3, 10).unwrap());
assert!(Interleaver::from_name("lte", 41).is_err());
assert!(Interleaver::from_name("umts", 40).is_err());
}
#[test]
fn permutations_invert() {
let x: Vec<u32> = (0..40).collect();
for p in [Interleaver::random(40, 3), Interleaver::block(5, 8), Interleaver::qpp(40, 3, 10).unwrap()] {
let mut sorted = p.perm.clone();
sorted.sort_unstable();
assert_eq!(sorted, x.iter().map(|v| *v as usize).collect::<Vec<_>>());
assert_eq!(p.deinterleave(&p.interleave(&x)), x);
}
assert!(Interleaver::qpp(40, 2, 10).is_err());
}
#[test]
fn a_convolutional_interleaver_delays_branch_j_by_i_m_j_symbols() {
let mut tx = ConvolutionalInterleaver::<u32>::new(12, 17).unwrap();
let x: Vec<u32> = (1..=20_000).collect();
let y = tx.process(&x);
for t in 0..x.len() {
let d = 12 * 17 * (t % 12);
if t + d < y.len() {
assert_eq!(y[t + d], x[t], "symbol {t} of branch {}", t % 12);
}
}
assert_eq!((tx.delay(0), tx.delay(11)), (0, 12 * 17 * 11));
}
#[test]
fn the_deinterleaver_undoes_the_interleaver_after_its_latency() {
for (i, m) in [(12, 17), (5, 3), (1, 4)] {
let mut tx = ConvolutionalInterleaver::new(i, m).unwrap();
let mut rx = ConvolutionalInterleaver::deinterleaver(i, m).unwrap();
let x: Vec<u16> = (0..3000).map(|v| (v * 7 + 1) as u16).collect();
let y = rx.process(&tx.process(&x));
let lat = tx.latency();
assert_eq!(lat, i * m * (i - 1));
assert_eq!(&y[lat..], &x[..x.len() - lat], "I = {i}, M = {m}");
assert!(y[..lat].iter().all(|&v| v == 0), "the initial content");
}
assert!(ConvolutionalInterleaver::<u8>::new(0, 3).is_err());
}
#[test]
fn a_burst_is_spread_over_symbols_i_m_apart() {
let (i, m) = (12, 17);
let mut tx = ConvolutionalInterleaver::new(i, m).unwrap();
let mut rx = ConvolutionalInterleaver::deinterleaver(i, m).unwrap();
let mut y = tx.process(&vec![0u8; 20_000]);
y[9_000..9_000 + i].iter_mut().for_each(|v| *v = 1);
let out = rx.process(&y);
let hit: Vec<usize> = (0..out.len()).filter(|&t| out[t] == 1).collect();
assert_eq!(hit.len(), i);
let gaps: Vec<usize> = hit.windows(2).map(|w| w[1] - w[0]).collect();
assert!(gaps.iter().all(|&g| g >= i * m - 1), "{gaps:?}");
}
#[test]
fn reset_restores_the_initial_state() {
let mut tx = ConvolutionalInterleaver::new(3, 2).unwrap();
let a = tx.process(&[1u8, 2, 3, 4, 5, 6, 7, 8, 9]);
tx.reset();
assert_eq!(tx.process(&[1u8, 2, 3, 4, 5, 6, 7, 8, 9]), a);
}
}