1use crate::matrix::AcceptCtx;
7
8#[derive(Clone, Debug)]
15pub enum Wave {
16 Pulse(Vec<f64>),
17 Sin(Vec<f64>),
18 Exp(Vec<f64>),
19 Pwl(Vec<f64>),
20 Sffm(Vec<f64>),
21 TrNoise(Vec<f64>),
27 TrRandom(Vec<f64>),
34}
35
36fn arg(c: &[f64], i: usize) -> Option<f64> {
37 c.get(i).copied()
38}
39
40fn arg_nz(c: &[f64], i: usize, default: f64) -> f64 {
43 match c.get(i) {
44 Some(&v) if v != 0.0 => v,
45 _ => default,
46 }
47}
48
49impl Wave {
50 pub(crate) fn value(&self, time: f64, step: f64, final_time: f64) -> f64 {
55 match self {
56 Wave::TrNoise(_) => 0.0,
57 Wave::TrRandom(c) => arg(c, 4).unwrap_or(0.0),
58 Wave::Pulse(c) => {
59 let v1 = arg(c, 0).unwrap_or(0.0);
60 let v2 = arg(c, 1).unwrap_or(0.0);
61 let td = arg(c, 2).unwrap_or(0.0);
62 let tr = arg_nz(c, 3, step);
63 let tf = arg_nz(c, 4, step);
64 let pw = arg_nz(c, 5, final_time);
65 let per = arg_nz(c, 6, final_time);
66 let mut time = time - td;
67 if time > per {
68 let basetime = per * (time / per).floor();
71 time -= basetime;
72 }
73 if time <= 0.0 || time >= tr + pw + tf {
74 v1
75 } else if time >= tr && time <= tr + pw {
76 v2
77 } else if time > 0.0 && time < tr {
78 v1 + (v2 - v1) * time / tr
79 } else {
80 v2 + (v1 - v2) * (time - (tr + pw)) / tf
81 }
82 }
83 Wave::Sin(c) => {
84 let vo = arg(c, 0).unwrap_or(0.0);
85 let va = arg(c, 1).unwrap_or(0.0);
86 let freq = arg_nz(c, 2, 1.0 / final_time);
87 let td = arg(c, 3).unwrap_or(0.0);
88 let theta = arg(c, 4).unwrap_or(0.0);
89 let time = time - td;
90 if time <= 0.0 {
91 vo
92 } else {
93 vo + va
94 * (freq * time * 2.0 * std::f64::consts::PI).sin()
95 * (-(time * theta)).exp()
96 }
97 }
98 Wave::Exp(c) => {
99 let v1 = arg(c, 0).unwrap_or(0.0);
100 let v2 = arg(c, 1).unwrap_or(0.0);
101 let td1 = arg(c, 2).unwrap_or(step);
102 let tau1 = arg_nz(c, 3, step);
103 let td2 = arg_nz(c, 4, td1 + step);
104 let tau2 = arg_nz(c, 5, step);
105 if time <= td1 {
106 v1
107 } else if time <= td2 {
108 v1 + (v2 - v1) * (1.0 - (-(time - td1) / tau1).exp())
109 } else {
110 v1 + (v2 - v1) * (1.0 - (-(time - td1) / tau1).exp())
111 + (v1 - v2) * (1.0 - (-(time - td2) / tau2).exp())
112 }
113 }
114 Wave::Sffm(c) => {
115 let vo = arg(c, 0).unwrap_or(0.0);
116 let va = arg(c, 1).unwrap_or(0.0);
117 let fc = arg_nz(c, 2, 1.0 / final_time);
118 let mdi = arg(c, 3).unwrap_or(0.0);
119 let fs = arg_nz(c, 4, 1.0 / final_time);
120 #[allow(clippy::approx_constant)]
122 const PI: f64 = 3.141_592_654;
123 vo + va * ((2.0 * PI * fc * time) + mdi * (2.0 * PI * fs * time).sin()).sin()
124 }
125 Wave::Pwl(c) => {
126 let pairs = c.len() / 2;
127 if pairs == 0 {
128 return 0.0;
129 }
130 if time < c[0] {
131 return c[1];
132 }
133 for i in 0..pairs.saturating_sub(1) {
134 let (t0, v0) = (c[2 * i], c[2 * i + 1]);
135 let (t1, v1) = (c[2 * i + 2], c[2 * i + 3]);
136 if t0 == time {
137 return v0;
138 }
139 if t0 < time && t1 > time {
140 return v0 + (time - t0) / (t1 - t0) * (v1 - v0);
141 }
142 }
143 c[2 * pairs - 1]
144 }
145 }
146 }
147
148 pub(crate) fn corners(&self, t0: f64, t1: f64, step: f64, final_time: f64, out: &mut Vec<f64>) {
156 let inside = |t: f64| t > t0 && t < t1;
157 match self {
158 Wave::Pulse(c) => {
159 let td = arg(c, 2).unwrap_or(0.0);
160 let tr = arg_nz(c, 3, step);
161 let tf = arg_nz(c, 4, step);
162 let pw = arg_nz(c, 5, final_time);
163 let per = arg_nz(c, 6, final_time);
164 let offsets = [0.0, tr, tr + pw, tr + pw + tf];
165 if !(per > 0.0 && per.is_finite()) {
166 return;
167 }
168 let k0 = ((t0 - td) / per).floor() - 1.0;
169 let k1 = ((t1 - td) / per).ceil() + 1.0;
170 if !(k0.is_finite() && k1.is_finite()) || k1 - k0 > 10_000.0 {
171 return;
172 }
173 let mut k = k0.max(0.0);
174 while k <= k1 {
175 for o in offsets {
176 let t = td + k * per + o;
177 if inside(t) {
178 out.push(t);
179 }
180 }
181 k += 1.0;
182 }
183 }
184 Wave::Pwl(c) => {
185 for &t in c.iter().step_by(2) {
187 if inside(t) {
188 out.push(t);
189 }
190 }
191 }
192 _ => {}
193 }
194 }
195
196 pub(crate) fn accept(&self, ctx: &mut AcceptCtx) {
200 match self {
201 Wave::Pulse(c) => {
202 let td = arg(c, 2).unwrap_or(0.0);
203 let tr = arg_nz(c, 3, ctx.step);
204 let tf = arg_nz(c, 4, ctx.step);
205 let pw = arg_nz(c, 5, ctx.final_time);
206 let per = arg_nz(c, 6, ctx.final_time);
207 let timetol = 1e-7 * pw;
208 let same = |a: f64, b: f64| (a - b).abs() <= timetol;
209
210 let mut time = ctx.time - td;
211 let mut basetime = 0.0;
212 if time >= per {
213 basetime = per * (time / per).floor();
214 time -= basetime;
215 }
216 if !ctx.brk {
217 return;
218 }
219 if time <= 0.0 || time >= tr + pw + tf {
220 if same(time, 0.0) {
221 ctx.set_break(basetime + tr + td);
222 } else if same(tr + pw + tf, time) {
223 ctx.set_break(basetime + per + td);
224 } else if time == -td {
225 ctx.set_break(basetime + td);
226 } else if same(per, time) {
227 ctx.set_break(basetime + td + tr + per);
228 }
229 } else if time >= tr && time <= tr + pw {
230 if same(time, tr) {
231 ctx.set_break(basetime + td + tr + pw);
232 } else if same(tr + pw, time) {
233 ctx.set_break(basetime + td + tr + pw + tf);
234 }
235 } else if time > 0.0 && time < tr {
236 if same(time, 0.0) {
237 ctx.set_break(basetime + td + tr);
238 } else if same(time, tr) {
239 ctx.set_break(basetime + td + tr + pw);
240 }
241 } else {
242 if same(time, tr + pw) {
244 ctx.set_break(basetime + td + tr + pw + tf);
245 } else if same(time, tr + pw + tf) {
246 ctx.set_break(basetime + td + per);
247 }
248 }
249 }
250 Wave::Pwl(c) => {
251 let pairs = c.len() / 2;
252 if pairs == 0 || !ctx.brk {
253 return;
254 }
255 if ctx.time < c[0] {
256 ctx.set_break(c[0]);
257 return;
258 }
259 for i in 0..pairs.saturating_sub(1) {
260 if c[2 * i] == ctx.time {
261 ctx.set_break(c[2 * i + 2]);
262 return;
263 }
264 }
265 }
266 _ => {}
267 }
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn pulse_shape_and_defaults() {
277 let w = Wave::Pulse(vec![0.0, 1.0]);
280 assert_eq!(w.value(0.0, 0.1, 7.0), 0.0);
281 assert!((w.value(0.05, 0.1, 7.0) - 0.5).abs() < 1e-12);
282 assert_eq!(w.value(0.1, 0.1, 7.0), 1.0);
283 assert_eq!(w.value(5.0, 0.1, 7.0), 1.0);
284 }
285
286 #[test]
287 fn pulse_full_period() {
288 let w = Wave::Pulse(vec![-1.0, 1.0, 1.0, 0.5, 0.5, 2.0, 5.0]);
289 assert_eq!(w.value(0.0, 0.1, 100.0), -1.0); assert_eq!(w.value(1.25, 0.1, 100.0), 0.0); assert_eq!(w.value(2.0, 0.1, 100.0), 1.0); assert_eq!(w.value(3.75, 0.1, 100.0), 0.0); assert_eq!(w.value(5.0, 0.1, 100.0), -1.0); assert_eq!(w.value(7.0, 0.1, 100.0), 1.0);
296 }
297
298 #[test]
299 fn pwl_segments() {
300 let w = Wave::Pwl(vec![0.0, -7.0, 10e-9, -7.0, 11e-9, -3.0]);
301 assert_eq!(w.value(-1.0, 0.0, 1.0), -7.0);
302 assert_eq!(w.value(5e-9, 0.0, 1.0), -7.0);
303 assert!((w.value(10.5e-9, 0.0, 1.0) - (-5.0)).abs() < 1e-9);
304 assert_eq!(w.value(2e-8, 0.0, 1.0), -3.0); }
306
307 #[test]
308 fn sin_and_exp() {
309 let w = Wave::Sin(vec![1.0, 2.0, 1.0]); assert_eq!(w.value(0.0, 0.0, 1.0), 1.0);
311 assert!((w.value(0.25, 0.0, 1.0) - 3.0).abs() < 1e-9);
312 let w = Wave::Exp(vec![0.0, 1.0, 0.1, 0.2, 0.5, 0.3]);
313 assert_eq!(w.value(0.05, 0.0, 1.0), 0.0);
314 let v = w.value(0.3, 0.0, 1.0);
315 assert!((v - (1.0 - (-0.2f64 / 0.2).exp())).abs() < 1e-12);
316 }
317}