use super::*;
use crate::config::*;
use anyhow::{bail, Error, Result};
use derive_builder::Builder;
#[derive(Builder, Clone, Debug)]
#[cfg_attr(feature = "python-bindings", pyclass)]
#[builder(build_fn(validate = "Self::validate", error = "Error"))]
pub struct ApsSettings {
#[builder(default)]
pub mode: ApsMode,
#[builder(default)]
pub overlap: Overlap,
#[builder(default)]
pub windowType: WindowType,
#[builder(default)]
pub freqWeightingType: FreqWeighting,
pub nfft: usize,
pub fs: Flt,
}
impl ApsSettingsBuilder {
fn validate(&self) -> Result<()> {
if self.fs.is_none() {
bail!("Sampling frequency not given");
}
let fs = self.fs.unwrap();
if !fs.is_normal() {
bail!("Sampling frequency not a normal number")
}
if fs <= 0.0 {
bail!("Invalid sampling frequency given as parameter");
}
if self.nfft.is_none() {
bail!("nfft not specified")
};
let nfft = self.nfft.unwrap();
if nfft % 2 != 0 {
bail!("NFFT should be even")
}
if nfft == 0 {
bail!("Invalid NFFT, should be > 0.")
}
if let Some(ApsMode::ExponentialWeighting { tau }) = self.mode {
if tau <= 0.0 {
bail!("Invalid time weighting constant [s]. Should be > 0 if given.");
}
}
Ok(())
}
}
#[cfg(feature = "python-bindings")]
#[cfg_attr(feature = "python-bindings", pymethods)]
impl ApsSettings {
#[new]
fn new(
mode: ApsMode,
overlap: Overlap,
windowType: WindowType,
freqWeightingType: FreqWeighting,
nfft: usize,
fs: Flt,
) -> ApsSettings {
ApsSettings {
mode,
overlap,
windowType,
freqWeightingType,
nfft,
fs,
}
}
}
impl ApsSettings {
pub fn get_overlap_keep(&self) -> usize {
self.validate_get_overlap_keep().unwrap()
}
fn validate_get_overlap_keep(&self) -> Result<usize> {
let nfft = self.nfft;
let overlap_keep = match self.overlap {
Overlap::Number { N } if N >= nfft => {
bail!("Invalid overlap number of samples. Should be < nfft, which is {nfft}.")
}
Overlap::Number { N } if N < nfft => N,
Overlap::Percentage { pct } if !(0.0..100.).contains(&pct) => {
bail!("Invalid overlap percentage. Should be >= 0. And < 100.")
}
Overlap::Percentage { pct } => ((pct * nfft as Flt) / 100.) as usize,
Overlap::NoOverlap {} => 0,
_ => unreachable!(),
};
if overlap_keep >= nfft {
bail!("Computed overlap results in invalid number of overlap samples. Please make sure the FFT length is large enough, when high overlap percentages are required.");
}
Ok(overlap_keep)
}
pub fn reasonableAcousticDefault(fs: Flt, mode: ApsMode) -> Result<ApsSettings> {
if !(1e3..=1e6).contains(&fs) {
bail!("Sampling frequency for reasonable acoustic data is >= 1 kHz and <= 1 MHz.");
}
let fs_div_10_rounded = (fs / 10.) as u32;
let nfft = (0..30).map(|i| 2u32.pow(i) - fs_div_10_rounded).fold(
fs as u32 * 10,
|cur, new| cur.min(new),
) as usize;
Ok(ApsSettings {
mode,
fs,
nfft,
windowType: WindowType::default(),
overlap: Overlap::default(),
freqWeightingType: FreqWeighting::default(),
})
}
pub fn fs(&self) -> Flt {
self.fs
}
pub fn fnyq(&self) -> Flt {
self.fs / 2.
}
pub fn getFreq(&self) -> Array1<Flt> {
let df = self.fs / self.nfft as Flt;
let K = self.nfft / 2 + 1;
Array1::linspace(0., (K - 1) as Flt * df, K)
}
}