reda_sp/parse/
measures.rs

1use nom::{
2    branch::alt, bytes::complete::{tag, tag_no_case, take_until}, combinator::map, error::context, sequence::{delimited, preceded}
3};
4
5use crate::model::{AnalysisType, FindWhenCondition, EdgeType, MeasureBasicStat, MeasureCommand, MeasureFindWhen, MeasureFunction, MeasureRise, OutputSuffix, OutputVariable, TrigTargCondition};
6use super::{hws, identifier, number, time_number, unsigned_int, NomResult, ToFailure};
7
8/// .MEAS TRAN rise ... 
9pub fn measure_command(input: &str) -> NomResult<MeasureCommand> {
10    context("measure_command", |input| {
11        let (input, _) = context("keyword", hws(tag_no_case(".MEAS")))(input)?;
12        let (input, analysis) = context("analysis_type", hws(analysis_type))(input).to_failure()?;
13        let (input, name) = context("measure_name", hws(identifier))(input).to_failure()?;
14
15        let x = alt((
16            context("measure_rise", map(|i| measure_rise(i, name, analysis), MeasureCommand::Rise)),
17            context("measure_basic_stat", map(|i| measure_basic_stat(i, name, analysis), MeasureCommand::BasicStat)),
18            context("measure_find_when", map(|i| measure_find_when(i, name, analysis), MeasureCommand::FindWhen)),
19        ))(input).to_failure();
20        x
21    })(input)
22}
23
24/// .MEAS TRAN rise TRIG V(1) VAL=.2 RISE=1
25///                 TARG V(1) VAL=.8 RISE=1
26fn measure_rise<'a>(input: &'a str, name: &'a str, analysis: AnalysisType) -> NomResult<'a, MeasureRise> {
27    let (input, _) = context("TRIG keyword", hws(tag_no_case("TRIG")))(input)?;
28    let (input, trig) = context("trigger_condition", hws(trig_targ_condition))(input).to_failure()?;
29    let (input, _) = context("TARG keyword", hws(tag_no_case("TARG")))(input).to_failure()?;
30    let (input, targ) = context("target_condition", hws(trig_targ_condition))(input).to_failure()?;
31
32    Ok((input, MeasureRise { name: name.to_string(), analysis, trig, targ }))
33}
34
35/// .MEAS TRAN avgval AVG V(1) FROM=10ns TO=55ns
36fn measure_basic_stat<'a>(input: &'a str, name: &'a str, analysis: AnalysisType) -> NomResult<'a, MeasureBasicStat> {
37    let (input, stat) = context("stat_function", hws(measure_function))(input)?;
38    let (input, variable) = context("variable", hws(output_variable))(input).to_failure()?;
39    let (input, from) = context("FROM value", preceded(hws(tag_no_case("FROM=")), hws(time_number)))(input).to_failure()?;
40    let (input, to) = context("TO value", preceded(hws(tag_no_case("TO=")), hws(time_number)))(input).to_failure()?;
41
42    Ok((input, MeasureBasicStat { name: name.to_string(), analysis, stat, variable, from, to }))
43}
44
45/// .MEAS TRAN DesiredCurr FIND I(Vmeas) WHEN V(1)=1V
46fn measure_find_when<'a>(input: &'a str, name: &'a str, analysis: AnalysisType) -> NomResult<'a, MeasureFindWhen> {
47    let (input, _) = context("FIND keyword", hws(tag_no_case("FIND")))(input)?;
48    let (input, variable) = context("variable", hws(output_variable))(input).to_failure()?;
49    let (input, _) = context("WHEN keyword", hws(tag_no_case("WHEN")))(input).to_failure()?;
50    let (input, condition) = context("condition", hws(finwhen_condition))(input).to_failure()?;
51
52    Ok((input, MeasureFindWhen { name: name.to_string(), analysis, variable, when: condition }))
53}
54
55
56/// V(1) VAL=.2 RISE=1
57fn trig_targ_condition(input: &str) -> NomResult<TrigTargCondition> {
58    let (input, variable) = hws(output_variable)(input)?;
59    let (input, _) = hws(tag_no_case("VAL="))(input)?;
60    let (input, value) = hws(number)(input)?;
61    let (input, edge) = hws(alt((
62        map(tag_no_case("RISE"), |_| EdgeType::Rise),
63        map(tag_no_case("FALL"), |_| EdgeType::Fall),
64    )))(input)?;
65    let (input, _) = hws(tag("="))(input)?;
66    let (input, num) = hws(unsigned_int)(input)?;
67
68    Ok((
69        input,
70        TrigTargCondition {
71            variable,
72            value,
73            edge,
74            number: num as usize,
75        },
76    ))
77}
78
79fn measure_function(input: &str) -> NomResult<MeasureFunction> {
80    map(
81        hws(alt((
82            tag_no_case("AVG"),
83            tag_no_case("RMS"),
84            tag_no_case("MIN"),
85            tag_no_case("MAX"),
86            tag_no_case("PP"),
87            tag_no_case("DERIV"),
88            tag_no_case("INTEGRATE"),
89        ))),
90        |s: &str| match &s.to_ascii_uppercase()[..] {
91            "AVG" => MeasureFunction::Avg,
92            "RMS" => MeasureFunction::Rms,
93            "MIN" => MeasureFunction::Min,
94            "MAX" => MeasureFunction::Max,
95            "PP" => MeasureFunction::Pp,
96            "DERIV" => MeasureFunction::Deriv,
97            "INTEGRATE" => MeasureFunction::Integrate,
98            _ => unreachable!(),
99        },
100    )(input)
101}
102
103fn finwhen_condition(input: &str) -> NomResult<FindWhenCondition> {
104    let (input, variable) = hws(output_variable)(input)?;
105    let (input, _) = hws(tag("="))(input)?;
106    let (input, value) = hws(number)(input)?;
107
108    Ok((input, FindWhenCondition { variable, value }))
109}
110
111fn output_variable(input: &str) -> NomResult<OutputVariable> {
112    let (input, kind) = hws(alt((tag_no_case("V"), tag_no_case("I"))))(input)?;
113
114    let (input, var) = hws(delimited(
115        hws(tag("(")),
116        take_until(")"),
117        tag(")"),
118    ))(input)?;
119
120    let suffix = if var.ends_with("M") {
121        Some(OutputSuffix::Magnitude)
122    } else if var.ends_with("DB") {
123        Some(OutputSuffix::Decibel)
124    } else if var.ends_with("P") {
125        Some(OutputSuffix::Phase)
126    } else if var.ends_with("R") {
127        Some(OutputSuffix::Real)
128    } else if var.ends_with("I") {
129        Some(OutputSuffix::Imag)
130    } else {
131        None
132    };
133
134    if kind.eq_ignore_ascii_case("V") {
135        let parts = var.split(',').map(|s| s.trim()).collect::<Vec<_>>();
136        let node1 = parts.get(0).unwrap_or(&"").to_string();
137        let node2 = parts.get(1).map(|s| s.to_string());
138        Ok((input, OutputVariable::Voltage { node1, node2, suffix }))
139    } else {
140        Ok((input, OutputVariable::Current {
141            element_name: var.to_string(),
142            suffix,
143        }))
144    }
145}
146
147fn analysis_type(input: &str) -> NomResult<AnalysisType> {
148    map(
149        hws(alt((tag_no_case("TRAN"), tag_no_case("AC"), tag_no_case("DC")))),
150        |s: &str| match &s.to_ascii_uppercase()[..] {
151            "TRAN" => AnalysisType::Tran,
152            "AC" => AnalysisType::Ac,
153            "DC" => AnalysisType::Dc,
154            _ => unreachable!(),
155        },
156    )(input)
157}
158
159#[cfg(test)]
160mod tests {
161    use nom::{error::convert_error, Err};
162
163    use super::*;
164    use reda_unit::{num, u};
165
166    #[test]
167    fn test_measure_rise() {
168        let input = ".MEAS TRAN rise1 TRIG V(n1) VAL=0.2 RISE=1 TARG V(n1) VAL=0.8 RISE=1";
169        let (_, meas) = measure_command(input).unwrap();
170
171        if let MeasureCommand::Rise(m) = meas {
172            assert_eq!(m.name, "rise1");
173            assert_eq!(m.analysis, AnalysisType::Tran);
174            assert_eq!(m.trig.value, num!(0.2));
175            assert_eq!(m.trig.edge, EdgeType::Rise);
176            assert_eq!(m.trig.number, 1);
177            assert_eq!(m.targ.value, num!(0.8));
178            assert_eq!(m.targ.edge, EdgeType::Rise);
179            assert_eq!(m.targ.number, 1);
180        } else {
181            panic!("Expected MeasureCommand::Rise");
182        }
183    }
184
185    #[test]
186    fn test_measure_basic_stat() {
187        let input = ".MEAS TRAN avgval AVG V(n1) FROM=10u TO=55u";
188        let (_, meas) = measure_command(input).unwrap();
189
190        if let MeasureCommand::BasicStat(m) = meas {
191            assert_eq!(m.name, "avgval");
192            assert_eq!(m.analysis, AnalysisType::Tran);
193            assert_eq!(m.stat, MeasureFunction::Avg);
194            assert!(matches!(m.variable, OutputVariable::Voltage { .. }));
195            assert_eq!(m.from, u!(10. us));
196            assert_eq!(m.to, u!(55. us));
197        } else {
198            panic!("Expected MeasureCommand::BasicStat");
199        }
200    }
201
202    #[test]
203    fn test_measure_find_when() {
204        let input = ".MEAS TRAN DesiredCurr FIND I(Vmeas) WHEN V(n1)=1V";
205        let (_, meas) = measure_command(input).unwrap();
206
207        if let MeasureCommand::FindWhen(m) = meas {
208            assert_eq!(m.name, "DesiredCurr");
209            assert_eq!(m.analysis, AnalysisType::Tran);
210            assert!(matches!(m.variable, OutputVariable::Current { .. }));
211            assert_eq!(m.when.value, num!(1));
212        } else {
213            panic!("Expected MeasureCommand::FindWhen");
214        }
215    }
216
217    #[test]
218    fn test_measure_bad_prefix() {
219        let input = ".XXX TRAN AVG V(1) FROM=0 TO=1";
220        let result = measure_command(input);
221        assert!(matches!(result, Err(Err::Error(_))));
222    }
223
224    #[test]
225    fn test_measure_unknown_type() {
226        let input = ".MEAS TRAN BOGUS V(1) FROM=0 TO=1";
227        let result = measure_command(input);
228        assert!(matches!(result, Err(Err::Failure(_))));
229    }
230
231    #[test]
232    fn test_measure_stat_missing_to() {
233        let input = ".MEAS TRAN result AVG V(1) FROM=1";
234        let result = measure_command(input);
235        assert!(matches!(result, Err(Err::Failure(_))));
236    }
237
238    #[test]
239    fn test_measure_rise_missing_targ() {
240        let input = ".MEAS TRAN rise TRIG V(1) VAL=.2 RISE=1";
241        let result = measure_command(input);
242        assert!(matches!(result, Err(Err::Failure(_))));
243    }
244
245    #[test]
246    fn test_measure_find_when_invalid_condition() {
247        let input = ".MEAS TRAN result FIND I(R1) WHEN V(1) == 1";
248        let result = measure_command(input);
249        assert!(matches!(result, Err(Err::Failure(_))));
250    }
251
252    #[test]
253    fn test_measure_debug_context() {
254        let input = ".MEAS TRAN avgval AVG V(1) FROM=10ns TO=abc";
255        let result = measure_command(input);
256        if let Err(Err::Failure(e)) = result {
257            println!("{}", convert_error(input, e));
258        }
259    }
260}