1+ #pragma once
2+
3+ #include < dsplib/array.h>
4+ #include < dsplib/utils.h>
5+ #include < dsplib/inplace.h>
6+ #include < memory>
7+
8+ namespace dsplib {
9+
10+ // ------------------------------------------------------------------------------------
11+ // TODO: naming
12+ enum class IIRStructure
13+ {
14+ DirectForm1, // Direct form I
15+ DirectForm2, // Direct form II
16+ DirectForm1Tr, // Direct form I (transposed)
17+ DirectForm2Tr // Direct form II (transposed)
18+ };
19+
20+ // ------------------------------------------------------------------------------------
21+ // Infinite impulse response filter
22+ template <typename T>
23+ class IIRFilter
24+ {
25+ public:
26+ explicit IIRFilter (const arr_real& num, const arr_real& den)
27+ : num_{num}
28+ , den_{den}
29+ , n_{den_.size ()} {
30+ if (den_.size () != num_.size ()) {
31+ DSPLIB_THROW (" IIR num/den size is not equal" );
32+ }
33+ // TODO: num_ /= den_[0], den_ /= den_[0]
34+ dx_ = dsplib::zeros (n_);
35+ dy_ = dsplib::zeros (n_ - 1 );
36+ }
37+
38+ base_array<T> operator ()(const base_array<T>& x) {
39+ return this ->process (x);
40+ }
41+
42+ T operator ()(const T& x) {
43+ return this ->process (x);
44+ }
45+
46+ base_array<T> process (const base_array<T>& x) {
47+ auto y = zeros (x.size ());
48+ for (int i = 0 ; i < x.size (); ++i) {
49+ y[i] = process (x[i]);
50+ }
51+ return y;
52+ }
53+
54+ T process (T x) {
55+ // TODO: cyclic buffer?
56+ dx_.slice (1 , n_) = dx_.slice (0 , n_ - 1 );
57+ dx_[0 ] = x;
58+ T y = 0 ;
59+ for (int i = 0 ; i < n_; ++i) {
60+ y += num_[i] * dx_[i];
61+ }
62+ for (int i = 0 ; i < n_ - 1 ; ++i) {
63+ y -= den_[i + 1 ] * dy_[i];
64+ }
65+ dy_.slice (1 , n_ - 1 ) = dy_.slice (0 , n_ - 2 );
66+ dy_[0 ] = y;
67+ return y;
68+ }
69+
70+ private:
71+ arr_real num_;
72+ arr_real den_;
73+ base_array<T> dx_;
74+ base_array<T> dy_;
75+ int n_;
76+ };
77+
78+ // ------------------------------------------------------------------------------------
79+ class BaseBiquadSection
80+ {
81+ public:
82+ explicit BaseBiquadSection (real_t b0, real_t b1, real_t b2, real_t a1, real_t a2)
83+ : b0_{b0}
84+ , b1_{b1}
85+ , b2_{b2}
86+ , a1_{a1}
87+ , a2_{a2} {
88+ }
89+
90+ virtual ~BaseBiquadSection () {
91+ }
92+
93+ virtual void process (arr_real& x) = 0;
94+
95+ protected:
96+ const real_t b0_;
97+ const real_t b1_;
98+ const real_t b2_;
99+ const real_t a1_;
100+ const real_t a2_;
101+ };
102+
103+ // ------------------------------------------------------------------------------------
104+ class BiquadSection1 : public BaseBiquadSection
105+ {
106+ public:
107+ explicit BiquadSection1 (real_t b0, real_t b1, real_t b2, real_t a1, real_t a2)
108+ : BaseBiquadSection(b0, b1, b2, a1, a2) {
109+ }
110+
111+ real_t process (const real_t & x) noexcept {
112+ const real_t y = (x * b0_) + (x1_ * b1_) + (x2_ * b2_) - (y1_ * a1_) - (y2_ * a2_);
113+ x2_ = x1_;
114+ x1_ = x;
115+ y2_ = y1_;
116+ y1_ = y;
117+ return y;
118+ }
119+
120+ void process (arr_real& x) noexcept final {
121+ for (auto & v : x) {
122+ v = process (v);
123+ }
124+ }
125+
126+ private:
127+ real_t x1_{0 };
128+ real_t x2_{0 };
129+ real_t y1_{0 };
130+ real_t y2_{0 };
131+ };
132+
133+ // ------------------------------------------------------------------------------------
134+ class BiquadSection1Tr : public BaseBiquadSection
135+ {
136+ public:
137+ explicit BiquadSection1Tr (real_t b0, real_t b1, real_t b2, real_t a1, real_t a2)
138+ : BaseBiquadSection(b0, b1, b2, a1, a2) {
139+ }
140+
141+ real_t process (const real_t & x) noexcept {
142+ const real_t u0 = (x - x1_);
143+ const real_t y = u0 * b0_ + y1_;
144+ x1_ = x2_ + u0 * a1_;
145+ x2_ = u0 * a2_;
146+ y1_ = y2_ + u0 * b1_;
147+ y2_ = u0 * b2_;
148+ return y;
149+ }
150+
151+ void process (arr_real& x) noexcept final {
152+ for (auto & v : x) {
153+ v = process (v);
154+ }
155+ }
156+
157+ private:
158+ real_t x1_{0 };
159+ real_t x2_{0 };
160+ real_t y1_{0 };
161+ real_t y2_{0 };
162+ };
163+
164+ // ------------------------------------------------------------------------------------
165+ class BiquadSection2 : public BaseBiquadSection
166+ {
167+ public:
168+ explicit BiquadSection2 (real_t b0, real_t b1, real_t b2, real_t a1, real_t a2)
169+ : BaseBiquadSection(b0, b1, b2, a1, a2) {
170+ }
171+
172+ real_t process (const real_t & x) noexcept {
173+ const real_t u0 = x - u2_ * a2_ - u1_ * a1_;
174+ const real_t y = u0 * b0_ + u1_ * b1_ + u2_ * b2_;
175+ u2_ = u1_;
176+ u1_ = u0;
177+ return y;
178+ }
179+
180+ void process (arr_real& x) noexcept final {
181+ for (auto & v : x) {
182+ v = process (v);
183+ }
184+ }
185+
186+ private:
187+ real_t u1_{0 };
188+ real_t u2_{0 };
189+ };
190+
191+ // ------------------------------------------------------------------------------------
192+ class BiquadSection2Tr : public BaseBiquadSection
193+ {
194+ public:
195+ explicit BiquadSection2Tr (real_t b0, real_t b1, real_t b2, real_t a1, real_t a2)
196+ : BaseBiquadSection(b0, b1, b2, a1, a2) {
197+ }
198+
199+ real_t process (const real_t & x) noexcept {
200+ const real_t y = u1_ + (b0_ * x);
201+ u1_ = (b1_ * x) - (a1_ * y) + u2_;
202+ u2_ = (b2_ * x) - (a2_ * y);
203+ return y;
204+ }
205+
206+ void process (arr_real& x) noexcept final {
207+ for (auto & v : x) {
208+ v = process (v);
209+ }
210+ }
211+
212+ private:
213+ real_t u1_{0 };
214+ real_t u2_{0 };
215+ };
216+
217+ // ------------------------------------------------------------------------------------
218+ // IIR filter using biquadratic structures
219+ class BiquadSection
220+ {
221+ public:
222+ explicit BiquadSection (real_t b0, real_t b1, real_t b2, real_t a1, real_t a2,
223+ IIRStructure structure = IIRStructure::DirectForm2Tr) {
224+ switch (structure) {
225+ case dsplib::IIRStructure::DirectForm1:
226+ d_ = std::make_unique<BiquadSection1>(b0, b1, b2, a1, a2);
227+ break ;
228+ case dsplib::IIRStructure::DirectForm1Tr:
229+ d_ = std::make_unique<BiquadSection1Tr>(b0, b1, b2, a1, a2);
230+ break ;
231+ case dsplib::IIRStructure::DirectForm2:
232+ d_ = std::make_unique<BiquadSection2>(b0, b1, b2, a1, a2);
233+ break ;
234+ case dsplib::IIRStructure::DirectForm2Tr:
235+ d_ = std::make_unique<BiquadSection2Tr>(b0, b1, b2, a1, a2);
236+ break ;
237+ };
238+ }
239+
240+ explicit BiquadSection (const arr_real& num, const arr_real& den)
241+ : BiquadSection{num[0 ], num[1 ], num[2 ], den[1 ], den[2 ]} {
242+ DSPLIB_ASSERT ((den.size () == 3 ) && (num.size () == 3 ), " biquad coeffs must have length 3" );
243+ }
244+
245+ void process (arr_real& x) noexcept {
246+ d_->process (x);
247+ }
248+
249+ private:
250+ std::unique_ptr<BaseBiquadSection> d_;
251+ };
252+
253+ // ------------------------------------------------------------------------------------
254+ // Second-order section biquadratic IIR filter structures
255+ // TODO: rename to BiquadFilter?
256+ // TODO: use BiquadIIR filter (fixed sizes algorithm)
257+ // TODO: use matrix instead vector<array>
258+ // TODO: base_array<T> implementation
259+ // TODO: inplace process
260+ class SOSFilter
261+ {
262+ public:
263+ explicit SOSFilter (const std::vector<arr_real>& num, const std::vector<arr_real>& den, arr_real scale)
264+ : scale_{scale} {
265+ DSPLIB_ASSERT ((num.size () == den.size ()) && (num[0 ].size () == den[0 ].size ()),
266+ " 'num' and 'den' arrays have different lengths" );
267+ DSPLIB_ASSERT (scale.size () == num.size () + 1 , " 'scale' must be scalar or array[n+1]" );
268+
269+ for (int i = 0 ; i < num.size (); ++i) {
270+ iirs_.emplace_back (BiquadSection (num[i], den[i]));
271+ }
272+ }
273+
274+ explicit SOSFilter (const std::vector<arr_real>& num, const std::vector<arr_real>& den, real_t scale = 1.0 )
275+ : SOSFilter{num, den, _scale_array (num.size (), scale)} {
276+ }
277+
278+ explicit SOSFilter (const arr_real& num, const arr_real& den, real_t scale = 1.0 )
279+ : SOSFilter{{num}, {den}, _scale_array (1 , scale)} {
280+ }
281+
282+ void operator ()(inplace_t <arr_real> x) {
283+ this ->process (x);
284+ }
285+
286+ [[nodiscard]] arr_real operator ()(const arr_real& x) {
287+ return this ->process (x);
288+ }
289+
290+ // TODO: inplace span<real_t>
291+ void process (inplace_t <arr_real> x) {
292+ arr_real& y = x.get ();
293+ y *= scale_[0 ];
294+ for (int k = 0 ; k < iirs_.size (); ++k) {
295+ iirs_[k].process (y);
296+ y *= scale_[k + 1 ];
297+ }
298+ }
299+
300+ [[nodiscard]] arr_real process (const arr_real& x) {
301+ arr_real y (x);
302+ process (inplace (y));
303+ return y;
304+ }
305+
306+ // TODO: get params
307+ // ...
308+
309+ private:
310+ const arr_real scale_;
311+ std::vector<BiquadSection> iirs_;
312+
313+ static arr_real _scale_array (int nsect, real_t scale) {
314+ arr_real r = ones (nsect + 1 );
315+ r[0 ] = scale;
316+ return r;
317+ }
318+ };
319+
320+ // TODO: isstable
321+
322+ } // namespace dsplib
0 commit comments