Skip to main content

comnum_spec/blocks/
mod.rs

1//! The blocks of a declarative link and their registry.
2//!
3//! A link is a list of blocks applied in order to a [`Frame`]. The first block is a
4//! `source`, which draws the transmitted bits and symbols; every other block transforms
5//! the current samples (a channel, a device, a DSP stage, a trained receiver) and may
6//! record diagnostic values (an estimated frequency offset, the iterations of a fit).
7//! [`registry`] lists every block with its parameters, their defaults and their meaning;
8//! the command `comnum blocks` prints it.
9//!
10//! A block is written as a mapping with a `type`, an optional `label` (the prefix of the
11//! values it records, the type by default) and its parameters; a block without
12//! parameters can be written as its name alone. Unknown parameters are errors.
13//!
14//! ```yaml
15//! link:
16//!   - {type: source, constellation: 16-QAM, n_symbols: 4096, symbol_rate: 32e9}
17//!   - {type: pulse_shape, sps: 2, rolloff: 0.1}
18//!   - {type: awgn, snr_db: $snr_db}
19//!   - {type: matched_filter, rolloff: 0.1}
20//!   - decimate
21//! ```
22//!
23//! The wireless blocks ([`wireless`]) add antennas, fading channels, OFDM and MIMO
24//! detection; the coding blocks ([`coding`]) add channel codes, mapping of coded bits
25//! and soft demapping.
26//!
27//! # The model of a trial
28//!
29//! [`Frame`] carries one trial along the link. A simulated source sets the
30//! transmitted symbols (`symbols`, `indices`, `bits`) and the samples; a coded link
31//! starts with `fec_encode`, which draws the information bits (`coding.payload`) and
32//! sets the coded bits, then `map`, which sets the symbols. With several transmit
33//! antennas or two polarisations the samples of stream 0 stay in `samples` and those of
34//! the others go to `antennas`, and [`Layout`] says what they are. The channel blocks
35//! act on every stream and record what a genie-aided receiver knows
36//! ([`ChannelState`]); a transmitter block leaves its frame to its receiver block in
37//! [`InFlight`]. The receiver blocks bring the signal back to one sample per transmitted
38//! symbol, aligned on the symbols, as the metrics need, and declare the noise of their
39//! outputs ([`ReceiverState`]); the soft demapper and the decoder then fill
40//! `coding.llr` and `coding.decoded`.
41//!
42//! # The families of blocks
43//!
44//! | Module | Blocks |
45//! |---|---|
46//! | [`source`] | simulated symbols, measured recordings |
47//! | [`channel`] | noise, phase noise, frequency offset, linear channels, IQ imbalance |
48//! | [`fiber`] | single-polarisation fibre propagation and its compensation |
49//! | [`optical`] | WDM, dual-polarisation propagation, PMD, back-propagation |
50//! | [`dsp`] | shaping, filtering, resampling, clock and carrier recovery, equalisation, frame synchronisation |
51//! | [`wireless`] | antennas, fading, OFDM, MIMO detection |
52//! | [`coding`] | channel codes, probabilistic amplitude shaping, mapping, soft demapping |
53//! | [`ce_ofdm`] | constant-envelope OFDM |
54//! | [`cpm`] | continuous-phase modulation (CPFSK, MSK, GMSK) |
55//! | [`systems`] | the DVB-S2 chain of `comnum-systems` (ADR 0010) |
56//! | [`analysis`] | probes, recordings of the samples (`dump`), the declared posterior of a receiver |
57//!
58//! Every module declares its blocks next to their code (`blocks`: the documentation
59//! and the builder of every block, an [`Extension`] as the blocks of other crates); a test checks
60//! that the parameters a block accepts and those it documents are the same.
61
62use comnum_core::rng::Rng;
63use comnum_core::{Error, Result};
64use comnum_mapping::constellation::{Constellation, ConstellationKind};
65use serde_json::Value as Json;
66
67#[macro_use]
68mod registry;
69#[macro_use]
70pub mod schema;
71mod context;
72mod frame;
73mod per_pol;
74
75pub mod analysis;
76pub mod ce_ofdm;
77pub mod channel;
78pub mod coding;
79pub mod cpm;
80pub mod dsp;
81pub mod fiber;
82pub mod optical;
83pub mod source;
84pub mod systems;
85pub mod wireless;
86
87#[cfg(test)]
88pub(crate) mod tests;
89
90pub use crate::params::parse;
91pub use context::BuildContext;
92pub(crate) use context::merge_fiber;
93use context::{FIBER_DOC, FiberSpec};
94pub use frame::{CaptureState, ChannelState, Coding, Frame, InFlight, Layout, ReceiverState};
95pub use optical::WdmFrame;
96use per_pol::{part, per_pol};
97pub use registry::{BlockInfo, Extension, build, check_link, json_schema, register, registry};
98pub(crate) use registry::{build_parts, with_registry};
99use schema::NumberOrList;
100pub use schema::{Fact, Mapping, NameOr, Named, ParamDefault, ParamInfo, Params};
101pub use wireless::{Csi, OfdmFrame};
102
103/// A block of a link.
104pub trait Block: Send + Sync {
105    /// Apply the block to the signal of one trial, drawing from `rng` only.
106    ///
107    /// # Errors
108    /// Any error of the underlying algorithm (inconsistent signal, invalid parameter).
109    fn apply(&self, s: &mut Frame, rng: &mut Rng) -> Result<()>;
110    /// Whether the block is trained once per condition before the trials.
111    fn needs_training(&self) -> bool {
112        false
113    }
114    /// Train on the signal that reaches the block on an independent training block.
115    ///
116    /// # Errors
117    /// Any error of the training.
118    fn train(&mut self, _s: &Frame, _rng: &mut Rng) -> Result<()> {
119        Ok(())
120    }
121}
122
123block_params! {
124    /// A constellation given as a mapping.
125    pub struct ConstellationSpec {
126        kind: String ["qam"] one_of ["pam", "qam", "psk", "ook"], "the family";
127        m: usize [required] >= 1, "the order";
128    }
129}
130
131/// The value of a `constellation` parameter: a name or a [`ConstellationSpec`].
132pub type ConstellationParam = NameOr<ConstellationSpec>;
133
134/// Parse a constellation, normalised to unit energy: a name of
135/// [`Constellation::from_name`] (`16-QAM`, `qam16`, `QPSK`, `BPSK`, `8-PSK`, `4-PAM`,
136/// `OOK`) or a mapping `{kind, m}` ([`ConstellationSpec`]).
137///
138/// # Errors
139/// [`Error::Config`] for an unknown family or a missing or invalid order.
140pub fn parse_constellation(v: &Json) -> Result<Constellation> {
141    let c = match v {
142        Json::Object(_) => {
143            let q = ConstellationSpec::parse("constellation", v.clone())?;
144            Constellation::new(q.kind.parse::<ConstellationKind>()?, q.m)?
145        }
146        Json::String(s) => Constellation::from_name(s)?,
147        other => return Err(Error::Config(format!("constellation must be a name or {{kind, m}}, got {other}"))),
148    };
149    Ok(c.normalized())
150}