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

paulmthompson / WhiskerToolbox / 18840223125

27 Oct 2025 12:01PM UTC coverage: 73.058% (+0.2%) from 72.822%
18840223125

push

github

paulmthompson
fix failing tests from table designer redesign

69 of 74 new or added lines in 2 files covered. (93.24%)

669 existing lines in 10 files now uncovered.

56029 of 76691 relevant lines covered (73.06%)

45039.63 hits per line

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

67.14
/src/DataManager/transforms/AnalogTimeSeries/AnalogFilter/analog_filter.cpp
1
#include "analog_filter.hpp"
2
#include "AnalogTimeSeries/Analog_Time_Series.hpp"
3
#include "utils/filter/FilterFactory.hpp"
4
#include "utils/filter/FilterImplementations.hpp"
5
#include "utils/filter/ZeroPhaseDecorator.hpp"
6

7
#include <stdexcept>
8

9
std::shared_ptr<AnalogTimeSeries> filter_analog(
14✔
10
        AnalogTimeSeries const * analog_time_series,
11
        AnalogFilterParams const & filterParams) {
12
    return filter_analog(analog_time_series, filterParams, [](int) {});
14✔
13
}
14

15
std::shared_ptr<AnalogTimeSeries> filter_analog(
18✔
16
        AnalogTimeSeries const * analog_time_series,
17
        AnalogFilterParams const & filterParams,
18
        ProgressCallback progressCallback) {
19
    if (!analog_time_series) {
18✔
20
        throw std::invalid_argument("Input analog time series is null");
×
21
    }
22

23
    // Validate filter parameters
24
    if (!filterParams.isValid()) {
18✔
25
        throw std::invalid_argument("Invalid filter parameters");
×
26
    }
27

28
    // Report initial progress
29
    progressCallback(0);
18✔
30

31
    // Handle different parameter types
32
    if (filterParams.filter_instance) {
18✔
33
        // Use pre-created filter instance
34
        auto result = filterWithInstance(analog_time_series, filterParams.filter_instance);
10✔
35
        progressCallback(100);
10✔
36
        return result;
10✔
37
    } else if (filterParams.filter_specification.has_value()) {
18✔
38
        // Create filter from specification
39
        auto filter = filterParams.filter_specification->createFilter();
5✔
40
        auto result = filterWithInstance(analog_time_series, std::move(filter));
5✔
41
        progressCallback(100);
5✔
42
        return result;
5✔
43
    } else if (filterParams.filter_factory) {
8✔
44
        // Create filter from factory
45
        auto filter = filterParams.filter_factory();
3✔
46
        auto result = filterWithInstance(analog_time_series, std::move(filter));
3✔
47
        progressCallback(100);
3✔
48
        return result;
3✔
49
    } else {
3✔
UNCOV
50
        throw std::invalid_argument("No valid filter configuration provided");
×
51
    }
52
}
53

54
// Helper function to filter with a direct filter instance
55
std::shared_ptr<AnalogTimeSeries> filterWithInstance(
18✔
56
        AnalogTimeSeries const * analog_time_series,
57
        std::shared_ptr<IFilter> filter) {
58
    if (!analog_time_series || !filter) {
18✔
UNCOV
59
        throw std::invalid_argument("Input analog time series or filter is null");
×
60
    }
61

62
    // Get all data from the time series
63
    auto & data_span = analog_time_series->getAnalogTimeSeries();
18✔
64
    auto time_series = analog_time_series->getTimeSeries();
18✔
65

66
    if (data_span.empty()) {
18✔
UNCOV
67
        throw std::invalid_argument("No data found in time series");
×
68
    }
69

70
    // Convert to mutable vector for processing
71
    std::vector<float> filtered_data(data_span.begin(), data_span.end());
54✔
72
    std::vector<TimeFrameIndex> filtered_times(time_series.begin(), time_series.end());
54✔
73

74
    // Apply filter
75
    std::span<float> data_span_mutable(filtered_data);
18✔
76
    filter->process(data_span_mutable);
18✔
77

78
    // Create new AnalogTimeSeries with filtered data
79
    return std::make_shared<AnalogTimeSeries>(
80
        std::move(filtered_data),
18✔
81
        std::move(filtered_times));
54✔
82
}
18✔
83

84
// Helper function to filter with a unique_ptr filter instance
85
std::shared_ptr<AnalogTimeSeries> filterWithInstance(
8✔
86
        AnalogTimeSeries const * analog_time_series,
87
        std::unique_ptr<IFilter> filter) {
88
    return filterWithInstance(analog_time_series, std::shared_ptr<IFilter>(std::move(filter)));
16✔
89
}
90

91
std::string AnalogFilterOperation::getName() const {
159✔
92
    return "Filter";
477✔
93
}
94

95
std::type_index AnalogFilterOperation::getTargetInputTypeIndex() const {
159✔
96
    return typeid(std::shared_ptr<AnalogTimeSeries>);
159✔
97
}
98

99
std::unique_ptr<TransformParametersBase> AnalogFilterOperation::getDefaultParameters() const {
8✔
100
    // Create default parameters with 4th order Butterworth lowpass filter
101
    auto params = std::make_unique<AnalogFilterParams>();
8✔
102
    // The default constructor automatically creates the default filter
103
    return params;
16✔
104
}
8✔
105

106
bool AnalogFilterOperation::canApply(DataTypeVariant const & dataVariant) const {
4✔
107
    return std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(dataVariant);
4✔
108
}
109

110
DataTypeVariant AnalogFilterOperation::execute(
3✔
111
        DataTypeVariant const & dataVariant,
112
        TransformParametersBase const * params) {
113
    return execute(dataVariant, params, [](int) {});
3✔
114
}
115

116
DataTypeVariant AnalogFilterOperation::execute(
4✔
117
        DataTypeVariant const & dataVariant,
118
        TransformParametersBase const * params,
119
        ProgressCallback progressCallback) {
120
    if (!params) {
4✔
UNCOV
121
        throw std::invalid_argument("Filter parameters are null");
×
122
    }
123

124
    auto const * filterParams = dynamic_cast<AnalogFilterParams const *>(params);
4✔
125
    if (!filterParams) {
4✔
UNCOV
126
        throw std::invalid_argument("Invalid parameter type for filter operation");
×
127
    }
128

129
    auto const * analog_time_series = std::get_if<std::shared_ptr<AnalogTimeSeries>>(&dataVariant);
4✔
130
    if (!analog_time_series || !*analog_time_series) {
4✔
UNCOV
131
        throw std::invalid_argument("Invalid input data type or null pointer");
×
132
    }
133

134
    auto filtered_data = filter_analog(analog_time_series->get(), *filterParams, progressCallback);
4✔
135
    return DataTypeVariant(filtered_data);
8✔
136
}
4✔
137

138
// ============================================================================
139
// FilterSpecification Implementation
140
// ============================================================================
141

142
std::vector<std::string> FilterSpecification::validate() const {
26✔
143
    std::vector<std::string> errors;
26✔
144
    
145
    // Validate order
146
    if (order < 1 || order > 8) {
26✔
147
        errors.push_back("Filter order must be between 1 and 8, got " + std::to_string(order));
3✔
148
    }
149
    
150
    // Validate sampling rate
151
    if (sampling_rate_hz <= 0.0) {
26✔
152
        errors.push_back("Sampling rate must be positive, got " + std::to_string(sampling_rate_hz));
1✔
153
    }
154
    
155
    // Validate cutoff frequencies based on response type
156
    if (response == FilterResponse::Lowpass || response == FilterResponse::Highpass) {
26✔
157
        if (cutoff_hz <= 0.0) {
20✔
UNCOV
158
            errors.push_back("Cutoff frequency must be positive, got " + std::to_string(cutoff_hz));
×
159
        }
160
        if (cutoff_hz >= sampling_rate_hz / 2.0) {
20✔
161
            errors.push_back("Cutoff frequency (" + std::to_string(cutoff_hz) + 
8✔
162
                           " Hz) must be less than Nyquist frequency (" + 
10✔
163
                           std::to_string(sampling_rate_hz / 2.0) + " Hz)");
12✔
164
        }
165
    } else {  // Bandpass or Bandstop
166
        if (cutoff_low_hz <= 0.0) {
6✔
UNCOV
167
            errors.push_back("Low cutoff frequency must be positive, got " + std::to_string(cutoff_low_hz));
×
168
        }
169
        if (cutoff_high_hz <= 0.0) {
6✔
UNCOV
170
            errors.push_back("High cutoff frequency must be positive, got " + std::to_string(cutoff_high_hz));
×
171
        }
172
        if (cutoff_low_hz >= cutoff_high_hz) {
6✔
173
            errors.push_back("Low cutoff (" + std::to_string(cutoff_low_hz) + 
4✔
174
                           " Hz) must be less than high cutoff (" + std::to_string(cutoff_high_hz) + " Hz)");
4✔
175
        }
176
        if (cutoff_high_hz >= sampling_rate_hz / 2.0) {
6✔
UNCOV
177
            errors.push_back("High cutoff frequency (" + std::to_string(cutoff_high_hz) + 
×
UNCOV
178
                           " Hz) must be less than Nyquist frequency (" + 
×
UNCOV
179
                           std::to_string(sampling_rate_hz / 2.0) + " Hz)");
×
180
        }
181
    }
182
    
183
    // Validate ripple for Chebyshev filters
184
    if (family == FilterFamily::ChebyshevI || family == FilterFamily::ChebyshevII) {
26✔
185
        if (ripple_db <= 0.0) {
4✔
186
            errors.push_back("Ripple must be positive for Chebyshev filters, got " + std::to_string(ripple_db));
1✔
187
        }
188
    }
189
    
190
    // Validate Q factor for RBJ filters
191
    if (family == FilterFamily::RBJ) {
26✔
192
        if (q_factor <= 0.0) {
5✔
UNCOV
193
            errors.push_back("Q factor must be positive for RBJ filters, got " + std::to_string(q_factor));
×
194
        }
195
        if (response != FilterResponse::Bandstop) {
5✔
196
            errors.push_back("RBJ filter family only supports bandstop (notch) response");
3✔
197
        }
198
    }
199
    
200
    return errors;
26✔
UNCOV
201
}
×
202

203
std::unique_ptr<IFilter> FilterSpecification::createFilter() const {
8✔
204
    // Validate first
205
    auto validation_errors = validate();
8✔
206
    if (!validation_errors.empty()) {
8✔
207
        std::string error_msg = "Invalid filter specification:\n";
3✔
208
        for (auto const& error : validation_errors) {
2✔
209
            error_msg += "  - " + error + "\n";
1✔
210
        }
211
        throw std::invalid_argument(error_msg);
1✔
212
    }
1✔
213
    
214
    // Helper lambda to create filter with runtime order dispatch
215
    auto createByOrder = [this](auto createFunc) -> std::unique_ptr<IFilter> {
13✔
216
        switch (order) {
6✔
UNCOV
217
            case 1: return createFunc.template operator()<1>();
×
UNCOV
218
            case 2: return createFunc.template operator()<2>();
×
219
            case 3: return createFunc.template operator()<3>();
2✔
220
            case 4: return createFunc.template operator()<4>();
4✔
UNCOV
221
            case 5: return createFunc.template operator()<5>();
×
UNCOV
222
            case 6: return createFunc.template operator()<6>();
×
UNCOV
223
            case 7: return createFunc.template operator()<7>();
×
UNCOV
224
            case 8: return createFunc.template operator()<8>();
×
UNCOV
225
            default: throw std::invalid_argument("Invalid filter order: " + std::to_string(order));
×
226
        }
227
    };
7✔
228
    
229
    // Create filter based on family and response
230
    if (family == FilterFamily::Butterworth) {
7✔
231
        if (response == FilterResponse::Lowpass) {
4✔
232
            return createByOrder([this]<int Order>() {
4✔
233
                return FilterFactory::createButterworthLowpass<Order>(
234
                    cutoff_hz, sampling_rate_hz, zero_phase);
4✔
235
            });
4✔
UNCOV
236
        } else if (response == FilterResponse::Highpass) {
×
UNCOV
237
            return createByOrder([this]<int Order>() {
×
238
                return FilterFactory::createButterworthHighpass<Order>(
UNCOV
239
                    cutoff_hz, sampling_rate_hz, zero_phase);
×
UNCOV
240
            });
×
UNCOV
241
        } else if (response == FilterResponse::Bandpass) {
×
UNCOV
242
            return createByOrder([this]<int Order>() {
×
243
                return FilterFactory::createButterworthBandpass<Order>(
UNCOV
244
                    cutoff_low_hz, cutoff_high_hz, sampling_rate_hz, zero_phase);
×
UNCOV
245
            });
×
246
        } else {  // Bandstop
UNCOV
247
            return createByOrder([this]<int Order>() {
×
248
                return FilterFactory::createButterworthBandstop<Order>(
UNCOV
249
                    cutoff_low_hz, cutoff_high_hz, sampling_rate_hz, zero_phase);
×
UNCOV
250
            });
×
251
        }
252
    } else if (family == FilterFamily::ChebyshevI) {
3✔
UNCOV
253
        if (response == FilterResponse::Lowpass) {
×
UNCOV
254
            return createByOrder([this]<int Order>() {
×
255
                return FilterFactory::createChebyshevILowpass<Order>(
UNCOV
256
                    cutoff_hz, sampling_rate_hz, ripple_db, zero_phase);
×
UNCOV
257
            });
×
UNCOV
258
        } else if (response == FilterResponse::Highpass) {
×
UNCOV
259
            return createByOrder([this]<int Order>() {
×
260
                return FilterFactory::createChebyshevIHighpass<Order>(
UNCOV
261
                    cutoff_hz, sampling_rate_hz, ripple_db, zero_phase);
×
UNCOV
262
            });
×
UNCOV
263
        } else if (response == FilterResponse::Bandpass) {
×
UNCOV
264
            return createByOrder([this]<int Order>() {
×
265
                return FilterFactory::createChebyshevIBandpass<Order>(
UNCOV
266
                    cutoff_low_hz, cutoff_high_hz, sampling_rate_hz, ripple_db, zero_phase);
×
UNCOV
267
            });
×
268
        } else {  // Bandstop
UNCOV
269
            return createByOrder([this]<int Order>() {
×
270
                return FilterFactory::createChebyshevIBandstop<Order>(
UNCOV
271
                    cutoff_low_hz, cutoff_high_hz, sampling_rate_hz, ripple_db, zero_phase);
×
UNCOV
272
            });
×
273
        }
274
    } else if (family == FilterFamily::ChebyshevII) {
3✔
275
        if (response == FilterResponse::Lowpass) {
2✔
UNCOV
276
            return createByOrder([this]<int Order>() {
×
277
                return FilterFactory::createChebyshevIILowpass<Order>(
UNCOV
278
                    cutoff_hz, sampling_rate_hz, ripple_db, zero_phase);
×
UNCOV
279
            });
×
280
        } else if (response == FilterResponse::Highpass) {
2✔
281
            return createByOrder([this]<int Order>() {
2✔
282
                return FilterFactory::createChebyshevIIHighpass<Order>(
283
                    cutoff_hz, sampling_rate_hz, ripple_db, zero_phase);
2✔
284
            });
2✔
UNCOV
285
        } else if (response == FilterResponse::Bandpass) {
×
UNCOV
286
            return createByOrder([this]<int Order>() {
×
287
                return FilterFactory::createChebyshevIIBandpass<Order>(
UNCOV
288
                    cutoff_low_hz, cutoff_high_hz, sampling_rate_hz, ripple_db, zero_phase);
×
UNCOV
289
            });
×
290
        } else {  // Bandstop
UNCOV
291
            return createByOrder([this]<int Order>() {
×
292
                return FilterFactory::createChebyshevIIBandstop<Order>(
UNCOV
293
                    cutoff_low_hz, cutoff_high_hz, sampling_rate_hz, ripple_db, zero_phase);
×
UNCOV
294
            });
×
295
        }
296
    } else if (family == FilterFamily::RBJ) {
1✔
297
        // RBJ only supports bandstop (validated above)
298
        return FilterFactory::createRBJBandstop(cutoff_hz, sampling_rate_hz, q_factor, zero_phase);
1✔
299
    }
300
    
UNCOV
301
    throw std::invalid_argument("Unknown filter family");
×
302
}
8✔
303

304
nlohmann::json FilterSpecification::toJson() const {
3✔
305
    nlohmann::json j;
3✔
306
    
307
    // Convert family enum to string
308
    switch (family) {
3✔
309
        case FilterFamily::Butterworth: j["filter_family"] = "butterworth"; break;
1✔
310
        case FilterFamily::ChebyshevI: j["filter_family"] = "chebyshev_i"; break;
1✔
UNCOV
311
        case FilterFamily::ChebyshevII: j["filter_family"] = "chebyshev_ii"; break;
×
312
        case FilterFamily::RBJ: j["filter_family"] = "rbj"; break;
1✔
313
    }
314
    
315
    // Convert response enum to string
316
    switch (response) {
3✔
317
        case FilterResponse::Lowpass: j["filter_response"] = "lowpass"; break;
1✔
UNCOV
318
        case FilterResponse::Highpass: j["filter_response"] = "highpass"; break;
×
319
        case FilterResponse::Bandpass: j["filter_response"] = "bandpass"; break;
1✔
320
        case FilterResponse::Bandstop: j["filter_response"] = "bandstop"; break;
1✔
321
    }
322
    
323
    // RBJ filters don't use order
324
    if (family != FilterFamily::RBJ) {
3✔
325
        j["order"] = order;
2✔
326
    }
327
    j["sampling_rate_hz"] = sampling_rate_hz;
3✔
328
    j["zero_phase"] = zero_phase;
3✔
329
    
330
    // Add frequency parameters based on response type and family
331
    // RBJ filters use a single center frequency even for bandstop
332
    if (family == FilterFamily::RBJ) {
3✔
333
        j["cutoff_hz"] = cutoff_hz;  // Center frequency for RBJ notch
1✔
334
    } else if (response == FilterResponse::Lowpass || response == FilterResponse::Highpass) {
2✔
335
        j["cutoff_hz"] = cutoff_hz;
1✔
336
    } else {
337
        j["cutoff_low_hz"] = cutoff_low_hz;
1✔
338
        j["cutoff_high_hz"] = cutoff_high_hz;
1✔
339
    }
340
    
341
    // Add family-specific parameters
342
    if (family == FilterFamily::ChebyshevI || family == FilterFamily::ChebyshevII) {
3✔
343
        j["ripple_db"] = ripple_db;
1✔
344
    }
345
    if (family == FilterFamily::RBJ) {
3✔
346
        j["q_factor"] = q_factor;
1✔
347
    }
348
    
349
    return j;
3✔
UNCOV
350
}
×
351

352
FilterSpecification FilterSpecification::fromJson(nlohmann::json const& json) {
12✔
353
    FilterSpecification spec;
12✔
354
    
355
    // Parse filter family (required)
356
    if (!json.contains("filter_family") || !json["filter_family"].is_string()) {
12✔
UNCOV
357
        throw std::invalid_argument("Missing or invalid 'filter_family' field");
×
358
    }
359
    std::string family_str = json["filter_family"];
12✔
360
    if (family_str == "butterworth") {
12✔
361
        spec.family = FilterFamily::Butterworth;
7✔
362
    } else if (family_str == "chebyshev_i") {
5✔
363
        spec.family = FilterFamily::ChebyshevI;
1✔
364
    } else if (family_str == "chebyshev_ii") {
4✔
UNCOV
365
        spec.family = FilterFamily::ChebyshevII;
×
366
    } else if (family_str == "rbj") {
4✔
367
        spec.family = FilterFamily::RBJ;
3✔
368
    } else {
369
        throw std::invalid_argument("Unknown filter family: " + family_str);
1✔
370
    }
371
    
372
    // Parse filter response (required)
373
    if (!json.contains("filter_response") || !json["filter_response"].is_string()) {
11✔
374
        throw std::invalid_argument("Missing or invalid 'filter_response' field");
1✔
375
    }
376
    std::string response_str = json["filter_response"];
10✔
377
    if (response_str == "lowpass") {
10✔
378
        spec.response = FilterResponse::Lowpass;
6✔
379
    } else if (response_str == "highpass") {
4✔
UNCOV
380
        spec.response = FilterResponse::Highpass;
×
381
    } else if (response_str == "bandpass") {
4✔
382
        spec.response = FilterResponse::Bandpass;
1✔
383
    } else if (response_str == "bandstop") {
3✔
384
        spec.response = FilterResponse::Bandstop;
3✔
385
    } else {
UNCOV
386
        throw std::invalid_argument("Unknown filter response: " + response_str);
×
387
    }
388
    
389
    // Parse order (required for non-RBJ filters)
390
    if (spec.family != FilterFamily::RBJ) {
10✔
391
        if (!json.contains("order") || !json["order"].is_number_integer()) {
7✔
UNCOV
392
            throw std::invalid_argument("Missing or invalid 'order' field");
×
393
        }
394
        spec.order = json["order"];
7✔
395
    }
396
    // RBJ filters don't use order, keep the default
397
    
398
    // Parse sampling rate (required)
399
    if (!json.contains("sampling_rate_hz") || !json["sampling_rate_hz"].is_number()) {
10✔
UNCOV
400
        throw std::invalid_argument("Missing or invalid 'sampling_rate_hz' field");
×
401
    }
402
    spec.sampling_rate_hz = json["sampling_rate_hz"];
10✔
403
    
404
    // Parse zero phase (optional, defaults to false)
405
    if (json.contains("zero_phase") && json["zero_phase"].is_boolean()) {
10✔
406
        spec.zero_phase = json["zero_phase"];
9✔
407
    }
408
    
409
    // Parse frequency parameters - RBJ is a special case
410
    if (spec.family == FilterFamily::RBJ) {
10✔
411
        // RBJ uses a single center frequency even for bandstop
412
        if (!json.contains("cutoff_hz") || !json["cutoff_hz"].is_number()) {
3✔
UNCOV
413
            throw std::invalid_argument("Missing or invalid 'cutoff_hz' field for RBJ filter");
×
414
        }
415
        spec.cutoff_hz = json["cutoff_hz"];
3✔
416
        
417
        if (json.contains("q_factor") && json["q_factor"].is_number()) {
3✔
418
            spec.q_factor = json["q_factor"];
3✔
419
        }
420
    } else if (spec.response == FilterResponse::Lowpass || spec.response == FilterResponse::Highpass) {
7✔
421
        if (!json.contains("cutoff_hz") || !json["cutoff_hz"].is_number()) {
6✔
UNCOV
422
            throw std::invalid_argument("Missing or invalid 'cutoff_hz' field for lowpass/highpass filter");
×
423
        }
424
        spec.cutoff_hz = json["cutoff_hz"];
6✔
425
    } else {  // Bandpass or Bandstop (non-RBJ)
426
        if (!json.contains("cutoff_low_hz") || !json["cutoff_low_hz"].is_number()) {
1✔
UNCOV
427
            throw std::invalid_argument("Missing or invalid 'cutoff_low_hz' field for bandpass/bandstop filter");
×
428
        }
429
        if (!json.contains("cutoff_high_hz") || !json["cutoff_high_hz"].is_number()) {
1✔
UNCOV
430
            throw std::invalid_argument("Missing or invalid 'cutoff_high_hz' field for bandpass/bandstop filter");
×
431
        }
432
        spec.cutoff_low_hz = json["cutoff_low_hz"];
1✔
433
        spec.cutoff_high_hz = json["cutoff_high_hz"];
1✔
434
    }
435
    
436
    // Parse family-specific parameters (Chebyshev only at this point, RBJ handled above)
437
    if (spec.family == FilterFamily::ChebyshevI || spec.family == FilterFamily::ChebyshevII) {
10✔
438
        if (!json.contains("ripple_db") || !json["ripple_db"].is_number()) {
1✔
UNCOV
439
            throw std::invalid_argument("Missing or invalid 'ripple_db' field for Chebyshev filter");
×
440
        }
441
        spec.ripple_db = json["ripple_db"];
1✔
442
    }
443
    
444
    // Validate the specification
445
    auto validation_errors = spec.validate();
10✔
446
    if (!validation_errors.empty()) {
10✔
447
        std::string error_msg = "Invalid filter specification from JSON:\n";
3✔
448
        for (auto const& error : validation_errors) {
2✔
449
            error_msg += "  - " + error + "\n";
1✔
450
        }
451
        throw std::invalid_argument(error_msg);
1✔
452
    }
1✔
453
    
454
    return spec;
18✔
455
}
14✔
456

UNCOV
457
std::string FilterSpecification::getName() const {
×
UNCOV
458
    std::string name;
×
459
    
460
    // Add family
UNCOV
461
    switch (family) {
×
UNCOV
462
        case FilterFamily::Butterworth: name += "Butterworth "; break;
×
UNCOV
463
        case FilterFamily::ChebyshevI: name += "Chebyshev I "; break;
×
UNCOV
464
        case FilterFamily::ChebyshevII: name += "Chebyshev II "; break;
×
UNCOV
465
        case FilterFamily::RBJ: name += "RBJ "; break;
×
466
    }
467
    
468
    // Add response
UNCOV
469
    switch (response) {
×
UNCOV
470
        case FilterResponse::Lowpass: name += "Lowpass"; break;
×
UNCOV
471
        case FilterResponse::Highpass: name += "Highpass"; break;
×
UNCOV
472
        case FilterResponse::Bandpass: name += "Bandpass"; break;
×
UNCOV
473
        case FilterResponse::Bandstop: name += "Bandstop"; break;
×
474
    }
475
    
476
    // Add order (not for RBJ)
UNCOV
477
    if (family != FilterFamily::RBJ) {
×
UNCOV
478
        name += " Order " + std::to_string(order);
×
479
    }
480
    
481
    // Add zero phase indicator
UNCOV
482
    if (zero_phase) {
×
UNCOV
483
        name += " (Zero-Phase)";
×
484
    }
485
    
UNCOV
486
    return name;
×
UNCOV
487
} 
×
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