Skip to main content

comnum_fec/
interleaving.rs

1//! Interleavers: permutations of the coded bits (or of their LLRs).
2//!
3//! An [`Interleaver`] holds a permutation $\pi$: the interleaved sequence is
4//! $y_{i} = x_{\pi(i)}$. [`Interleaver::block`] writes row by row and reads column by
5//! column; [`Interleaver::random`] draws a uniform permutation from a seed; the
6//! quadratic permutation polynomial of LTE turbo codes is
7//! [`Interleaver::qpp`], $\pi(i) = (f_{1} i + f_{2} i^{2}) \bmod K$ (Sun & Takeshita 2005).
8//!
9//! A [`ConvolutionalInterleaver`] spreads a stream instead of a block (Forney 1971): a
10//! commutator sends symbol $t$ to branch $j = t \bmod I$, a FIFO of $M j$ cells clocked
11//! once every $I$ symbols, so that symbol $t$ leaves $I M j$ symbols late; its
12//! deinterleaver has the depths $M (I - 1 - j)$, and every symbol crosses the pair in
13//! $I M (I - 1)$ symbols. A burst of $b \le I$ consecutive channel errors lands, at the
14//! output of the deinterleaver, in $b$ symbols at least $I M - 1$ apart. DVB-S and DVB-T
15//! interleave the bytes of their Reed–Solomon codewords with $I = 12$, $M = 17$ (ETSI
16//! EN 300 421, Sec. 4.4.2, Figure 4).
17//!
18//! # Examples
19//! ```
20//! use comnum_fec::interleaving::Interleaver;
21//! let p = Interleaver::block(2, 3);
22//! assert_eq!(p.interleave(&[0, 1, 2, 3, 4, 5]), vec![0, 3, 1, 4, 2, 5]);
23//! assert_eq!(p.deinterleave(&p.interleave(&[9, 8, 7, 6, 5, 4])), vec![9, 8, 7, 6, 5, 4]);
24//! ```
25//!
26//! # References
27//!
28//! 1. J. Sun and O. Y. Takeshita, "Interleavers for turbo codes using permutation
29//!    polynomials over integer rings," *IEEE Trans. Inf. Theory*, 51(1), 101–119, 2005.
30//! 2. G. D. Forney, "Burst-correcting codes for the classic bursty channel," *IEEE
31//!    Trans. Commun. Technol.*, 19(5), 772–781, 1971.
32//! 3. ETSI EN 300 421 V1.1.2, *DVB; Framing structure, channel coding and modulation for
33//!    11/12 GHz satellite services*, 1997, Sec. 4.4.2.
34
35use comnum_core::rng::Rng;
36use comnum_core::{Error, Result, ensure};
37use std::collections::VecDeque;
38
39/// A permutation of the positions of a block.
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct Interleaver {
42    /// $\pi$: output position $i$ takes input position `perm[i]`.
43    perm: Vec<usize>,
44}
45
46impl Interleaver {
47    /// The interleaver of the permutation `perm`: output position $i$ takes input
48    /// position `perm[i]`.
49    ///
50    /// # Errors
51    /// [`comnum_core::Error::Config`] if `perm` is not a permutation of $0, \dots, n - 1$.
52    ///
53    /// # Examples
54    /// ```
55    /// use comnum_fec::interleaving::Interleaver;
56    /// let p = Interleaver::from_permutation(vec![2, 0, 1])?;
57    /// assert_eq!(p.interleave(&['a', 'b', 'c']), ['c', 'a', 'b']);
58    /// assert!(Interleaver::from_permutation(vec![0, 0, 1]).is_err());
59    /// # Ok::<(), comnum_core::Error>(())
60    /// ```
61    pub fn from_permutation(perm: Vec<usize>) -> Result<Interleaver> {
62        ensure_permutation(&perm, || "the interleaver is not a permutation".into())?;
63        Ok(Interleaver { perm })
64    }
65
66    /// $\pi$: output position $i$ takes input position `perm()[i]`.
67    pub fn perm(&self) -> &[usize] {
68        &self.perm
69    }
70
71    /// Write row by row into `rows × cols`, read column by column.
72    pub fn block(rows: usize, cols: usize) -> Interleaver {
73        Interleaver { perm: (0..rows * cols).map(|i| (i % rows) * cols + i / rows).collect() }
74    }
75
76    /// A uniform random permutation of `n` positions (seeded).
77    pub fn random(n: usize, seed: u64) -> Interleaver {
78        let mut perm: Vec<usize> = (0..n).collect();
79        Rng::new(seed).shuffle(&mut perm);
80        Interleaver { perm }
81    }
82
83    /// The quadratic permutation polynomial $\pi(i) = (f_{1} i + f_{2} i^{2}) \bmod K$.
84    ///
85    /// # Errors
86    /// [`comnum_core::Error::Config`] if the polynomial is not a permutation.
87    pub fn qpp(k: usize, f1: usize, f2: usize) -> Result<Interleaver> {
88        let perm: Vec<usize> = (0..k).map(|i| ((f1 * i) % k + (f2 * ((i * i) % k)) % k) % k).collect();
89        ensure_permutation(&perm, || format!("({f1}, {f2}) is not a permutation polynomial modulo {k}"))?;
90        Ok(Interleaver { perm })
91    }
92
93    /// The interleaver of a standard by name, for blocks of `k` bits: `lte`, the QPP
94    /// interleaver of the LTE turbo code ([`crate::turbo::lte_qpp`], TS 36.212,
95    /// Table 5.1.3-3), case-insensitive.
96    ///
97    /// # Errors
98    /// [`comnum_core::Error::Config`] for another name, or a size `k` outside the table.
99    pub fn from_name(name: &str, k: usize) -> Result<Interleaver> {
100        match name.to_ascii_lowercase().as_str() {
101            "lte" => {
102                let (f1, f2) = crate::turbo::lte_qpp(k).ok_or_else(|| {
103                    Error::Config(format!("K = {k} is not a block size of LTE (TS 36.212, Table 5.1.3-3)"))
104                })?;
105                Interleaver::qpp(k, f1, f2)
106            }
107            _ => Err(Error::Config(format!("unknown interleaver {name:?}: lte"))),
108        }
109    }
110
111    /// Length of the block.
112    pub fn len(&self) -> usize {
113        self.perm.len()
114    }
115
116    /// Whether the block is empty.
117    pub fn is_empty(&self) -> bool {
118        self.perm.is_empty()
119    }
120
121    /// $y_{i} = x_{\pi(i)}$.
122    ///
123    /// # Panics
124    /// If `x` is shorter than the permutation.
125    pub fn interleave<T: Copy>(&self, x: &[T]) -> Vec<T> {
126        self.perm.iter().map(|&p| x[p]).collect()
127    }
128
129    /// The inverse permutation.
130    ///
131    /// # Panics
132    /// If `y` is shorter than the permutation.
133    pub fn deinterleave<T: Copy + Default>(&self, y: &[T]) -> Vec<T> {
134        let mut x = vec![T::default(); self.perm.len()];
135        for (i, &p) in self.perm.iter().enumerate() {
136            x[p] = y[i];
137        }
138        x
139    }
140}
141
142/// [`comnum_core::Error::Config`] with the message of `msg` unless `perm` holds every
143/// position below its length once.
144fn ensure_permutation(perm: &[usize], msg: impl FnOnce() -> String) -> Result<()> {
145    let mut seen = vec![false; perm.len()];
146    for &p in perm {
147        if p >= perm.len() || seen[p] {
148            return ensure(false, msg);
149        }
150        seen[p] = true;
151    }
152    Ok(())
153}
154
155/// A convolutional (Forney) interleaver or deinterleaver of $I$ branches of unit depth
156/// $M$ (see the module): a stream processed symbol by symbol, its FIFOs holding the
157/// symbols still in flight.
158///
159/// Branch $j$ of the interleaver holds $M j$ cells; branch $j$ of the deinterleaver
160/// holds $M (I - 1 - j)$ cells. Both start filled with `T::default()`; the commutator
161/// starts on branch 0, so that the first symbol of a stream (the sync byte of DVB) takes
162/// the branch without delay. Symbol $t$ leaves the interleaver $I M (t \bmod I)$ symbols
163/// late, and the pair delays every symbol by [`ConvolutionalInterleaver::latency`].
164///
165/// # Examples
166/// ```
167/// use comnum_fec::interleaving::ConvolutionalInterleaver;
168/// // the interleaver of DVB-S and DVB-T: I = 12, M = 17
169/// let mut tx = ConvolutionalInterleaver::new(12, 17)?;
170/// let mut rx = ConvolutionalInterleaver::deinterleaver(12, 17)?;
171/// let data: Vec<u8> = (0..5000).map(|i| (i % 251) as u8).collect();
172/// let y = rx.process(&tx.process(&data));
173/// let lat = tx.latency();                          // 12 · 17 · 11 = 2244 symbols
174/// assert_eq!(&y[lat..], &data[..data.len() - lat]);
175/// # Ok::<(), comnum_core::Error>(())
176/// ```
177#[derive(Clone, Debug)]
178pub struct ConvolutionalInterleaver<T> {
179    unit: usize,
180    deinterleave: bool,
181    /// The FIFO of every branch (empty for a branch without delay).
182    fifos: Vec<VecDeque<T>>,
183    /// The branch of the next symbol.
184    branch: usize,
185}
186
187impl<T: Copy + Default> ConvolutionalInterleaver<T> {
188    /// The interleaver of `branches` branches of unit depth `unit` ($I$, $M$).
189    ///
190    /// # Errors
191    /// [`comnum_core::Error::Config`] without a branch.
192    pub fn new(branches: usize, unit: usize) -> Result<Self> {
193        Self::build(branches, unit, false)
194    }
195
196    /// The deinterleaver of [`ConvolutionalInterleaver::new`] with the same parameters.
197    ///
198    /// # Errors
199    /// As [`ConvolutionalInterleaver::new`].
200    pub fn deinterleaver(branches: usize, unit: usize) -> Result<Self> {
201        Self::build(branches, unit, true)
202    }
203
204    fn build(branches: usize, unit: usize, deinterleave: bool) -> Result<Self> {
205        ensure(branches >= 1, || "a convolutional interleaver needs at least one branch".into())?;
206        let depth = |j: usize| unit * if deinterleave { branches - 1 - j } else { j };
207        let fifos = (0..branches).map(|j| std::iter::repeat_n(T::default(), depth(j)).collect()).collect();
208        Ok(ConvolutionalInterleaver { unit, deinterleave, fifos, branch: 0 })
209    }
210
211    /// The number of branches $I$.
212    pub fn branches(&self) -> usize {
213        self.fifos.len()
214    }
215
216    /// The unit depth $M$.
217    pub fn unit_depth(&self) -> usize {
218        self.unit
219    }
220
221    /// Whether this is the deinterleaver.
222    pub fn is_deinterleaver(&self) -> bool {
223        self.deinterleave
224    }
225
226    /// The delay (in symbols) of the symbols of branch `branch`: $I M j$ for the
227    /// interleaver, $I M (I - 1 - j)$ for the deinterleaver.
228    pub fn delay(&self, branch: usize) -> usize {
229        self.branches() * self.fifos[branch % self.branches()].len()
230    }
231
232    /// The delay of the interleaver and deinterleaver together, $I M (I - 1)$ symbols.
233    pub fn latency(&self) -> usize {
234        let i = self.branches();
235        i * self.unit * (i - 1)
236    }
237
238    /// Back to the initial state: the FIFOs filled with `T::default()`, the commutator on
239    /// branch 0.
240    pub fn reset(&mut self) {
241        for f in &mut self.fifos {
242            for v in f.iter_mut() {
243                *v = T::default();
244            }
245        }
246        self.branch = 0;
247    }
248
249    /// One symbol in, one symbol out.
250    pub fn push(&mut self, x: T) -> T {
251        let n = self.fifos.len();
252        let fifo = &mut self.fifos[self.branch];
253        self.branch = (self.branch + 1) % n;
254        match fifo.pop_front() {
255            Some(y) => {
256                fifo.push_back(x);
257                y
258            }
259            None => x,
260        }
261    }
262
263    /// The symbols of `x` through the interleaver, into `out` (as many).
264    ///
265    /// # Errors
266    /// [`comnum_core::Error::Shape`] if `out` does not have the length of `x`.
267    pub fn process_into(&mut self, x: &[T], out: &mut [T]) -> Result<()> {
268        comnum_core::same_len(out.len(), x.len(), "interleaved symbols")?;
269        for (o, &v) in out.iter_mut().zip(x) {
270            *o = self.push(v);
271        }
272        Ok(())
273    }
274
275    /// The symbols of `x` through the interleaver.
276    pub fn process(&mut self, x: &[T]) -> Vec<T> {
277        x.iter().map(|&v| self.push(v)).collect()
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn the_lte_interleaver_is_read_by_name() {
287        assert_eq!(Interleaver::from_name("LTE", 40).unwrap(), Interleaver::qpp(40, 3, 10).unwrap());
288        assert!(Interleaver::from_name("lte", 41).is_err());
289        assert!(Interleaver::from_name("umts", 40).is_err());
290    }
291
292    #[test]
293    fn permutations_invert() {
294        let x: Vec<u32> = (0..40).collect();
295        for p in [Interleaver::random(40, 3), Interleaver::block(5, 8), Interleaver::qpp(40, 3, 10).unwrap()] {
296            let mut sorted = p.perm.clone();
297            sorted.sort_unstable();
298            assert_eq!(sorted, x.iter().map(|v| *v as usize).collect::<Vec<_>>());
299            assert_eq!(p.deinterleave(&p.interleave(&x)), x);
300        }
301        // the QPP of LTE for K = 40 is (3, 10); (2, 10) is not a permutation
302        assert!(Interleaver::qpp(40, 2, 10).is_err());
303    }
304    #[test]
305    fn a_convolutional_interleaver_delays_branch_j_by_i_m_j_symbols() {
306        // EN 300 421 Sec. 4.4.2: branch j holds M j cells, clocked once every I symbols
307        let mut tx = ConvolutionalInterleaver::<u32>::new(12, 17).unwrap();
308        let x: Vec<u32> = (1..=20_000).collect();
309        let y = tx.process(&x);
310        for t in 0..x.len() {
311            let d = 12 * 17 * (t % 12);
312            if t + d < y.len() {
313                assert_eq!(y[t + d], x[t], "symbol {t} of branch {}", t % 12);
314            }
315        }
316        assert_eq!((tx.delay(0), tx.delay(11)), (0, 12 * 17 * 11));
317    }
318
319    #[test]
320    fn the_deinterleaver_undoes_the_interleaver_after_its_latency() {
321        for (i, m) in [(12, 17), (5, 3), (1, 4)] {
322            let mut tx = ConvolutionalInterleaver::new(i, m).unwrap();
323            let mut rx = ConvolutionalInterleaver::deinterleaver(i, m).unwrap();
324            let x: Vec<u16> = (0..3000).map(|v| (v * 7 + 1) as u16).collect();
325            let y = rx.process(&tx.process(&x));
326            let lat = tx.latency();
327            assert_eq!(lat, i * m * (i - 1));
328            assert_eq!(&y[lat..], &x[..x.len() - lat], "I = {i}, M = {m}");
329            assert!(y[..lat].iter().all(|&v| v == 0), "the initial content");
330        }
331        assert!(ConvolutionalInterleaver::<u8>::new(0, 3).is_err());
332    }
333
334    #[test]
335    fn a_burst_is_spread_over_symbols_i_m_apart() {
336        // I consecutive channel errors land in I symbols of the stream, I M - 1 apart at least
337        let (i, m) = (12, 17);
338        let mut tx = ConvolutionalInterleaver::new(i, m).unwrap();
339        let mut rx = ConvolutionalInterleaver::deinterleaver(i, m).unwrap();
340        let mut y = tx.process(&vec![0u8; 20_000]);
341        y[9_000..9_000 + i].iter_mut().for_each(|v| *v = 1);
342        let out = rx.process(&y);
343        let hit: Vec<usize> = (0..out.len()).filter(|&t| out[t] == 1).collect();
344        assert_eq!(hit.len(), i);
345        let gaps: Vec<usize> = hit.windows(2).map(|w| w[1] - w[0]).collect();
346        assert!(gaps.iter().all(|&g| g >= i * m - 1), "{gaps:?}");
347    }
348
349    #[test]
350    fn reset_restores_the_initial_state() {
351        let mut tx = ConvolutionalInterleaver::new(3, 2).unwrap();
352        let a = tx.process(&[1u8, 2, 3, 4, 5, 6, 7, 8, 9]);
353        tx.reset();
354        assert_eq!(tx.process(&[1u8, 2, 3, 4, 5, 6, 7, 8, 9]), a);
355    }
356}