Skip to main content

dune_netlist/
ir.rs

1//! Intermediate representation of a parsed deck (PLAN.md §2.1).
2//!
3//! Faithful to the 3f5 contract: identifiers are lowercased (the C
4//! lowercases the whole deck), parameter names are the 3f5 keywords,
5//! nodes are strings and `0` is ground. Everything carries a [`Span`].
6
7use crate::span::Span;
8
9/// A lowercased identifier (device, node, model or subcircuit name).
10pub type Ident = String;
11
12#[derive(Clone, Debug, Default)]
13pub struct Netlist {
14    pub title: String,
15    /// Flat element list (subcircuits already expanded).
16    pub elements: Vec<Element>,
17    pub models: Vec<Model>,
18    pub analyses: Vec<Analysis>,
19    /// `.options`, as given; defaults live in `dune-core::consts`.
20    pub options: Vec<Param>,
21    /// `.print` / `.plot` / `.save` / `.four`.
22    pub outputs: Vec<Output>,
23    /// `.ic` and `.nodeset` entries.
24    pub node_inits: Vec<NodeInit>,
25    /// `.measure` / `.meas` cards (ngspice grammar; pure
26    /// post-processing, never part of the simulation).
27    pub measures: Vec<Measure>,
28    /// `.control` … `.endc` lines, verbatim and uninterpreted.
29    pub control: Vec<String>,
30    /// `.width out=N`: print column width (the `in=` half only ever
31    /// mattered to punched-card decks and is ignored).
32    pub width_out: Option<usize>,
33}
34
35/// One circuit element (an instance line).
36#[derive(Clone, Debug)]
37pub struct Element {
38    /// Full lowercased name; the first letter is the device type.
39    /// Subcircuit expansion produces dotted names (`x1.r1`).
40    pub name: Ident,
41    /// Connection nodes, in card order (empty for `K`).
42    pub nodes: Vec<Ident>,
43    pub device: Device,
44    pub span: Span,
45}
46
47/// Extra per-card data, one variant per 3f5 device letter.
48///
49/// `params` hold the keyword parameters exactly as the card gave
50/// them (3f5 keywords, lowercase); validation against each device's
51/// real parameter table is `dune-core`'s job when the device is
52/// implemented.
53#[derive(Clone, Debug)]
54pub enum Device {
55    /// `R`: `RXXXXXXX N1 N2 VALUE` / semiconductor form with MNAME.
56    Resistor {
57        value: Option<f64>,
58        model: Option<Ident>,
59        params: Vec<Param>,
60    },
61    /// `C`: `CXXXXXXX N+ N- VALUE <IC=INCOND>` / semiconductor form.
62    Capacitor {
63        value: Option<f64>,
64        model: Option<Ident>,
65        params: Vec<Param>,
66    },
67    /// `L`: `LYYYYYYY N+ N- VALUE <IC=INCOND>`.
68    Inductor {
69        value: Option<f64>,
70        params: Vec<Param>,
71    },
72    /// `K`: `KXXXXXXX LYYYYYYY LZZZZZZZ VALUE` (no nodes).
73    Mutual { l1: Ident, l2: Ident, k: f64 },
74    /// `V`/`I`: independent source.
75    Source {
76        kind: SourceKind,
77        dc: Option<f64>,
78        ac: Option<AcSpec>,
79        tran: Option<TranFn>,
80        /// `DISTOF1 <MAG <PHASE>>`.
81        distof1: Option<DistoSpec>,
82        distof2: Option<DistoSpec>,
83        /// `PORTNUM N <Z0 Z>` (ngspice RF port, V sources only):
84        /// present only when the card makes the source a port —
85        /// `N > 0` and a positive reference impedance — as
86        /// `VSRCtemp` decides it.
87        port: Option<RfPort>,
88    },
89    /// `E`: VCVS, 4 nodes + gain.
90    Vcvs { gain: Option<f64> },
91    /// `G`: VCCS, 4 nodes + transconductance.
92    Vccs { gain: Option<f64> },
93    /// `F`: CCCS, 2 nodes + controlling V-source + gain.
94    Cccs { vname: Ident, gain: Option<f64> },
95    /// `H`: CCVS, 2 nodes + controlling V-source + transresistance.
96    Ccvs { vname: Ident, gain: Option<f64> },
97    /// `D`: `DXXXXXXX N+ N- MNAME <AREA> <OFF> <IC=VD> <TEMP=T>`.
98    Diode {
99        model: Ident,
100        area: Option<f64>,
101        off: bool,
102        params: Vec<Param>,
103    },
104    /// `Q`: 3 or 4 nodes (optional substrate) + model.
105    Bjt {
106        model: Ident,
107        area: Option<f64>,
108        off: bool,
109        params: Vec<Param>,
110    },
111    /// `J`: `JXXXXXXX ND NG NS MNAME <AREA> <OFF> <IC=…>`.
112    Jfet {
113        model: Ident,
114        area: Option<f64>,
115        off: bool,
116        params: Vec<Param>,
117    },
118    /// `Z`: MESFET, same shape as `J`.
119    Mesfet {
120        model: Ident,
121        area: Option<f64>,
122        off: bool,
123        params: Vec<Param>,
124    },
125    /// `M`: 4 nodes + model + geometry parameters.
126    Mosfet { model: Ident, params: Vec<Param> },
127    /// `S`: voltage-controlled switch, 4 nodes + model.
128    SwitchV { model: Ident, state: Option<OnOff> },
129    /// `W`: current-controlled switch, 2 nodes + V-source + model.
130    SwitchI {
131        vname: Ident,
132        model: Ident,
133        state: Option<OnOff>,
134    },
135    /// `T`: lossless transmission line, 4 nodes, `Z0=` etc.
136    TlineIdeal { params: Vec<Param> },
137    /// `O`: lossy transmission line, 4 nodes + LTRA model.
138    TlineLossy { model: Ident },
139    /// `U`: uniform distributed RC, 3 nodes + URC model.
140    Urc { model: Ident, params: Vec<Param> },
141    /// `B`: non-linear dependent source (`ASRC`), `V=EXPR` or
142    /// `I=EXPR`; when the card gives both, the last one wins, as in
143    /// `INPdevParse` + `ASRCparam`.
144    Behavioral { kind: SourceKind, expr: Expr },
145    /// `X`: subcircuit call, only seen before expansion.
146    SubcktCall { subname: Ident },
147}
148
149#[derive(Clone, Copy, Debug, PartialEq, Eq)]
150pub enum SourceKind {
151    Voltage,
152    Current,
153}
154
155/// A B-source expression: the 3f5 parse tree (`INPparseNode`,
156/// `inpptree.h`) as `PTparse` builds it — no constant folding (only
157/// the derivative trees fold, in `dune-core`) and every name already
158/// resolved. Built by [`crate::parse_expr`].
159#[derive(Clone, Debug, PartialEq)]
160pub enum Expr {
161    /// `PT_CONSTANT`: a number or one of the constants `e`, `pi`.
162    Num(f64),
163    /// `v(node)` (`PT_VAR` of type `IF_NODE`). `v(a,b)` is parsed
164    /// into `v(a) - v(b)`, as `mkfnode` does.
165    V(Ident),
166    /// `i(vname)` (`PT_VAR` of type `IF_INSTANCE`): the branch current
167    /// of a device with a branch equation (V, E, H, L, a voltage-type
168    /// B).
169    I(Ident),
170    /// `PT_PLUS` … `PT_POWER`.
171    Bin(ExprOp, Box<Expr>, Box<Expr>),
172    /// `PT_FUNCTION` (unary minus included, as `ExprFunc::Neg`).
173    Call(ExprFunc, Box<Expr>),
174}
175
176/// The binary operators of the 3f5 expression grammar (`ops[]` in
177/// `inpptree.c`).
178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
179pub enum ExprOp {
180    Plus,
181    Minus,
182    Times,
183    Divide,
184    Power,
185}
186
187/// The functions of `ptfuncs.c` (`funcs[]` in `inpptree.c`), with
188/// their 3f5 semantics: `log` is the base-10 logarithm, `ln`/`log`/
189/// `sqrt` act on `|x|`, `u` is the unit step (½ at 0), `uramp` the
190/// unit ramp.
191#[derive(Clone, Copy, Debug, PartialEq, Eq)]
192pub enum ExprFunc {
193    Abs,
194    Acos,
195    Acosh,
196    Asin,
197    Asinh,
198    Atan,
199    Atanh,
200    Cos,
201    Cosh,
202    Exp,
203    Ln,
204    Log,
205    Sgn,
206    Sin,
207    Sinh,
208    Sqrt,
209    Tan,
210    Tanh,
211    U,
212    Uramp,
213    /// Unary minus (`PTF_UMINUS`, the function named `-`).
214    Neg,
215}
216
217impl ExprFunc {
218    /// The 3f5 function table, by name (`funcs[]`; `-` is reached
219    /// only through the unary-minus token).
220    pub const TABLE: [(&'static str, ExprFunc); 20] = [
221        ("abs", ExprFunc::Abs),
222        ("acos", ExprFunc::Acos),
223        ("acosh", ExprFunc::Acosh),
224        ("asin", ExprFunc::Asin),
225        ("asinh", ExprFunc::Asinh),
226        ("atan", ExprFunc::Atan),
227        ("atanh", ExprFunc::Atanh),
228        ("cos", ExprFunc::Cos),
229        ("cosh", ExprFunc::Cosh),
230        ("exp", ExprFunc::Exp),
231        ("ln", ExprFunc::Ln),
232        ("log", ExprFunc::Log),
233        ("sgn", ExprFunc::Sgn),
234        ("sin", ExprFunc::Sin),
235        ("sinh", ExprFunc::Sinh),
236        ("sqrt", ExprFunc::Sqrt),
237        ("tan", ExprFunc::Tan),
238        ("tanh", ExprFunc::Tanh),
239        ("u", ExprFunc::U),
240        ("uramp", ExprFunc::Uramp),
241    ];
242
243    /// The name the function is written with (`-` for [`ExprFunc::Neg`]).
244    pub fn name(self) -> &'static str {
245        if self == ExprFunc::Neg {
246            return "-";
247        }
248        Self::TABLE
249            .iter()
250            .find(|(_, f)| *f == self)
251            .map_or("?", |(n, _)| n)
252    }
253}
254
255impl Expr {
256    /// Visit every `v()` node name and `i()` device name, mutably
257    /// (subcircuit expansion renames them). Depth is bounded by the
258    /// parser, so the recursion is too.
259    pub fn visit_refs_mut(
260        &mut self,
261        node: &mut dyn FnMut(&mut Ident),
262        device: &mut dyn FnMut(&mut Ident),
263    ) {
264        match self {
265            Expr::Num(_) => {}
266            Expr::V(n) => node(n),
267            Expr::I(d) => device(d),
268            Expr::Bin(_, l, r) => {
269                l.visit_refs_mut(node, device);
270                r.visit_refs_mut(node, device);
271            }
272            Expr::Call(_, a) => a.visit_refs_mut(node, device),
273        }
274    }
275}
276
277#[derive(Clone, Copy, Debug, PartialEq, Eq)]
278pub enum OnOff {
279    On,
280    Off,
281}
282
283/// `AC <ACMAG <ACPHASE>>` — defaults are the 3f5's (magnitude 1,
284/// phase 0 when omitted).
285#[derive(Clone, Copy, Debug, PartialEq)]
286pub struct AcSpec {
287    pub mag: f64,
288    pub phase: f64,
289}
290
291/// An ngspice RF port on a V source (`PORTNUM N <Z0 Z>`, `vsrc.c`
292/// under `RFSPICE`): the source sits behind a series reference
293/// impedance `z0` (default 50 Ω) and `.sp` excites it as port `num`.
294#[derive(Clone, Copy, Debug, PartialEq)]
295pub struct RfPort {
296    /// Port index, 1-based (`VSRCportNum`).
297    pub num: u32,
298    /// Reference impedance in ohms (`VSRCportZ0`), > 0.
299    pub z0: f64,
300}
301
302/// `DISTOF1/DISTOF2 <MAG <PHASE>>` (defaults 1 and 0).
303#[derive(Clone, Copy, Debug, PartialEq)]
304pub struct DistoSpec {
305    pub mag: f64,
306    pub phase: f64,
307}
308
309/// A time-dependent source function. Arguments are stored exactly as
310/// given; defaults (many depend on TSTEP/TSTOP) are applied by
311/// `dune-core` at analysis time, as the 3f5 does.
312#[derive(Clone, Debug, PartialEq)]
313pub struct TranFn {
314    pub kind: TranFnKind,
315    pub args: Vec<f64>,
316}
317
318#[derive(Clone, Copy, Debug, PartialEq, Eq)]
319pub enum TranFnKind {
320    Pulse,
321    Sin,
322    Exp,
323    Pwl,
324    Sffm,
325    /// ngspice-47 `TRNOISE(NA NT NALPHA NAMP RTSAM RTSCAPT RTSEMT)`:
326    /// transient noise (white, 1/f^alpha, random telegraph).
327    TrNoise,
328    /// ngspice-47 `TRRANDOM(TYPE TS TD PARAM1 PARAM2)`: a random
329    /// value held for `TS` seconds.
330    TrRandom,
331}
332
333/// A keyword parameter from a card: `IC=3`, `OFF`, `IC=2,0.5,0,0.2`,
334/// `METHOD=GEAR`.
335#[derive(Clone, Debug)]
336pub struct Param {
337    /// Lowercased 3f5 keyword.
338    pub name: Ident,
339    pub value: ParamValue,
340    pub span: Span,
341}
342
343#[derive(Clone, Debug, PartialEq)]
344pub enum ParamValue {
345    /// Bare keyword (`off`, `uic`, `acct`).
346    Flag,
347    Num(f64),
348    /// Consecutive numbers after the keyword (`ic=2,0.5,0,0.2`).
349    NumList(Vec<f64>),
350    /// Non-numeric value (`method=gear`).
351    Str(String),
352}
353
354/// `.model MNAME TYPE(PNAME1=PVAL1 …)`.
355#[derive(Clone, Debug)]
356pub struct Model {
357    pub name: Ident,
358    /// 3f5 type name: `r c sw csw urc ltra d npn pnp njf pjf nmos
359    /// pmos nmf pmf`.
360    pub mtype: Ident,
361    pub params: Vec<Param>,
362    pub span: Span,
363}
364
365/// An analysis request (one dot card).
366#[derive(Clone, Debug)]
367pub enum Analysis {
368    /// `.op`
369    Op { span: Span },
370    /// `.dc SRCNAM VSTART VSTOP VINCR [SRC2 START2 STOP2 INCR2]`
371    Dc { sweeps: Vec<DcSweep>, span: Span },
372    /// `.ac DEC|OCT|LIN N FSTART FSTOP`
373    Ac {
374        variation: Variation,
375        points: f64,
376        fstart: f64,
377        fstop: f64,
378        span: Span,
379    },
380    /// `.tran TSTEP TSTOP <TSTART <TMAX>> <UIC>`
381    Tran {
382        tstep: f64,
383        tstop: f64,
384        tstart: Option<f64>,
385        tmax: Option<f64>,
386        uic: bool,
387        span: Span,
388    },
389    /// `.pz NODE1 NODE2 NODE3 NODE4 CUR|VOL POL|ZER|PZ`
390    Pz {
391        nodes: [Ident; 4],
392        input: PzInput,
393        which: PzWhich,
394        span: Span,
395    },
396    /// `.noise V(OUT<,REF>) SRC DEC|LIN|OCT PTS FSTART FSTOP <PPS>`
397    Noise {
398        output: Ident,
399        output_ref: Option<Ident>,
400        src: Ident,
401        variation: Variation,
402        points: f64,
403        fstart: f64,
404        fstop: f64,
405        pts_per_summary: Option<f64>,
406        span: Span,
407    },
408    /// `.disto DEC|OCT|LIN NP FSTART FSTOP <F2OVERF1>`
409    Disto {
410        variation: Variation,
411        points: f64,
412        fstart: f64,
413        fstop: f64,
414        f2overf1: Option<f64>,
415        span: Span,
416    },
417    /// `.sens OUTVAR [FILTER…] [AC DEC|OCT|LIN N FSTART FSTOP | DC]`
418    /// (the filters — glob patterns over the sensitivity vector
419    /// names, `*` and `?` — are ngspice's extension of the 3f5 card).
420    Sens {
421        outvar: OutVar,
422        filters: Vec<Ident>,
423        ac: Option<(Variation, f64, f64, f64)>,
424        span: Span,
425    },
426    /// `.sp DEC|OCT|LIN N FSTART FSTOP <DONOISE>` — ngspice's
427    /// S-parameter analysis over the `PORTNUM` V sources
428    /// (`inp2dot.c` `dot_sp`; `DONOISE` is the integer 1 to add the
429    /// noise-correlation outputs).
430    Sp {
431        variation: Variation,
432        points: f64,
433        fstart: f64,
434        fstop: f64,
435        donoise: bool,
436        span: Span,
437    },
438    /// `.pss FREQ TSTAB OSCNODE <POINTS <HARMS <SCITER <STEADYCOEFF>>>>
439    /// <UIC>` — the periodic steady state, in ngspice-47's positional
440    /// grammar (`inp2dot.c` `dot_pss`; there every field up to
441    /// STEADYCOEFF is required). `FREQ = 0` asks Dune to take the
442    /// fundamental from the periodic sources.
443    Pss {
444        freq: f64,
445        tstab: f64,
446        osc_node: Ident,
447        points: Option<f64>,
448        harmonics: Option<f64>,
449        sc_iter: Option<f64>,
450        steady_coeff: Option<f64>,
451        uic: bool,
452        span: Span,
453    },
454    /// `.tf OUTVAR INSRC`
455    Tf {
456        outvar: OutVar,
457        insrc: Ident,
458        span: Span,
459    },
460}
461
462/// One `.dc` sweep source: name, start, stop, increment.
463#[derive(Clone, Debug)]
464pub struct DcSweep {
465    pub src: Ident,
466    pub start: f64,
467    pub stop: f64,
468    pub step: f64,
469}
470
471#[derive(Clone, Copy, Debug, PartialEq, Eq)]
472pub enum Variation {
473    Dec,
474    Oct,
475    Lin,
476}
477
478#[derive(Clone, Copy, Debug, PartialEq, Eq)]
479pub enum PzInput {
480    Current,
481    Voltage,
482}
483
484#[derive(Clone, Copy, Debug, PartialEq, Eq)]
485pub enum PzWhich {
486    Poles,
487    Zeros,
488    Both,
489}
490
491/// `.print` / `.plot` / `.save` / `.four`.
492#[derive(Clone, Debug)]
493pub struct Output {
494    pub kind: OutputKind,
495    /// `DC`, `AC`, `TRAN`, `NOISE` or `DISTO` for print/plot.
496    pub analysis: Option<Ident>,
497    pub vars: Vec<OutVar>,
498    pub span: Span,
499}
500
501#[derive(Clone, Debug, PartialEq)]
502pub enum OutputKind {
503    Print,
504    Plot,
505    Save,
506    /// `.four FREQ OV1 …`
507    Four {
508        freq: f64,
509    },
510}
511
512/// An output variable reference.
513#[derive(Clone, Debug, PartialEq)]
514pub enum OutVar {
515    /// `V(N1<,N2>)` with an optional AC suffix (`VM`, `VP`, `VR`,
516    /// `VI`, `VDB`).
517    Voltage {
518        node: Ident,
519        reference: Option<Ident>,
520        suffix: Option<AcSuffix>,
521    },
522    /// `I(VXXXXXXX)` with the same optional suffixes.
523    Current {
524        vsrc: Ident,
525        suffix: Option<AcSuffix>,
526    },
527    /// Anything else (a raw vector name for `.save`, e.g. `all`).
528    Raw(Ident),
529}
530
531#[derive(Clone, Copy, Debug, PartialEq, Eq)]
532pub enum AcSuffix {
533    Real,
534    Imag,
535    Mag,
536    Phase,
537    Db,
538}
539
540/// One `.ic`/`.nodeset` assignment.
541#[derive(Clone, Debug)]
542pub struct NodeInit {
543    pub kind: NodeInitKind,
544    pub node: Ident,
545    pub value: f64,
546    pub span: Span,
547}
548
549#[derive(Clone, Copy, Debug, PartialEq, Eq)]
550pub enum NodeInitKind {
551    /// `.ic`: enforced initial condition.
552    Ic,
553    /// `.nodeset`: initial guess only.
554    Nodeset,
555}
556
557/// One `.measure` card (ngspice-47 `com_measure2.c` grammar — the
558/// 3f5 has none, so ngspice is the contract here; see
559/// `docs/dune-measure.md`).
560#[derive(Clone, Debug, PartialEq)]
561pub struct Measure {
562    /// Which plot the card measures.
563    pub analysis: MeasAnalysis,
564    /// Result name (lowercased, like every deck identifier).
565    pub name: Ident,
566    /// The measurement itself.
567    pub func: MeasFunc,
568    /// The function keyword as written, lowercased (`trig`, `when`,
569    /// `avg`…), for ngspice-style messages.
570    pub keyword: String,
571    /// The card text as written (continuations joined).
572    pub text: String,
573    pub span: Span,
574}
575
576/// The analysis a `.measure` card applies to (`chkAnalysisType`).
577#[derive(Clone, Copy, Debug, PartialEq, Eq)]
578pub enum MeasAnalysis {
579    Tran,
580    Ac,
581    Dc,
582    /// ngspice's `.sp` (measured like `.ac`).
583    Sp,
584}
585
586impl MeasAnalysis {
587    /// The card keyword (`tran`, `ac`, `dc`, `sp`).
588    pub fn keyword(self) -> &'static str {
589        match self {
590            MeasAnalysis::Tran => "tran",
591            MeasAnalysis::Ac => "ac",
592            MeasAnalysis::Dc => "dc",
593            MeasAnalysis::Sp => "sp",
594        }
595    }
596}
597
598/// What a `.measure` computes.
599#[derive(Clone, Debug, PartialEq)]
600pub enum MeasFunc {
601    /// `TRIG … TARG …` (also spelled `DELAY`): `targ − trig`.
602    Delay { trig: MeasTrig, targ: MeasTrig },
603    /// `WHEN out=val|out2`: the scale value of a crossing.
604    When(MeasCross),
605    /// `FIND out AT=x` / `FIND out WHEN …`: the interpolated value.
606    Find { var: OutVar, at: MeasPoint },
607    /// `DERIV out AT=x` / `DERIV out WHEN …`: d(out)/d(scale) —
608    /// a Dune extension; ngspice-47 parses it but reports
609    /// "currently not supported".
610    Deriv { var: OutVar, at: MeasPoint },
611    /// `AVG|MIN|MAX|MIN_AT|MAX_AT|PP|RMS|INTEG out FROM= TO=`.
612    Stat {
613        stat: MeasStat,
614        var: OutVar,
615        window: MeasWindow,
616    },
617}
618
619/// One side of a `TRIG … TARG …` delay.
620#[derive(Clone, Debug, PartialEq)]
621pub enum MeasTrig {
622    /// `TRIG AT=x`: a fixed scale value.
623    At(f64),
624    /// `TRIG out VAL=v RISE|FALL|CROSS=n …`.
625    Cross(MeasCross),
626}
627
628/// Where `FIND`/`DERIV` evaluate.
629#[derive(Clone, Debug, PartialEq)]
630pub enum MeasPoint {
631    At(f64),
632    When(MeasCross),
633}
634
635/// A level crossing: `out` against a constant or a second vector.
636#[derive(Clone, Debug, PartialEq)]
637pub struct MeasCross {
638    pub var: OutVar,
639    pub level: MeasLevel,
640    pub edge: MeasEdge,
641    pub window: MeasWindow,
642}
643
644#[derive(Clone, Debug, PartialEq)]
645pub enum MeasLevel {
646    Value(f64),
647    /// `WHEN v(a)=v(b)`.
648    Var(OutVar),
649}
650
651/// Which crossing counts (`RISE=`, `FALL=`, `CROSS=`; the last one
652/// given wins, as in `measure_parse_stdParams`).
653#[derive(Clone, Copy, Debug, PartialEq, Eq)]
654pub enum MeasEdge {
655    /// None given: the first crossing either way.
656    Any,
657    Rise(MeasCount),
658    Fall(MeasCount),
659    Cross(MeasCount),
660}
661
662#[derive(Clone, Copy, Debug, PartialEq, Eq)]
663pub enum MeasCount {
664    /// The n-th (1-based).
665    Nth(u32),
666    /// `=LAST` (or a bare `LAST`, which means `CROSS=LAST`).
667    Last,
668}
669
670/// `TD=`, `FROM=`, `TO=` as given (`None` = not on the card; the
671/// per-analysis defaults are applied by the evaluator, like ngspice).
672#[derive(Clone, Copy, Debug, Default, PartialEq)]
673pub struct MeasWindow {
674    pub td: Option<f64>,
675    pub from: Option<f64>,
676    pub to: Option<f64>,
677}
678
679#[derive(Clone, Copy, Debug, PartialEq, Eq)]
680pub enum MeasStat {
681    Min,
682    Max,
683    /// The scale value of the minimum.
684    MinAt,
685    /// The scale value of the maximum.
686    MaxAt,
687    /// Peak to peak.
688    Pp,
689    Avg,
690    Rms,
691    Integ,
692}