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