comnum_spec/blocks/schema/mod.rs
1//! The single description of a block: its parameters and its ports.
2//!
3//! The parameters of a block are declared once, with
4//! [`block_params!`](crate::block_params): the name and the Rust type of every
5//! parameter, its default, its unit, its accepted values, its bounds and its meaning.
6//! The macro writes the structure, its parser ([`Params`]) and its documentation
7//! ([`ParamInfo`]), so that what a block accepts, what `comnum blocks` prints, what the
8//! Python classes of `comnum.blocks` take and the JSON Schema of the links
9//! ([`json_schema`]) cannot differ. A block of another crate declares its parameters the
10//! same way (see [`register`](super::register)).
11//!
12//! ```
13//! use comnum_spec::blocks::Params;
14//! comnum_spec::block_params! {
15//! struct AwgnSpec {
16//! snr_db: f64 [required] "dB", "signal-to-noise ratio";
17//! reference: String ["signal"] one_of ["signal", "unit"], "power reference of the SNR";
18//! bandwidth_hz: Option<f64> [absent "symbol rate"] "Hz" > 0, "noise bandwidth";
19//! }
20//! }
21//! let p = AwgnSpec::parse("awgn", serde_json::json!({"snr_db": 10})).unwrap();
22//! assert_eq!((p.snr_db, p.reference.as_str(), p.bandwidth_hz), (10.0, "signal", None));
23//! assert_eq!(AwgnSpec::params()[2].default_text(), "symbol rate");
24//! assert!(AwgnSpec::parse("awgn", serde_json::json!({"snr_db": 10, "bandwidth_hz": 0})).is_err());
25//! ```
26//!
27//! A default is `required`, `absent` (an `Option` left to `None`, with the text that says
28//! what the block then does) or a JSON value, read as the value of a specification
29//! would be (`VALUE => "TEXT"` shows it as `TEXT`). Accepted values (`one_of`) are
30//! compared without case; bounds (`>`, `>=`, `<=`, `<`) apply to a number and to every
31//! number of a list.
32//!
33//! A parameter whose value is a mapping of parameters of its own (the fibre of the
34//! optical blocks, the pilots of `ofdm_tx`) is declared with `block_params!` too, and
35//! typed [`Mapping`] (or [`NameOr`], a name or such a mapping): its keys reach the
36//! documentation, the JSON Schema and the Python classes, and are checked as those of
37//! a block when the block parses it.
38//!
39//! # Ports
40//!
41//! A block also declares what it reads from the [`Frame`](super::Frame) of a trial and what it may
42//! add to it, as [`Fact`]s: `awgn` needs `samples` and gives `noise`, `demap` needs
43//! `samples`, `symbols`, `constellation` and `noise` and gives `llr`. [`check_link`]
44//! checks a link before it runs, so that a missing block is named at once (`demap
45//! needs noise: add awgn, awgn_ebn0, noise_estimate… before it`), and a graphical editor
46//! can tell which blocks may follow which. In debug builds every block also checks
47//! what it gives while it runs: a fact that it adds to the frame must be declared in its
48//! `gives` (what it needs is checked once, before the link runs, by [`check_link`]).
49
50#[macro_use]
51mod macros;
52mod fact;
53mod json_schema;
54mod param;
55
56#[cfg(debug_assertions)]
57pub(crate) use fact::Checked;
58pub use fact::{Fact, check_link};
59pub use json_schema::{json_schema, object_schema, variants_schema};
60pub(crate) use param::NumberOrList;
61pub use param::{Mapping, NameOr, Named, ParamDefault, ParamInfo, ParamType, Params, choice, mapping, shown, take};