///|
/// Measured magnitude response of a filter at one frequency.
pub struct FilterResponsePoint {
frequency : Double
input_rms : Double
output_rms : Double
gain : Double
gain_db : Double
}
///|
/// Measure an arbitrary trace transformation over a frequency grid.
pub fn measure_filter_response(
transform : (Trace) -> Trace,
sample_rate : Double,
frequencies : Array[Double],
cycles : Int,
) -> Array[FilterResponsePoint] {
let result = Array::new()
let count = if cycles < 1 { 1 } else { cycles }
for frequency in frequencies {
let length = (sample_rate / frequency * count.to_double()).to_int().max(16)
let input = Trace::new(
"response",
"test",
sample_rate,
0.0,
tapered_sine(length, frequency, sample_rate, 1.0, 0.05),
)
let output = transform(input)
let input_rms = rms_samples(input.data)
let output_rms = rms_samples(output.data)
let gain = if input_rms == 0.0 { 0.0 } else { output_rms / input_rms }
result.push({
frequency,
input_rms,
output_rms,
gain,
gain_db: if gain <= 0.0 {
-300.0
} else {
20.0 * @math.log10(gain)
},
})
}
result
}
///|
/// Measure the existing low-pass implementation on a frequency grid.
pub fn lowpass_response(
sample_rate : Double,
cutoff : Double,
frequencies : Array[Double],
) -> Array[FilterResponsePoint] {
measure_filter_response(
trace => {
lowpass(trace, cutoff, false)
trace
},
sample_rate,
frequencies,
8,
)
}
///|
/// Find the first frequency where measured gain falls below a dB threshold.
pub fn cutoff_from_response(
response : Array[FilterResponsePoint],
threshold_db : Double,
) -> Double? {
for point in response {
if point.gain_db <= threshold_db {
return Some(point.frequency)
}
}
None
}
///|
/// Return the maximum passband ripple in dB.
pub fn passband_ripple(
response : Array[FilterResponsePoint],
low_frequency : Double,
high_frequency : Double,
) -> Double {
let values = Array::new()
for point in response {
if point.frequency >= low_frequency && point.frequency <= high_frequency {
values.push(point.gain_db)
}
}
if values.is_empty() {
0.0
} else {
maximum_sample(values) - minimum_sample(values)
}
}