Skip to main content

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