///|
pub fn signal_energy(signal : ArrayView[Double]) -> Double raise SpectrumError {
if signal.length() == 0 {
raise EmptySignal
}
let mut total = 0.0
for value in signal {
total = total + value * value
}
total
}
///|
pub fn zero_crossing_rate(
signal : ArrayView[Double],
) -> Double raise SpectrumError {
if signal.length() < 2 {
raise InvalidArgument(message="zero crossing rate needs two samples")
}
let mut crossings = 0
for i in 1.. Array[Double] raise SpectrumError {
if signal.length() == 0 {
raise EmptySignal
}
if sample_interval <= 0.0 {
raise InvalidArgument(message="sample_interval must be positive")
}
if signal.length() == 1 {
return [0.0]
}
let out = Array::make(signal.length(), 0.0)
for i in 0..<(signal.length() - 1) {
out[i] = (signal[i + 1] - signal[i]) / sample_interval
}
let last = signal.length() - 1
out[last] = (signal[last] - signal[last - 1]) / sample_interval
out
}
///|
pub fn integrate_trapezoid(
signal : ArrayView[Double],
sample_interval : Double,
) -> Double raise SpectrumError {
if signal.length() == 0 {
raise EmptySignal
}
if sample_interval <= 0.0 {
raise InvalidArgument(message="sample_interval must be positive")
}
if signal.length() == 1 {
return 0.0
}
let mut area = 0.0
for i in 1.. Double raise SpectrumError {
if signal.length() == 0 {
raise EmptySignal
}
if probability < 0.0 || probability > 1.0 {
raise InvalidArgument(message="probability must be in [0, 1]")
}
let sorted = insertion_sorted(signal)
if sorted.length() == 1 {
return sorted[0]
}
let position = probability * Double::from_int(sorted.length() - 1)
let lower = position.floor().to_int()
let upper = if lower + 1 < sorted.length() { lower + 1 } else { lower }
let fraction = position - Double::from_int(lower)
sorted[lower] * (1.0 - fraction) + sorted[upper] * fraction
}
///|
pub fn median(signal : ArrayView[Double]) -> Double raise SpectrumError {
quantile(signal, 0.5)
}
///|
pub fn moving_rms(
signal : ArrayView[Double],
width : Int,
) -> Array[Double] raise SpectrumError {
if signal.length() == 0 {
raise EmptySignal
}
if width <= 0 {
raise InvalidArgument(message="width must be positive")
}
let out = Array::make(signal.length(), 0.0)
let mut sum = 0.0
for i in 0..= width {
sum = sum - signal[i - width] * signal[i - width]
}
let count = if i + 1 < width { i + 1 } else { width }
out[i] = (sum / Double::from_int(count)).sqrt()
}
out
}
///|
fn insertion_sorted(signal : ArrayView[Double]) -> Array[Double] {
let sorted : Array[Double] = []
for value in signal {
let mut index = 0
while index < sorted.length() && sorted[index] <= value {
index = index + 1
}
sorted.insert(index, value)
}
sorted
}