ldpc_toolbox/simulation/
factory.rs

1//! BER test factory.
2//!
3//! This module contains a factory that generates BER test objects as a boxed
4//! trait object using the [`BerTestBuilder`].
5
6use super::{
7    ber::{BerTest, Reporter, Statistics},
8    modulation::{Bpsk, Psk8},
9};
10use crate::{
11    decoder::factory::{DecoderFactory, DecoderImplementation},
12    sparse::SparseMatrix,
13};
14use clap::ValueEnum;
15
16/// BER test.
17///
18/// This trait is used to define trait objects that implement BER tests.
19pub trait Ber {
20    /// Runs the BER test.
21    ///
22    /// This function runs the BER test until completion. It returns a list of
23    /// statistics for each Eb/N0, or an error.
24    fn run(self: Box<Self>) -> Result<Vec<Statistics>, Box<dyn std::error::Error>>;
25
26    /// Returns the frame size of the code.
27    ///
28    /// This corresponds to the frame size after puncturing.
29    fn n(&self) -> usize;
30
31    /// Returns the codeword size of the code.
32    ///
33    /// This corresponds to the codeword size before puncturing.
34    fn n_cw(&self) -> usize;
35
36    /// Returns the number of information bits of the code.
37    fn k(&self) -> usize;
38
39    /// Returns the rate of the code.
40    fn rate(&self) -> f64;
41}
42
43/// BER test builder.
44///
45/// This struct contains all the parameters needed to create a BER test.
46#[derive(Debug)]
47pub struct BerTestBuilder<'a, Dec = DecoderImplementation> {
48    /// LDPC parity check matrix.
49    pub h: SparseMatrix,
50    /// LDPC decoder implementation.
51    pub decoder_implementation: Dec,
52    /// Modulation.
53    pub modulation: Modulation,
54    /// Codeword puncturing pattern.
55    pub puncturing_pattern: Option<&'a [bool]>,
56    /// Codeword interleaving.
57    ///
58    /// A negative value indicates that the columns should be read backwards.
59    pub interleaving_columns: Option<isize>,
60    /// Maximum number of frame errors per Eb/N0.
61    pub max_frame_errors: u64,
62    /// Maximum number of iterations per codeword.
63    pub max_iterations: usize,
64    /// List of Eb/N0's (in dB) to simulate.
65    pub ebn0s_db: &'a [f32],
66    /// An optional reporter object to which the BER test will send periodic
67    /// updates about its progress.
68    pub reporter: Option<Reporter>,
69    /// Maximum number of bit errors that the BCH decoder can correct.
70    ///
71    /// A value of zero means that there is no BCH decoder.
72    pub bch_max_errors: u64,
73    /// Number of worker threads.
74    pub num_workers: usize,
75}
76
77/// Modulation.
78///
79/// This enum represents the modulations that can be simulated.
80#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, ValueEnum)]
81#[clap(rename_all = "UPPER")]
82pub enum Modulation {
83    /// BPSK modulation.
84    Bpsk,
85    /// 8PSK modulation.
86    Psk8,
87}
88
89impl std::str::FromStr for Modulation {
90    type Err = String;
91
92    fn from_str(s: &str) -> Result<Modulation, String> {
93        Ok(match s {
94            "BPSK" => Modulation::Bpsk,
95            "8PSK" => Modulation::Psk8,
96            _ => Err(format!("invalid modulation {s}"))?,
97        })
98    }
99}
100
101impl std::fmt::Display for Modulation {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
103        write!(
104            f,
105            "{}",
106            match self {
107                Modulation::Bpsk => "BPSK",
108                Modulation::Psk8 => "8PSK",
109            }
110        )
111    }
112}
113
114impl<Dec: DecoderFactory> BerTestBuilder<'_, Dec> {
115    /// Create a BER test.
116    ///
117    /// This function only defines the BER test. To run it it is necessary to
118    /// call the [`Ber::run`] method.
119    pub fn build(self) -> Result<Box<dyn Ber>, Box<dyn std::error::Error>> {
120        Ok(match self.modulation {
121            Modulation::Bpsk => Box::new(BerTest::<Bpsk, Dec>::new(
122                self.h,
123                self.decoder_implementation,
124                self.puncturing_pattern,
125                self.interleaving_columns,
126                self.max_frame_errors,
127                self.max_iterations,
128                self.ebn0s_db,
129                self.reporter,
130                self.bch_max_errors,
131                self.num_workers,
132            )?),
133            Modulation::Psk8 => Box::new(BerTest::<Psk8, Dec>::new(
134                self.h,
135                self.decoder_implementation,
136                self.puncturing_pattern,
137                self.interleaving_columns,
138                self.max_frame_errors,
139                self.max_iterations,
140                self.ebn0s_db,
141                self.reporter,
142                self.bch_max_errors,
143                self.num_workers,
144            )?),
145        })
146    }
147}