1 #ifndef WDF_H_INCLUDED
2 #define WDF_H_INCLUDED
3
4 #include "omega.h"
5 #include <string>
6 #include <cmath>
7 #include <memory>
8 #include <type_traits>
9
10 namespace chowdsp
11 {
12
13 /**
14 * A framework for creating circuit emulations with Wave Digital Filters.
15 * For more technical information, see:
16 * -
17 * https://www.eit.lth.se/fileadmin/eit/courses/eit085f/Fettweis_Wave_Digital_Filters_Theory_and_Practice_IEEE_Proc_1986_-_This_is_a_real_challange.pdf
18 * - https://searchworks.stanford.edu/view/11891203
19 *
20 * To start, initialize all your circuit elements and connections.
21 * Be sure to pick a "root" node, and call `root.connectToNode (adaptor);`
22 *
23 * To run the simulation, call the following code
24 * once per sample:
25 * ```
26 * // set source inputs here...
27 *
28 * root.incident (adaptor.reflected());
29 * // if probing the root node, do that here...
30 * adaptor.incident (root.reflected());
31 *
32 * // probe other elements here...
33 * ```
34 *
35 * To probe a node, call `element.voltage()` or `element.current()`.
36 */
37 namespace WDF
38 {
39
40 namespace detail
41 {
42 template <typename T>
43 typename std::enable_if<std::is_default_constructible<T>::value, void>::type
default_construct(std::unique_ptr<T> & ptr)44 default_construct(std::unique_ptr<T> &ptr)
45 {
46 ptr = std::make_unique<T>();
47 }
48
49 template <typename T>
50 typename std::enable_if<!std::is_default_constructible<T>::value, void>::type
default_construct(std::unique_ptr<T> & ptr)51 default_construct(std::unique_ptr<T> &ptr)
52 {
53 ptr.reset(nullptr);
54 }
55 } // namespace detail
56
57 /** Wave digital filter base class */
58 class WDF
59 {
60 public:
WDF(std::string type)61 WDF(std::string type) : type(type) {}
~WDF()62 virtual ~WDF() {}
63
64 /** Sub-classes override this function to recompute
65 * the impedance of this element.
66 */
67 virtual void calcImpedance() = 0;
68
69 /** Sub-classes override this function to propogate
70 * an impedance change to the upstream elements in
71 * the WDF tree.
72 */
73 virtual void propagateImpedance() = 0;
74
75 /** Sub-classes override this function to accept an incident wave. */
76 virtual void incident(double x) noexcept = 0;
77
78 /** Sub-classes override this function to propogate a reflected wave. */
79 virtual double reflected() noexcept = 0;
80
81 /** Probe the voltage across this circuit element. */
voltage()82 inline double voltage() const noexcept { return (a + b) / 2.0; }
83
84 /**Probe the current through this circuit element. */
current()85 inline double current() const noexcept { return (a - b) / (2.0 * R); }
86
87 // These classes need access to a,b
88 friend class YParameter;
89 friend class WDFParallel;
90 friend class WDFSeries;
91
92 template <typename Port1Type, typename Port2Type> friend class WDFParallelT;
93
94 template <typename Port1Type, typename Port2Type> friend class WDFSeriesT;
95
96 double R = 1.0e-9; // impedance
97 double G = 1.0 / R; // admittance
98
99 protected:
100 double a = 0.0; // incident wave
101 double b = 0.0; // reflected wave
102
103 private:
104 const std::string type;
105 };
106
107 /** WDF node base class */
108 class WDFNode : public WDF
109 {
110 public:
WDFNode(std::string type)111 WDFNode(std::string type) : WDF(type) {}
~WDFNode()112 virtual ~WDFNode() {}
113
114 /** Connects this WDF node to an upstream node in the WDF tree. */
connectToNode(WDF * node)115 void connectToNode(WDF *node) { next = node; }
116
117 /** When this function is called from a downstream
118 * element in the WDF tree, the impedance is recomputed
119 * and then propogated upstream to the next element in the
120 * WDF tree.
121 */
propagateImpedance()122 inline void propagateImpedance() override
123 {
124 calcImpedance();
125
126 if (next != nullptr)
127 next->propagateImpedance();
128 }
129
130 protected:
131 WDF *next = nullptr;
132 };
133
134 /** WDF Resistor Node */
135 class Resistor : public WDFNode
136 {
137 public:
138 /** Creates a new WDF Resistor with a given resistance.
139 * @param value: resistance in Ohms
140 */
Resistor(double value)141 Resistor(double value) : WDFNode("Resistor"), R_value(value) { calcImpedance(); }
~Resistor()142 virtual ~Resistor() {}
143
144 /** Sets the resistance value of the WDF resistor, in Ohms. */
setResistanceValue(double newR)145 void setResistanceValue(double newR)
146 {
147 if (newR == R_value)
148 return;
149
150 R_value = newR;
151 propagateImpedance();
152 }
153
154 /** Computes the impedance of the WDF resistor, Z_R = R. */
calcImpedance()155 inline void calcImpedance() override
156 {
157 R = R_value;
158 G = 1.0 / R;
159 }
160
161 /** Accepts an incident wave into a WDF resistor. */
incident(double x)162 inline void incident(double x) noexcept override { a = x; }
163
164 /** Propogates a reflected wave from a WDF resistor. */
reflected()165 inline double reflected() noexcept override
166 {
167 b = 0.0;
168 return b;
169 }
170
171 private:
172 double R_value = 1.0e-9;
173 };
174
175 /** WDF Capacitor Node */
176 class Capacitor : public WDFNode
177 {
178 public:
179 /** Creates a new WDF Capacitor.
180 * @param value: Capacitance value in Farads
181 * @param fs: WDF sample rate
182 * @param alpha: alpha value to be used for the alpha transform,
183 * use 0 for Backwards Euler, use 1 for Bilinear Transform.
184 */
185 Capacitor(double value, double fs, double alpha = 1.0)
186 : WDFNode("Capacitor"), C_value(value), fs(fs), alpha(alpha), b_coef((1.0 - alpha) / 2.0),
187 a_coef((1.0 + alpha) / 2.0)
188 {
189 calcImpedance();
190 }
~Capacitor()191 virtual ~Capacitor() {}
192
193 /** Sets the capacitance value of the WDF capacitor, in Farads. */
setCapacitanceValue(double newC)194 void setCapacitanceValue(double newC)
195 {
196 if (newC == C_value)
197 return;
198
199 C_value = newC;
200 propagateImpedance();
201 }
202
203 /** Computes the impedance of the WDF capacitor,
204 * 1
205 * Z_C = ---------------------
206 * (1 + alpha) * f_s * C
207 */
calcImpedance()208 inline void calcImpedance() override
209 {
210 R = 1.0 / ((1.0 + alpha) * C_value * fs);
211 G = 1.0 / R;
212 }
213
214 /** Accepts an incident wave into a WDF capacitor. */
incident(double x)215 inline void incident(double x) noexcept override
216 {
217 a = x;
218 z = a;
219 }
220
221 /** Propogates a reflected wave from a WDF capacitor. */
reflected()222 inline double reflected() noexcept override
223 {
224 b = b_coef * b + a_coef * z;
225 return b;
226 }
227
228 private:
229 double C_value = 1.0e-6;
230 double z = 0.0;
231
232 const double fs;
233 const double alpha;
234
235 const double b_coef;
236 const double a_coef;
237 };
238
239 /** WDF Inductor Node */
240 class Inductor : public WDFNode
241 {
242 public:
243 /** Creates a new WDF Inductor.
244 * @param value: Inductance value in Farads
245 * @param fs: WDF sample rate
246 * @param alpha: alpha value to be used for the alpha transform,
247 * use 0 for Backwards Euler, use 1 for Bilinear Transform.
248 */
249 Inductor(double value, double fs, double alpha = 1.0)
250 : WDFNode("Inductor"), L_value(value), fs(fs), alpha(alpha), b_coef((1.0 - alpha) / 2.0),
251 a_coef((1.0 + alpha) / 2.0)
252 {
253 calcImpedance();
254 }
~Inductor()255 virtual ~Inductor() {}
256
257 /** Sets the inductance value of the WDF capacitor, in Henries. */
setInductanceValue(double newL)258 void setInductanceValue(double newL)
259 {
260 if (newL == L_value)
261 return;
262
263 L_value = newL;
264 propagateImpedance();
265 }
266
267 /** Computes the impedance of the WDF capacitor,
268 * Z_L = (1 + alpha) * f_s * L
269 */
calcImpedance()270 inline void calcImpedance() override
271 {
272 R = (1.0 + alpha) * L_value * fs;
273 G = 1.0 / R;
274 }
275
276 /** Accepts an incident wave into a WDF inductor. */
incident(double x)277 inline void incident(double x) noexcept override
278 {
279 a = x;
280 z = a;
281 }
282
283 /** Propogates a reflected wave from a WDF inductor. */
reflected()284 inline double reflected() noexcept override
285 {
286 b = b_coef * b - a_coef * z;
287 return b;
288 }
289
290 private:
291 double L_value = 1.0e-6;
292 double z = 0.0;
293
294 const double fs;
295 const double alpha;
296
297 const double b_coef;
298 const double a_coef;
299 };
300
301 /** WDF Switch (non-adaptable) */
302 class Switch : public WDFNode
303 {
304 public:
Switch()305 Switch() : WDFNode("Switch") {}
~Switch()306 virtual ~Switch() {}
307
calcImpedance()308 inline void calcImpedance() override {}
309
310 /** Sets the state of the switch. */
setClosed(bool shouldClose)311 void setClosed(bool shouldClose) { closed = shouldClose; }
312
313 /** Accepts an incident wave into a WDF switch. */
incident(double x)314 inline void incident(double x) noexcept override { a = x; }
315
316 /** Propogates a reflected wave from a WDF switch. */
reflected()317 inline double reflected() noexcept override
318 {
319 b = closed ? -a : a;
320 return b;
321 }
322
323 private:
324 bool closed = true;
325 };
326
327 /** WDF Open circuit (non-adaptable) */
328 class Open : public WDFNode
329 {
330 public:
Open()331 Open() : WDFNode("Open") {}
~Open()332 virtual ~Open()
333 {
334 R = 1.0e15;
335 G = 1.0 / R;
336 }
337
calcImpedance()338 inline void calcImpedance() override {}
339
340 /** Accepts an incident wave into a WDF open. */
incident(double x)341 inline void incident(double x) noexcept override { a = x; }
342
343 /** Propogates a reflected wave from a WDF open. */
reflected()344 inline double reflected() noexcept override
345 {
346 b = a;
347 return b;
348 }
349 };
350
351 /** WDF Short circuit (non-adaptable) */
352 class Short : public WDFNode
353 {
354 public:
Short()355 Short() : WDFNode("Short") {}
~Short()356 virtual ~Short()
357 {
358 R = 1.0e-15;
359 G = 1.0 / R;
360 }
361
calcImpedance()362 inline void calcImpedance() override {}
363
364 /** Accepts an incident wave into a WDF short. */
incident(double x)365 inline void incident(double x) noexcept override { a = x; }
366
367 /** Propogates a reflected wave from a WDF short. */
reflected()368 inline double reflected() noexcept override
369 {
370 b = -a;
371 return b;
372 }
373 };
374
375 /** WDF Voltage Polarity Inverter */
376 class PolarityInverter : public WDFNode
377 {
378 public:
379 /** Creates a new WDF polarity inverter
380 * @param port1: the port to connect to the inverter
381 */
PolarityInverter(WDFNode * port1)382 PolarityInverter(WDFNode *port1) : WDFNode("Polarity Inverter"), port1(port1)
383 {
384 port1->connectToNode(this);
385 calcImpedance();
386 }
~PolarityInverter()387 virtual ~PolarityInverter() {}
388
389 /** Calculates the impedance of the WDF inverter
390 * (same impedance as the connected node).
391 */
calcImpedance()392 inline void calcImpedance() override
393 {
394 R = port1->R;
395 G = 1.0 / R;
396 }
397
398 /** Accepts an incident wave into a WDF inverter. */
incident(double x)399 inline void incident(double x) noexcept override
400 {
401 a = x;
402 port1->incident(-x);
403 }
404
405 /** Propogates a reflected wave from a WDF inverter. */
reflected()406 inline double reflected() noexcept override
407 {
408 b = -port1->reflected();
409 return b;
410 }
411
412 private:
413 WDFNode *port1 = nullptr;
414 };
415
416 /** WDF Voltage Polarity Inverter */
417 template <typename PortType> class PolarityInverterT : public WDFNode
418 {
419 public:
420 /** Creates a new WDF polarity inverter */
PolarityInverterT()421 PolarityInverterT() : WDFNode("Polarity Inverter") { detail::default_construct(port1); }
~PolarityInverterT()422 virtual ~PolarityInverterT() {}
423
initialise()424 void initialise()
425 {
426 port1->connectToNode(this);
427 calcImpedance();
428 }
429
430 /** Calculates the impedance of the WDF inverter
431 * (same impedance as the connected node).
432 */
calcImpedance()433 inline void calcImpedance() override
434 {
435 R = port1->R;
436 G = 1.0 / R;
437 }
438
439 /** Accepts an incident wave into a WDF inverter. */
incident(double x)440 inline void incident(double x) noexcept override
441 {
442 a = x;
443 port1->incident(-x);
444 }
445
446 /** Propogates a reflected wave from a WDF inverter. */
reflected()447 inline double reflected() noexcept override
448 {
449 b = -port1->reflected();
450 return b;
451 }
452
453 std::unique_ptr<PortType> port1;
454 };
455
456 /** WDF y-parameter 2-port (short circuit admittance) */
457 class YParameter : public WDFNode
458 {
459 public:
YParameter(WDFNode * port1,double y11,double y12,double y21,double y22)460 YParameter(WDFNode *port1, double y11, double y12, double y21, double y22)
461 : WDFNode("YParameter"), port1(port1)
462 {
463 y[0][0] = y11;
464 y[0][1] = y12;
465 y[1][0] = y21;
466 y[1][1] = y22;
467
468 port1->connectToNode(this);
469 calcImpedance();
470 }
471
~YParameter()472 virtual ~YParameter() {}
473
calcImpedance()474 inline void calcImpedance() override
475 {
476 denominator = y[1][1] + port1->R * y[0][0] * y[1][1] - port1->R * y[0][1] * y[1][0];
477 R = (port1->R * y[0][0] + 1.0) / denominator;
478 G = 1.0 / R;
479
480 double rSq = port1->R * port1->R;
481 double num1A = -y[1][1] * rSq * y[0][0] * y[0][0];
482 double num2A = y[0][1] * y[1][0] * rSq * y[0][0];
483
484 A = (num1A + num2A + y[1][1]) / (denominator * (port1->R * y[0][0] + 1.0));
485 B = -port1->R * y[0][1] / (port1->R * y[0][0] + 1.0);
486 C = -y[1][0] / denominator;
487 }
488
incident(double x)489 inline void incident(double x) noexcept override
490 {
491 a = x;
492 port1->incident(A * port1->b + B * x);
493 }
494
reflected()495 inline double reflected() noexcept override
496 {
497 b = C * port1->reflected();
498 return b;
499 }
500
501 private:
502 WDFNode *port1;
503 double y[2][2] = {{0.0, 0.0}, {0.0, 0.0}};
504
505 double denominator = 1.0;
506 double A = 1.0f;
507 double B = 1.0f;
508 double C = 1.0f;
509 };
510
511 /** WDF 3-port adapter base class */
512 class WDFAdaptor : public WDFNode
513 {
514 public:
WDFAdaptor(WDFNode * port1,WDFNode * port2,std::string type)515 WDFAdaptor(WDFNode *port1, WDFNode *port2, std::string type)
516 : WDFNode(type), port1(port1), port2(port2)
517 {
518 port1->connectToNode(this);
519 port2->connectToNode(this);
520 }
~WDFAdaptor()521 virtual ~WDFAdaptor() {}
522
523 protected:
524 WDFNode *port1 = nullptr;
525 WDFNode *port2 = nullptr;
526 };
527
528 /** WDF 3-port parallel adaptor */
529 class WDFParallel : public WDFAdaptor
530 {
531 public:
532 /** Creates a new WDF parallel adaptor from two connected ports. */
WDFParallel(WDFNode * port1,WDFNode * port2)533 WDFParallel(WDFNode *port1, WDFNode *port2) : WDFAdaptor(port1, port2, "Parallel")
534 {
535 calcImpedance();
536 }
~WDFParallel()537 virtual ~WDFParallel() {}
538
539 /** Computes the impedance for a WDF parallel adaptor.
540 * 1 1 1
541 * --- = --- + ---
542 * Z_p Z_1 Z_2
543 */
calcImpedance()544 inline void calcImpedance() override
545 {
546 G = port1->G + port2->G;
547 R = 1.0 / G;
548 port1Reflect = port1->G / G;
549 port2Reflect = port2->G / G;
550 }
551
552 /** Accepts an incident wave into a WDF parallel adaptor. */
incident(double x)553 inline void incident(double x) noexcept override
554 {
555 port1->incident(x + (port2->b - port1->b) * port2Reflect);
556 port2->incident(x + (port2->b - port1->b) * -port1Reflect);
557 a = x;
558 }
559
560 /** Propogates a reflected wave from a WDF parallel adaptor. */
reflected()561 inline double reflected() noexcept override
562 {
563 b = port1Reflect * port1->reflected() + port2Reflect * port2->reflected();
564 return b;
565 }
566
567 private:
568 double port1Reflect = 1.0;
569 double port2Reflect = 1.0;
570 };
571
572 /** WDF 3-port parallel adaptor */
573 template <typename Port1Type, typename Port2Type> class WDFParallelT : public WDFNode
574 {
575 public:
576 /** Creates a new WDF parallel adaptor from two connected ports. */
WDFParallelT()577 WDFParallelT() : WDFNode("Parallel")
578 {
579 detail::default_construct(port1);
580 detail::default_construct(port2);
581 }
~WDFParallelT()582 virtual ~WDFParallelT() {}
583
initialise()584 void initialise()
585 {
586 port1->connectToNode(this);
587 port2->connectToNode(this);
588 calcImpedance();
589 }
590
591 /** Computes the impedance for a WDF parallel adaptor.
592 * 1 1 1
593 * --- = --- + ---
594 * Z_p Z_1 Z_2
595 */
calcImpedance()596 inline void calcImpedance() override
597 {
598 G = port1->G + port2->G;
599 R = 1.0 / G;
600 port1Reflect = port1->G / G;
601 port2Reflect = port2->G / G;
602 }
603
604 /** Accepts an incident wave into a WDF parallel adaptor. */
incident(double x)605 inline void incident(double x) noexcept override
606 {
607 port1->incident(x + (port2->b - port1->b) * port2Reflect);
608 port2->incident(x + (port2->b - port1->b) * -port1Reflect);
609 a = x;
610 }
611
612 /** Propogates a reflected wave from a WDF parallel adaptor. */
reflected()613 inline double reflected() noexcept override
614 {
615 b = port1Reflect * port1->reflected() + port2Reflect * port2->reflected();
616 return b;
617 }
618
619 std::unique_ptr<Port1Type> port1;
620 std::unique_ptr<Port2Type> port2;
621
622 private:
623 double port1Reflect = 1.0;
624 double port2Reflect = 1.0;
625 };
626
627 /** WDF 3-port series adaptor */
628 class WDFSeries : public WDFAdaptor
629 {
630 public:
631 /** Creates a new WDF series adaptor from two connected ports. */
WDFSeries(WDFNode * port1,WDFNode * port2)632 WDFSeries(WDFNode *port1, WDFNode *port2) : WDFAdaptor(port1, port2, "Series")
633 {
634 calcImpedance();
635 }
~WDFSeries()636 virtual ~WDFSeries() {}
637
638 /** Computes the impedance for a WDF parallel adaptor.
639 * Z_s = Z_1 + Z_2
640 */
calcImpedance()641 inline void calcImpedance() override
642 {
643 R = port1->R + port2->R;
644 G = 1.0 / R;
645 port1Reflect = port1->R / R;
646 port2Reflect = port2->R / R;
647 }
648
649 /** Accepts an incident wave into a WDF series adaptor. */
incident(double x)650 inline void incident(double x) noexcept override
651 {
652 port1->incident(port1->b - port1Reflect * (x + port1->b + port2->b));
653 port2->incident(port2->b - port2Reflect * (x + port1->b + port2->b));
654
655 a = x;
656 }
657
658 /** Propogates a reflected wave from a WDF series adaptor. */
reflected()659 inline double reflected() noexcept override
660 {
661 b = -(port1->reflected() + port2->reflected());
662 return b;
663 }
664
665 private:
666 double port1Reflect = 1.0;
667 double port2Reflect = 1.0;
668 };
669
670 /** WDF 3-port series adaptor */
671 template <typename Port1Type, typename Port2Type> class WDFSeriesT : public WDFNode
672 {
673 public:
674 /** Creates a new WDF series adaptor from two connected ports. */
WDFSeriesT()675 WDFSeriesT() : WDFNode("Series")
676 {
677 detail::default_construct(port1);
678 detail::default_construct(port2);
679 }
~WDFSeriesT()680 virtual ~WDFSeriesT() {}
681
initialise()682 void initialise()
683 {
684 port1->connectToNode(this);
685 port2->connectToNode(this);
686 calcImpedance();
687 }
688
689 /** Computes the impedance for a WDF parallel adaptor.
690 * Z_s = Z_1 + Z_2
691 */
calcImpedance()692 inline void calcImpedance() override
693 {
694 R = port1->R + port2->R;
695 G = 1.0 / R;
696 port1Reflect = port1->R / R;
697 port2Reflect = port2->R / R;
698 }
699
700 /** Accepts an incident wave into a WDF series adaptor. */
incident(double x)701 inline void incident(double x) noexcept override
702 {
703 port1->incident(port1->b - port1Reflect * (x + port1->b + port2->b));
704 port2->incident(port2->b - port2Reflect * (x + port1->b + port2->b));
705
706 a = x;
707 }
708
709 /** Propogates a reflected wave from a WDF series adaptor. */
reflected()710 inline double reflected() noexcept override
711 {
712 b = -(port1->reflected() + port2->reflected());
713 return b;
714 }
715
716 std::unique_ptr<Port1Type> port1;
717 std::unique_ptr<Port2Type> port2;
718
719 private:
720 double port1Reflect = 1.0;
721 double port2Reflect = 1.0;
722 };
723
724 /** WDF Voltage source with series resistance */
725 class ResistiveVoltageSource : public WDFNode
726 {
727 public:
728 /** Creates a new resistive voltage source.
729 * @param value: initial resistance value, in Ohms
730 */
731 ResistiveVoltageSource(double value = 1.0e-9) : WDFNode("Resistive Voltage"), R_value(value)
732 {
733 calcImpedance();
734 }
~ResistiveVoltageSource()735 virtual ~ResistiveVoltageSource() {}
736
737 /** Sets the resistance value of the series resistor, in Ohms. */
setResistanceValue(double newR)738 void setResistanceValue(double newR)
739 {
740 if (newR == R_value)
741 return;
742
743 R_value = newR;
744 propagateImpedance();
745 }
746
747 /** Computes the impedance for a WDF resistive voltage souce
748 * Z_Vr = Z_R
749 */
calcImpedance()750 inline void calcImpedance() override
751 {
752 R = R_value;
753 G = 1.0 / R;
754 }
755
756 /** Sets the voltage of the voltage source, in Volts */
setVoltage(double newV)757 void setVoltage(double newV) { Vs = newV; }
758
759 /** Accepts an incident wave into a WDF resistive voltage source. */
incident(double x)760 inline void incident(double x) noexcept override { a = x; }
761
762 /** Propogates a reflected wave from a WDF resistive voltage source. */
reflected()763 inline double reflected() noexcept override
764 {
765 b = Vs;
766 return b;
767 }
768
769 private:
770 double Vs = 0.0;
771 double R_value = 1.0e-9;
772 };
773
774 /** WDF Ideal Voltage source (non-adaptable) */
775 class IdealVoltageSource : public WDFNode
776 {
777 public:
IdealVoltageSource()778 IdealVoltageSource() : WDFNode("IdealVoltage") { calcImpedance(); }
~IdealVoltageSource()779 virtual ~IdealVoltageSource() {}
780
calcImpedance()781 inline void calcImpedance() override {}
782
783 /** Sets the voltage of the voltage source, in Volts */
setVoltage(double newV)784 void setVoltage(double newV) { Vs = newV; }
785
786 /** Accepts an incident wave into a WDF ideal voltage source. */
incident(double x)787 inline void incident(double x) noexcept override { a = x; }
788
789 /** Propogates a reflected wave from a WDF ideal voltage source. */
reflected()790 inline double reflected() noexcept override
791 {
792 b = -a + 2.0 * Vs;
793 return b;
794 }
795
796 private:
797 double Vs = 0.0;
798 };
799
800 /** WDF Current source with parallel resistance */
801 class ResistiveCurrentSource : public WDFNode
802 {
803 public:
804 /** Creates a new resistive current source.
805 * @param value: initial resistance value, in Ohms
806 */
807 ResistiveCurrentSource(double value = 1.0e9) : WDFNode("Resistive Current"), R_value(value)
808 {
809 calcImpedance();
810 }
~ResistiveCurrentSource()811 virtual ~ResistiveCurrentSource() {}
812
813 /** Sets the resistance value of the parallel resistor, in Ohms. */
setResistanceValue(double newR)814 void setResistanceValue(double newR)
815 {
816 if (newR == R_value)
817 return;
818
819 R_value = newR;
820 propagateImpedance();
821 }
822
823 /** Computes the impedance for a WDF resistive current souce
824 * Z_Ir = Z_R
825 */
calcImpedance()826 inline void calcImpedance() override
827 {
828 R = R_value;
829 G = 1.0 / R;
830 }
831
832 /** Sets the current of the current source, in Amps */
setCurrent(double newI)833 void setCurrent(double newI) { Is = newI; }
834
835 /** Accepts an incident wave into a WDF resistive current source. */
incident(double x)836 inline void incident(double x) noexcept override { a = x; }
837
838 /** Propogates a reflected wave from a WDF resistive current source. */
reflected()839 inline double reflected() noexcept override
840 {
841 b = 2 * R * Is;
842 return b;
843 }
844
845 private:
846 double Is = 0.0;
847 double R_value = 1.0e9;
848 };
849
850 /** WDF Current source (non-adpatable) */
851 class IdealCurrentSource : public WDFNode
852 {
853 public:
IdealCurrentSource()854 IdealCurrentSource() : WDFNode("Ideal Current") { calcImpedance(); }
~IdealCurrentSource()855 virtual ~IdealCurrentSource() {}
856
calcImpedance()857 inline void calcImpedance() override {}
858
859 /** Sets the current of the current source, in Amps */
setCurrent(double newI)860 void setCurrent(double newI) { Is = newI; }
861
862 /** Accepts an incident wave into a WDF ideal current source. */
incident(double x)863 inline void incident(double x) noexcept override { a = x; }
864
865 /** Propogates a reflected wave from a WDF ideal current source. */
reflected()866 inline double reflected() noexcept override
867 {
868 b = 2 * next->R * Is + a;
869 return b;
870 }
871
872 private:
873 double Is = 0.0;
874 };
875
876 /** Signum function to determine the sign of the input. */
signum(T val)877 template <typename T> inline int signum(T val) { return (T(0) < val) - (val < T(0)); }
878
879 /** WDF diode pair (non-adaptable)
880 * See Werner et al., "An Improved and Generalized Diode Clipper Model for Wave Digital Filters"
881 * https://www.researchgate.net/publication/299514713_An_Improved_and_Generalized_Diode_Clipper_Model_for_Wave_Digital_Filters
882 */
883 class DiodePair : public WDFNode
884 {
885 public:
886 /** Creates a new WDF diode pair, with the given diode specifications.
887 * @param Is: reverse saturation current
888 * @param Vt: thermal voltage
889 */
DiodePair(double Is,double Vt)890 DiodePair(double Is, double Vt) : WDFNode("DiodePair"), Is(Is), Vt(Vt) {}
891
~DiodePair()892 virtual ~DiodePair() {}
893
calcImpedance()894 inline void calcImpedance() override {}
895
896 /** Accepts an incident wave into a WDF diode pair. */
incident(double x)897 inline void incident(double x) noexcept override { a = x; }
898
899 /** Propogates a reflected wave from a WDF diode pair. */
reflected()900 inline double reflected() noexcept override
901 {
902 // See eqn (18) from reference paper
903 double lambda = (double)signum(a);
904 b = a + 2 * lambda *
905 (next->R * Is - Vt * Omega::omega4(std::log(next->R * Is / Vt) +
906 (lambda * a + next->R * Is) / Vt));
907 return b;
908 }
909
910 private:
911 const double Is; // reverse saturation current
912 const double Vt; // thermal voltage
913 };
914
915 /** WDF diode (non-adaptable)
916 * See Werner et al., "An Improved and Generalized Diode Clipper Model for Wave Digital Filters"
917 * https://www.researchgate.net/publication/299514713_An_Improved_and_Generalized_Diode_Clipper_Model_for_Wave_Digital_Filters
918 */
919 class Diode : public WDFNode
920 {
921 public:
922 /** Creates a new WDF diode, with the given diode specifications.
923 * @param Is: reverse saturation current
924 * @param Vt: thermal voltage
925 */
Diode(double Is,double Vt)926 Diode(double Is, double Vt) : WDFNode("Diode"), Is(Is), Vt(Vt) {}
927
~Diode()928 virtual ~Diode() {}
929
calcImpedance()930 inline void calcImpedance() override {}
931
932 /** Accepts an incident wave into a WDF diode. */
incident(double x)933 inline void incident(double x) noexcept override { a = x; }
934
935 /** Propogates a reflected wave from a WDF diode. */
reflected()936 inline double reflected() noexcept override
937 {
938 // See eqn (10) from reference paper
939 b = a + 2 * next->R * Is -
940 2 * Vt * Omega::omega4(std::log(next->R * Is / Vt) + (a + next->R * Is) / Vt);
941 return b;
942 }
943
944 private:
945 const double Is; // reverse saturation current
946 const double Vt; // thermal voltage
947 };
948
949 } // namespace WDF
950
951 } // namespace chowdsp
952
953 #endif // WDF_H_INCLUDED
954