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