• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17282283436

28 Aug 2025 12:35AM UTC coverage: 66.075% (-0.2%) from 66.266%
17282283436

push

github

web-flow
Merge pull request #104 from paulmthompson/feature/analog-filter

feat: Implement Analog Filter transform

152 of 179 new or added lines in 4 files covered. (84.92%)

25 existing lines in 3 files now uncovered.

26703 of 40413 relevant lines covered (66.08%)

1115.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.42
/src/DataManager/utils/filter/FilterFactory.hpp
1
#ifndef FILTER_FACTORY_HPP
2
#define FILTER_FACTORY_HPP
3

4
#include "IFilter.hpp"
5
#include "FilterImplementations.hpp"
6
#include "ZeroPhaseDecorator.hpp"
7

8
#include <memory>
9
#include <stdexcept>
10

11
/**
12
 * @brief Factory for creating filters with the new interface
13
 * 
14
 * This factory provides a way to create filters from FilterOptions (for compatibility)
15
 * or directly with specific parameters.
16
 */
17
class FilterFactory {
18
public:
19
    /**
20
     * @brief Create a Butterworth lowpass filter
21
     * 
22
     * @tparam Order Filter order (1-8)
23
     * @param cutoff_hz Cutoff frequency in Hz
24
     * @param sampling_rate_hz Sampling rate in Hz
25
     * @param zero_phase Whether to apply zero-phase filtering
26
     * @return Unique pointer to the created filter
27
     */
28
    template<int Order>
29
    static std::unique_ptr<IFilter> createButterworthLowpass(
11✔
30
            double cutoff_hz, 
31
            double sampling_rate_hz, 
32
            bool zero_phase = false) {
33
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
34
        
35
        auto filter = std::make_unique<ButterworthLowpassFilter<Order>>(cutoff_hz, sampling_rate_hz);
11✔
36
        
37
        if (zero_phase) {
11✔
38
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
2✔
39
        }
40
        
41
        return filter;
9✔
42
    }
11✔
43

44
    /**
45
     * @brief Create a Butterworth highpass filter
46
     * 
47
     * @tparam Order Filter order (1-8)
48
     * @param cutoff_hz Cutoff frequency in Hz
49
     * @param sampling_rate_hz Sampling rate in Hz
50
     * @param zero_phase Whether to apply zero-phase filtering
51
     * @return Unique pointer to the created filter
52
     */
53
    template<int Order>
54
    static std::unique_ptr<IFilter> createButterworthHighpass(
3✔
55
            double cutoff_hz, 
56
            double sampling_rate_hz, 
57
            bool zero_phase = false) {
58
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
59
        
60
        auto filter = std::make_unique<ButterworthHighpassFilter<Order>>(cutoff_hz, sampling_rate_hz);
3✔
61
        
62
        if (zero_phase) {
3✔
63
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
64
        }
65
        
66
        return filter;
3✔
67
    }
3✔
68

69
    /**
70
     * @brief Create a Butterworth bandpass filter
71
     * 
72
     * @tparam Order Filter order (1-8)
73
     * @param low_cutoff_hz Low cutoff frequency in Hz
74
     * @param high_cutoff_hz High cutoff frequency in Hz
75
     * @param sampling_rate_hz Sampling rate in Hz
76
     * @param zero_phase Whether to apply zero-phase filtering
77
     * @return Unique pointer to the created filter
78
     */
79
    template<int Order>
80
    static std::unique_ptr<IFilter> createButterworthBandpass(
3✔
81
            double low_cutoff_hz,
82
            double high_cutoff_hz,
83
            double sampling_rate_hz, 
84
            bool zero_phase = false) {
85
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
86
        
87
        auto filter = std::make_unique<ButterworthBandpassFilter<Order>>(
3✔
88
            low_cutoff_hz, high_cutoff_hz, sampling_rate_hz);
89
        
90
        if (zero_phase) {
3✔
91
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
92
        }
93
        
94
        return filter;
3✔
95
    }
3✔
96

97
    /**
98
     * @brief Create a Butterworth bandstop filter
99
     * 
100
     * @tparam Order Filter order (1-8)
101
     * @param low_cutoff_hz Low cutoff frequency in Hz
102
     * @param high_cutoff_hz High cutoff frequency in Hz
103
     * @param sampling_rate_hz Sampling rate in Hz
104
     * @param zero_phase Whether to apply zero-phase filtering
105
     * @return Unique pointer to the created filter
106
     */
107
    template<int Order>
108
    static std::unique_ptr<IFilter> createButterworthBandstop(
1✔
109
            double low_cutoff_hz,
110
            double high_cutoff_hz,
111
            double sampling_rate_hz, 
112
            bool zero_phase = false) {
113
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
114
        
115
        auto filter = std::make_unique<ButterworthBandstopFilter<Order>>(
1✔
116
            low_cutoff_hz, high_cutoff_hz, sampling_rate_hz);
117
        
118
        if (zero_phase) {
1✔
119
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
120
        }
121
        
122
        return filter;
1✔
123
    }
1✔
124

125
    /**
126
     * @brief Create a Chebyshev I lowpass filter
127
     * 
128
     * @tparam Order Filter order (1-8)
129
     * @param cutoff_hz Cutoff frequency in Hz
130
     * @param sampling_rate_hz Sampling rate in Hz
131
     * @param ripple_db Passband ripple in dB
132
     * @param zero_phase Whether to apply zero-phase filtering
133
     * @return Unique pointer to the created filter
134
     */
135
    template<int Order>
136
    static std::unique_ptr<IFilter> createChebyshevILowpass(
5✔
137
            double cutoff_hz, 
138
            double sampling_rate_hz, 
139
            double ripple_db,
140
            bool zero_phase = false) {
141
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
142
        
143
        auto filter = std::make_unique<ChebyshevILowpassFilter<Order>>(cutoff_hz, sampling_rate_hz, ripple_db);
5✔
144
        
145
        if (zero_phase) {
5✔
146
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
1✔
147
        }
148
        
149
        return filter;
4✔
150
    }
5✔
151

152
    /**
153
     * @brief Create a Chebyshev I highpass filter
154
     * 
155
     * @tparam Order Filter order (1-8)
156
     * @param cutoff_hz Cutoff frequency in Hz
157
     * @param sampling_rate_hz Sampling rate in Hz
158
     * @param ripple_db Passband ripple in dB
159
     * @param zero_phase Whether to apply zero-phase filtering
160
     * @return Unique pointer to the created filter
161
     */
162
    template<int Order>
163
    static std::unique_ptr<IFilter> createChebyshevIHighpass(
2✔
164
            double cutoff_hz, 
165
            double sampling_rate_hz, 
166
            double ripple_db,
167
            bool zero_phase = false) {
168
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
169
        
170
        auto filter = std::make_unique<ChebyshevIHighpassFilter<Order>>(cutoff_hz, sampling_rate_hz, ripple_db);
2✔
171
        
172
        if (zero_phase) {
2✔
173
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
174
        }
175
        
176
        return filter;
2✔
177
    }
2✔
178

179
    /**
180
     * @brief Create a Chebyshev I bandpass filter
181
     * 
182
     * @tparam Order Filter order (1-8)
183
     * @param low_cutoff_hz Low cutoff frequency in Hz
184
     * @param high_cutoff_hz High cutoff frequency in Hz
185
     * @param sampling_rate_hz Sampling rate in Hz
186
     * @param ripple_db Passband ripple in dB
187
     * @param zero_phase Whether to apply zero-phase filtering
188
     * @return Unique pointer to the created filter
189
     */
190
    template<int Order>
191
    static std::unique_ptr<IFilter> createChebyshevIBandpass(
1✔
192
            double low_cutoff_hz,
193
            double high_cutoff_hz,
194
            double sampling_rate_hz, 
195
            double ripple_db,
196
            bool zero_phase = false) {
197
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
198
        
199
        auto filter = std::make_unique<ChebyshevIBandpassFilter<Order>>(
1✔
200
            low_cutoff_hz, high_cutoff_hz, sampling_rate_hz, ripple_db);
201
        
202
        if (zero_phase) {
1✔
203
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
204
        }
205
        
206
        return filter;
1✔
207
    }
1✔
208

209
    /**
210
     * @brief Create a Chebyshev I bandstop filter
211
     * 
212
     * @tparam Order Filter order (1-8)
213
     * @param low_cutoff_hz Low cutoff frequency in Hz
214
     * @param high_cutoff_hz High cutoff frequency in Hz
215
     * @param ripple_db Passband ripple in dB
216
     * @param zero_phase Whether to apply zero-phase filtering
217
     * @return Unique pointer to the created filter
218
     */
219
    template<int Order>
220
    static std::unique_ptr<IFilter> createChebyshevIBandstop(
1✔
221
            double low_cutoff_hz,
222
            double high_cutoff_hz,
223
            double sampling_rate_hz, 
224
            double ripple_db,
225
            bool zero_phase = false) {
226
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
227
        
228
        auto filter = std::make_unique<ChebyshevIBandstopFilter<Order>>(
1✔
229
            low_cutoff_hz, high_cutoff_hz, sampling_rate_hz, ripple_db);
230
        
231
        if (zero_phase) {
1✔
232
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
233
        }
234
        
235
        return filter;
1✔
236
    }
1✔
237

238
    /**
239
     * @brief Create a Chebyshev II lowpass filter
240
     * 
241
     * @tparam Order Filter order (1-8)
242
     * @param cutoff_hz Cutoff frequency in Hz
243
     * @param sampling_rate_hz Sampling rate in Hz
244
     * @param stopband_ripple_db Stopband ripple in dB
245
     * @param zero_phase Whether to apply zero-phase filtering
246
     * @return Unique pointer to the created filter
247
     */
248
    template<int Order>
249
    static std::unique_ptr<IFilter> createChebyshevIILowpass(
1✔
250
            double cutoff_hz, 
251
            double sampling_rate_hz, 
252
            double stopband_ripple_db,
253
            bool zero_phase = false) {
254
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
255
        
256
        auto filter = std::make_unique<ChebyshevIILowpassFilter<Order>>(cutoff_hz, sampling_rate_hz, stopband_ripple_db);
1✔
257
        
258
        if (zero_phase) {
1✔
259
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
260
        }
261
        
262
        return filter;
1✔
263
    }
1✔
264

265
    /**
266
     * @brief Create a Chebyshev II highpass filter
267
     * 
268
     * @tparam Order Filter order (1-8)
269
     * @param cutoff_hz Cutoff frequency in Hz
270
     * @param sampling_rate_hz Sampling rate in Hz
271
     * @param stopband_ripple_db Stopband ripple in dB
272
     * @param zero_phase Whether to apply zero-phase filtering
273
     * @return Unique pointer to the created filter
274
     */
275
    template<int Order>
276
    static std::unique_ptr<IFilter> createChebyshevIIHighpass(
1✔
277
            double cutoff_hz, 
278
            double sampling_rate_hz, 
279
            double stopband_ripple_db,
280
            bool zero_phase = false) {
281
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
282
        
283
        auto filter = std::make_unique<ChebyshevIIHighpassFilter<Order>>(cutoff_hz, sampling_rate_hz, stopband_ripple_db);
1✔
284
        
285
        if (zero_phase) {
1✔
286
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
287
        }
288
        
289
        return filter;
1✔
290
    }
1✔
291

292
    /**
293
     * @brief Create a Chebyshev II bandpass filter
294
     * 
295
     * @tparam Order Filter order (1-8)
296
     * @param low_cutoff_hz Low cutoff frequency in Hz
297
     * @param high_cutoff_hz High cutoff frequency in Hz
298
     * @param sampling_rate_hz Sampling rate in Hz
299
     * @param stopband_ripple_db Stopband ripple in dB
300
     * @param zero_phase Whether to apply zero-phase filtering
301
     * @return Unique pointer to the created filter
302
     */
303
    template<int Order>
304
    static std::unique_ptr<IFilter> createChebyshevIIBandpass(
1✔
305
            double low_cutoff_hz,
306
            double high_cutoff_hz,
307
            double sampling_rate_hz, 
308
            double stopband_ripple_db,
309
            bool zero_phase = false) {
310
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
311
        
312
        auto filter = std::make_unique<ChebyshevIIBandpassFilter<Order>>(
1✔
313
            low_cutoff_hz, high_cutoff_hz, sampling_rate_hz, stopband_ripple_db);
314
        
315
        if (zero_phase) {
1✔
316
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
317
        }
318
        
319
        return filter;
1✔
320
    }
1✔
321

322
    /**
323
     * @brief Create a Chebyshev II bandstop filter
324
     * 
325
     * @tparam Order Filter order (1-8)
326
     * @param low_cutoff_hz Low cutoff frequency in Hz
327
     * @param high_cutoff_hz High cutoff frequency in Hz
328
     * @param sampling_rate_hz Sampling rate in Hz
329
     * @param stopband_ripple_db Stopband ripple in dB
330
     * @param zero_phase Whether to apply zero-phase filtering
331
     * @return Unique pointer to the created filter
332
     */
333
    template<int Order>
334
    static std::unique_ptr<IFilter> createChebyshevIIBandstop(
1✔
335
            double low_cutoff_hz,
336
            double high_cutoff_hz,
337
            double sampling_rate_hz, 
338
            double stopband_ripple_db,
339
            bool zero_phase = false) {
340
        static_assert(Order >= 1 && Order <= 8, "Filter order must be between 1 and 8");
341
        
342
        auto filter = std::make_unique<ChebyshevIIBandstopFilter<Order>>(
1✔
343
            low_cutoff_hz, high_cutoff_hz, sampling_rate_hz, stopband_ripple_db);
344
        
345
        if (zero_phase) {
1✔
346
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
347
        }
348
        
349
        return filter;
1✔
350
    }
1✔
351

352
    /**
353
     * @brief Create an RBJ lowpass filter (always 2nd order)
354
     * 
355
     * @param cutoff_hz Cutoff frequency in Hz
356
     * @param sampling_rate_hz Sampling rate in Hz
357
     * @param q_factor Q factor for the filter
358
     * @param zero_phase Whether to apply zero-phase filtering
359
     * @return Unique pointer to the created filter
360
     */
361
    static std::unique_ptr<IFilter> createRBJLowpass(
1✔
362
            double cutoff_hz, 
363
            double sampling_rate_hz, 
364
            double q_factor = 0.707,
365
            bool zero_phase = false) {
366
        
367
        auto filter = std::make_unique<RBJLowpassFilter>(cutoff_hz, sampling_rate_hz, q_factor);
1✔
368
        
369
        if (zero_phase) {
1✔
370
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
371
        }
372
        
373
        return filter;
1✔
374
    }
1✔
375

376
    /**
377
     * @brief Create an RBJ highpass filter (always 2nd order)
378
     * 
379
     * @param cutoff_hz Cutoff frequency in Hz
380
     * @param sampling_rate_hz Sampling rate in Hz
381
     * @param q_factor Q factor for the filter
382
     * @param zero_phase Whether to apply zero-phase filtering
383
     * @return Unique pointer to the created filter
384
     */
385
    static std::unique_ptr<IFilter> createRBJHighpass(
1✔
386
            double cutoff_hz, 
387
            double sampling_rate_hz, 
388
            double q_factor = 0.707,
389
            bool zero_phase = false) {
390
        
391
        auto filter = std::make_unique<RBJHighpassFilter>(cutoff_hz, sampling_rate_hz, q_factor);
1✔
392
        
393
        if (zero_phase) {
1✔
394
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
395
        }
396
        
397
        return filter;
1✔
398
    }
1✔
399

400
    /**
401
     * @brief Create an RBJ bandpass filter (always 2nd order)
402
     * 
403
     * @param center_freq_hz Center frequency in Hz
404
     * @param sampling_rate_hz Sampling rate in Hz
405
     * @param q_factor Q factor for the filter
406
     * @param zero_phase Whether to apply zero-phase filtering
407
     * @return Unique pointer to the created filter
408
     */
409
    static std::unique_ptr<IFilter> createRBJBandpass(
1✔
410
            double center_freq_hz,
411
            double sampling_rate_hz, 
412
            double q_factor = 1.0,
413
            bool zero_phase = false) {
414
        
415
        auto filter = std::make_unique<RBJBandpassFilter>(center_freq_hz, sampling_rate_hz, q_factor);
1✔
416
        
417
        if (zero_phase) {
1✔
418
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
419
        }
420
        
421
        return filter;
1✔
422
    }
1✔
423

424
    /**
425
     * @brief Create an RBJ bandstop/notch filter (always 2nd order)
426
     * 
427
     * @param center_freq_hz Center frequency in Hz for notch filter
428
     * @param sampling_rate_hz Sampling rate in Hz
429
     * @param q_factor Q factor for notch filter
430
     * @param zero_phase Whether to apply zero-phase filtering
431
     * @return Unique pointer to the created filter
432
     */
433
    static std::unique_ptr<IFilter> createRBJBandstop(
1✔
434
            double center_freq_hz,
435
            double sampling_rate_hz, 
436
            double q_factor = 10.0,
437
            bool zero_phase = false) {
438
        
439
        auto filter = std::make_unique<RBJBandstopFilter>(center_freq_hz, sampling_rate_hz, q_factor);
1✔
440
        
441
        if (zero_phase) {
1✔
UNCOV
442
            return std::make_unique<ZeroPhaseDecorator>(std::move(filter));
×
443
        }
444
        
445
        return filter;
1✔
446
    }
1✔
447

448
private:
449
    // Helper template to dispatch filter creation based on runtime order
450
    template<typename FilterCreator>
451
    static std::unique_ptr<IFilter> createWithRuntimeOrder(int order, FilterCreator creator) {
452
        switch (order) {
453
            case 1: return creator.template operator()<1>();
454
            case 2: return creator.template operator()<2>();
455
            case 3: return creator.template operator()<3>();
456
            case 4: return creator.template operator()<4>();
457
            case 5: return creator.template operator()<5>();
458
            case 6: return creator.template operator()<6>();
459
            case 7: return creator.template operator()<7>();
460
            case 8: return creator.template operator()<8>();
461
            default:
462
                throw std::invalid_argument("Unsupported filter order: " + std::to_string(order));
463
        }
464
    }
465
};
466

467
#endif // FILTER_FACTORY_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc