reda_sp/model/control/
sim.rs

1use derive_builder::Builder;
2use reda_unit::{Frequency, Time, Voltage};
3
4use crate::ToSpice;
5
6#[derive(Debug, Clone)]
7pub enum SimCommand {
8    Op,
9    Dc(DcCommand),
10    Ac(AcCommand),
11    Tran(TranCommand)
12}
13
14#[derive(Debug, Clone, Builder)]
15#[builder(setter(strip_option, into))]
16pub struct DcCommand {
17    pub src_name: String,
18    pub start: Voltage,
19    pub stop: Voltage,
20    pub step: Voltage,
21}
22
23#[derive(Debug, Clone, PartialEq, PartialOrd)]
24pub enum AcSweepType {
25    Lin, // 线性扫描
26    Dec, // 每十倍频率 ND 点
27    Oct, // 每倍频率 NO 点
28}
29
30#[derive(Debug, Clone)]
31pub struct AcCommand {
32    pub sweep_type: AcSweepType,
33    pub points: usize,
34    pub f_start: Frequency,
35    pub f_stop: Frequency,
36}
37
38impl AcCommand {
39    pub fn linear<F1: Into<Frequency>, F2: Into<Frequency>>(points: usize, f_start: F1, f_stop: F2) -> Self {
40        Self::new(AcSweepType::Lin, points, f_start, f_stop)
41        
42    }
43
44    pub fn dec<F1: Into<Frequency>, F2: Into<Frequency>>(points: usize, f_start: F1, f_stop: F2) -> Self {
45        Self::new(AcSweepType::Dec, points, f_start, f_stop)
46
47    }
48
49    pub fn oct<F1: Into<Frequency>, F2: Into<Frequency>>(points: usize, f_start: F1, f_stop: F2) -> Self {
50        Self::new(AcSweepType::Oct, points, f_start, f_stop)
51
52    }
53
54    pub fn new<F1: Into<Frequency>, F2: Into<Frequency>>(ty: AcSweepType, points: usize, f_start: F1, f_stop: F2) -> Self {
55        Self {
56            sweep_type: ty,
57            points,
58            f_start: f_start.into(),
59            f_stop: f_stop.into(),
60        }
61    }
62}
63
64#[derive(Debug, Clone, Builder)]
65#[builder(setter(into, strip_option))]
66pub struct TranCommand {
67    pub t_step: Time,
68    pub t_stop: Time,
69    #[builder(default)]
70    pub t_start: Option<Time>,
71    #[builder(default)]
72    pub t_max: Option<Time>,
73    #[builder(default = "false")]
74    pub uic: bool, // Use Initial Condition
75}
76
77impl ToSpice for SimCommand {
78    fn to_spice(&self) -> String {
79        match self {
80            SimCommand::Op => ".op".to_string(),
81            SimCommand::Dc(dc) => dc.to_spice(),
82            SimCommand::Ac(ac) => ac.to_spice(),
83            SimCommand::Tran(tran) => tran.to_spice(),
84        }
85    }
86}
87
88impl ToSpice for DcCommand {
89    fn to_spice(&self) -> String {
90        format!(
91            ".DC {} {} {} {}",
92            self.src_name, self.start, self.stop, self.step
93        )
94    }
95}
96
97impl ToSpice for AcCommand {
98    fn to_spice(&self) -> String {
99        format!(
100            ".AC {} {} {} {}",
101            self.sweep_type.to_spice_str(),
102            self.points,
103            self.f_start.to_spice(),
104            self.f_stop.to_spice(),
105        )
106    }
107}
108
109impl ToSpice for TranCommand {
110    fn to_spice(&self) -> String {
111        let mut line = format!(".TRAN {} {}", self.t_step, self.t_stop);
112
113        if let Some(t_start) = self.t_start {
114            line.push_str(&format!(" {}", t_start));
115        }
116
117        if let Some(t_max) = self.t_max {
118            if self.t_start.is_none() {
119                line.push_str(" 0");
120            }
121            line.push_str(&format!(" {}", t_max));
122        }
123
124        if self.uic {
125            line.push_str(" UIC");
126        }
127
128        line
129    }
130}
131
132impl AcSweepType {
133    pub fn to_spice_str(&self) -> &'static str {
134        match self {
135            AcSweepType::Lin => "LIN",
136            AcSweepType::Dec => "DEC",
137            AcSweepType::Oct => "OCT",
138        }
139    }
140}