Moved apsmode and apssettings to their own files
This commit is contained in:
parent
08ecdf6dc4
commit
826266b8ee
@ -11,7 +11,7 @@ cfg_if::cfg_if! {
|
|||||||
/// Ratio between circumference and diameter of a circle
|
/// Ratio between circumference and diameter of a circle
|
||||||
pub const pi: Flt = std::f64::consts::PI;
|
pub const pi: Flt = std::f64::consts::PI;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if #[cfg(feature="f32")] {
|
else if #[cfg(feature="f32")] {
|
||||||
/// Floating-point value, compile time option to make it either f32, or f64
|
/// Floating-point value, compile time option to make it either f32, or f64
|
||||||
@ -26,7 +26,8 @@ cfg_if::cfg_if! {
|
|||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(feature = "python-bindings")] {
|
if #[cfg(feature = "python-bindings")] {
|
||||||
pub use numpy::{IntoPyArray,PyArray, PyArray1, PyArrayDyn, PyArrayLike1, PyReadonlyArrayDyn};
|
pub use numpy::{IntoPyArray,PyArray, PyArray1, PyArray2, PyArray3, PyArrayDyn, PyArrayLike1,
|
||||||
|
PyArrayLike2,PyArrayLike3,PyReadonlyArrayDyn, convert::ToPyArray};
|
||||||
pub use pyo3::prelude::*;
|
pub use pyo3::prelude::*;
|
||||||
pub use pyo3::exceptions::PyValueError;
|
pub use pyo3::exceptions::PyValueError;
|
||||||
pub use pyo3::{pymodule, types::PyModule, PyResult};
|
pub use pyo3::{pymodule, types::PyModule, PyResult};
|
||||||
|
@ -64,6 +64,9 @@ fn lasprs(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
m.add_class::<slm::SLM>()?;
|
m.add_class::<slm::SLM>()?;
|
||||||
m.add_class::<ps::WindowType>()?;
|
m.add_class::<ps::WindowType>()?;
|
||||||
m.add_class::<ps::Overlap>()?;
|
m.add_class::<ps::Overlap>()?;
|
||||||
|
m.add_class::<ps::ApsMode>()?;
|
||||||
|
m.add_class::<ps::ApsSettings>()?;
|
||||||
|
m.add_class::<ps::AvPowerSpectra>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
209
src/ps/aps.rs
209
src/ps/aps.rs
@ -1,198 +1,25 @@
|
|||||||
use super::timebuffer::TimeBuffer;
|
|
||||||
use super::CrossPowerSpecra;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use super::{timebuffer::TimeBuffer, CrossPowerSpecra};
|
||||||
use crate::{config::*, TransferFunction, ZPKModel};
|
use crate::{config::*, TransferFunction, ZPKModel};
|
||||||
use anyhow::{bail, Error, Result};
|
use anyhow::{bail, Error, Result};
|
||||||
use derive_builder::Builder;
|
|
||||||
use freqweighting::FreqWeighting;
|
use freqweighting::FreqWeighting;
|
||||||
|
|
||||||
/// All settings used for computing averaged power spectra using Welch' method.
|
|
||||||
#[derive(Builder, Clone)]
|
|
||||||
#[builder(build_fn(validate = "Self::validate", error = "Error"))]
|
|
||||||
pub struct ApsSettings {
|
|
||||||
/// Mode of computation, see [ApsMode].
|
|
||||||
#[builder(default)]
|
|
||||||
mode: ApsMode,
|
|
||||||
/// Overlap in time segments. See [Overlap].
|
|
||||||
#[builder(default)]
|
|
||||||
overlap: Overlap,
|
|
||||||
/// Window applied to time segments. See [WindowType].
|
|
||||||
#[builder(default)]
|
|
||||||
windowType: WindowType,
|
|
||||||
/// Kind of freqency weighting. Defaults to Z
|
|
||||||
#[builder(default)]
|
|
||||||
freqWeightingType: FreqWeighting,
|
|
||||||
/// FFT Length
|
|
||||||
nfft: usize,
|
|
||||||
/// Sampling frequency
|
|
||||||
fs: Flt,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApsSettingsBuilder {
|
|
||||||
fn validate(&self) -> Result<()> {
|
|
||||||
if !self.fs.is_some() {
|
|
||||||
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.")
|
|
||||||
}
|
|
||||||
// Perform some checks on ApsMode
|
|
||||||
if let Some(ApsMode::ExponentialWeighting { tau }) = self.mode {
|
|
||||||
if tau <= 0.0 {
|
|
||||||
bail!("Invalid time weighting constant [s]. Should be > 0 if given.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl ApsSettings {
|
|
||||||
/// Returns nfft
|
|
||||||
pub fn nfft(&self) -> usize {
|
|
||||||
self.nfft
|
|
||||||
}
|
|
||||||
fn get_overlap_keep(&self) -> usize {
|
|
||||||
self.validate_get_overlap_keep().unwrap()
|
|
||||||
}
|
|
||||||
/// Returns the amount of samples to `keep` in the time buffer when
|
|
||||||
/// overlapping time segments using [TimeBuffer].
|
|
||||||
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}.")
|
|
||||||
}
|
|
||||||
// Keep 1 sample, if overlap is 1 sample etc.
|
|
||||||
Overlap::Number { N } if N < nfft => N,
|
|
||||||
|
|
||||||
// If overlap percentage is >= 100, or < 0.0 its an error
|
|
||||||
Overlap::Percentage { pct } if !(0.0..100.).contains(&pct) => {
|
|
||||||
bail!("Invalid overlap percentage. Should be >= 0. And < 100.")
|
|
||||||
}
|
|
||||||
// If overlap percentage is 0, this gives
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a reasonable acoustic default with a frequency resolution around
|
|
||||||
/// ~ 10 Hz, where nfft is still an integer power of 2.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// If `fs` is something odd, i.e. < 1 kHz, or higher than 1 MHz.
|
|
||||||
///
|
|
||||||
pub fn reasonableAcousticDefault(fs: Flt, mode: ApsMode) -> Result<ApsSettings> {
|
|
||||||
if fs < 1e3 || fs > 1e6 {
|
|
||||||
bail!("Sampling frequency for reasonable acoustic data is >= 1 kHz and <= 1 MHz.");
|
|
||||||
}
|
|
||||||
let fs_div_10_rounded = (fs / 10.) as u32;
|
|
||||||
|
|
||||||
// 2^30 is about 1 million. We search for a two-power of an nfft that is
|
|
||||||
// the closest to fs/10. The frequency resolution is about fs/nfft.
|
|
||||||
let nfft = (0..30).map(|i| 2u32.pow(i) - fs_div_10_rounded).fold(
|
|
||||||
// Start wth a value that is always too large
|
|
||||||
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(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return sampling frequency
|
|
||||||
pub fn fs(&self) -> Flt {
|
|
||||||
self.fs
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return Nyquist frequency
|
|
||||||
pub fn fnyq(&self) -> Flt {
|
|
||||||
self.fs() / 2.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a single-sided frequency array corresponding to points in Power
|
|
||||||
/// spectra computation.
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// The 'mode' used in computing averaged power spectra. When providing data in
|
|
||||||
/// blocks to the [AvPowerSpectra] the resulting 'current estimate' responds
|
|
||||||
/// differently, depending on the model.
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "python-bindings", pyclass)]
|
|
||||||
pub enum ApsMode {
|
|
||||||
/// Averaged over all data provided. New averages can be created by calling
|
|
||||||
/// `AvPowerSpectra::reset()`
|
|
||||||
AllAveraging {},
|
|
||||||
/// In this mode, the `AvPowerSpectra` works a bit like a sound level meter,
|
|
||||||
/// where new data is weighted with old data, and old data exponentially
|
|
||||||
/// backs off. This mode only makes sense when `tau >> nfft/fs`
|
|
||||||
ExponentialWeighting {
|
|
||||||
/// Time weighting constant, follows convention of Sound Level Meters.
|
|
||||||
/// Means the data is approximately low-pass filtered with a cut-off
|
|
||||||
/// frequency f_c of s/tau ≅ 1 → f_c = (2 * pi * tau)^-1.
|
|
||||||
tau: Flt,
|
|
||||||
},
|
|
||||||
/// Spectrogram mode. Only returns the latest estimate(s).
|
|
||||||
Spectrogram {},
|
|
||||||
}
|
|
||||||
impl Default for ApsMode {
|
|
||||||
fn default() -> Self {
|
|
||||||
ApsMode::AllAveraging {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "python-bindings")]
|
|
||||||
#[cfg_attr(feature = "python-bindings", pymethods)]
|
|
||||||
impl ApsMode {
|
|
||||||
#[inline]
|
|
||||||
fn __eq__(&self, other: &Self) -> bool {
|
|
||||||
self == other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Averaged power spectra computing engine
|
/// Averaged power spectra computing engine
|
||||||
/// Used to compute power spectra estimations on
|
/// Used to compute power spectra estimations on
|
||||||
/// long datasets, where nfft << length of data. This way, the variance of a
|
/// long datasets, where nfft << length of data. This way, the variance of a
|
||||||
/// single periodogram is suppressed with increasing number of averages.
|
/// single periodogram is suppressed with increasing number of averages.
|
||||||
///
|
///
|
||||||
/// For more information, see the book on numerical recipes.
|
/// For more information, see the book on numerical recipes.
|
||||||
///
|
|
||||||
|
#[cfg(feature = "python-bindings")]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
|
||||||
pub struct AvPowerSpectra {
|
pub struct AvPowerSpectra {
|
||||||
// Power spectra estimator for single block
|
// Power spectra estimator for single block
|
||||||
ps: PowerSpectra,
|
ps: PowerSpectra,
|
||||||
|
|
||||||
|
// Settings for computing power spectra, see [ApsSettings]
|
||||||
settings: ApsSettings,
|
settings: ApsSettings,
|
||||||
|
|
||||||
// The number of samples to keep in the time buffer when overlapping time
|
// The number of samples to keep in the time buffer when overlapping time
|
||||||
@ -454,6 +281,28 @@ impl AvPowerSpectra {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python-bindings")]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||||
|
impl AvPowerSpectra {
|
||||||
|
#[new]
|
||||||
|
fn new_py(s: ApsSettings) -> AvPowerSpectra {
|
||||||
|
AvPowerSpectra::new(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyo3(name = "compute")]
|
||||||
|
fn compute_py<'py>(
|
||||||
|
&mut self,
|
||||||
|
py: Python<'py>,
|
||||||
|
dat: PyArrayLike2<Flt>,
|
||||||
|
) -> Bound<'py, PyArray3<Cflt>> {
|
||||||
|
let dat = dat.as_array();
|
||||||
|
if let Some(res) = self.compute_last(dat) {
|
||||||
|
let res = res.clone();
|
||||||
|
return res.to_pyarray_bound(py);
|
||||||
|
}
|
||||||
|
panic!("No data!");
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use approx::assert_abs_diff_eq;
|
use approx::assert_abs_diff_eq;
|
||||||
|
35
src/ps/apsmode.rs
Normal file
35
src/ps/apsmode.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crate::config::*;
|
||||||
|
/// The 'mode' used in computing averaged power spectra. When providing data in
|
||||||
|
/// blocks to the [AvPowerSpectra] the resulting 'current estimate' responds
|
||||||
|
/// differently, depending on the model.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
|
pub enum ApsMode {
|
||||||
|
/// Averaged over all data provided. New averages can be created by calling
|
||||||
|
/// `AvPowerSpectra::reset()`
|
||||||
|
AllAveraging {},
|
||||||
|
/// In this mode, the `AvPowerSpectra` works a bit like a sound level meter,
|
||||||
|
/// where new data is weighted with old data, and old data exponentially
|
||||||
|
/// backs off. This mode only makes sense when `tau >> nfft/fs`
|
||||||
|
ExponentialWeighting {
|
||||||
|
/// Time weighting constant, follows convention of Sound Level Meters.
|
||||||
|
/// Means the data is approximately low-pass filtered with a cut-off
|
||||||
|
/// frequency f_c of s/tau ≅ 1 → f_c = (2 * pi * tau)^-1.
|
||||||
|
tau: Flt,
|
||||||
|
},
|
||||||
|
/// Spectrogram mode. Only returns the latest estimate(s).
|
||||||
|
Spectrogram {},
|
||||||
|
}
|
||||||
|
impl Default for ApsMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
ApsMode::AllAveraging {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "python-bindings")]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pymethods)]
|
||||||
|
impl ApsMode {
|
||||||
|
#[inline]
|
||||||
|
fn __eq__(&self, other: &Self) -> bool {
|
||||||
|
self == other
|
||||||
|
}
|
||||||
|
}
|
168
src/ps/apssettings.rs
Normal file
168
src/ps/apssettings.rs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::config::*;
|
||||||
|
use anyhow::{bail, Error, Result};
|
||||||
|
use derive_builder::Builder;
|
||||||
|
|
||||||
|
/// All settings used for computing averaged power spectra using Welch' method.
|
||||||
|
#[derive(Builder, Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "python-bindings", pyclass)]
|
||||||
|
#[builder(build_fn(validate = "Self::validate", error = "Error"))]
|
||||||
|
pub struct ApsSettings {
|
||||||
|
/// Mode of computation, see [ApsMode].
|
||||||
|
#[builder(default)]
|
||||||
|
pub mode: ApsMode,
|
||||||
|
/// Overlap in time segments. See [Overlap].
|
||||||
|
#[builder(default)]
|
||||||
|
pub overlap: Overlap,
|
||||||
|
/// Window applied to time segments. See [WindowType].
|
||||||
|
#[builder(default)]
|
||||||
|
pub windowType: WindowType,
|
||||||
|
/// Kind of freqency weighting. Defaults to Z
|
||||||
|
#[builder(default)]
|
||||||
|
pub freqWeightingType: FreqWeighting,
|
||||||
|
/// FFT Length
|
||||||
|
pub nfft: usize,
|
||||||
|
/// Sampling frequency
|
||||||
|
pub fs: Flt,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApsSettingsBuilder {
|
||||||
|
fn validate(&self) -> Result<()> {
|
||||||
|
if !self.fs.is_some() {
|
||||||
|
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.")
|
||||||
|
}
|
||||||
|
// Perform some checks on ApsMode
|
||||||
|
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 {
|
||||||
|
/// Returns the amount of samples to keep in overlapping blocks of power
|
||||||
|
/// spectra.
|
||||||
|
pub fn get_overlap_keep(&self) -> usize {
|
||||||
|
self.validate_get_overlap_keep().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of samples to `keep` in the time buffer when
|
||||||
|
/// overlapping time segments using [TimeBuffer].
|
||||||
|
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}.")
|
||||||
|
}
|
||||||
|
// Keep 1 sample, if overlap is 1 sample etc.
|
||||||
|
Overlap::Number { N } if N < nfft => N,
|
||||||
|
|
||||||
|
// If overlap percentage is >= 100, or < 0.0 its an error
|
||||||
|
Overlap::Percentage { pct } if !(0.0..100.).contains(&pct) => {
|
||||||
|
bail!("Invalid overlap percentage. Should be >= 0. And < 100.")
|
||||||
|
}
|
||||||
|
// If overlap percentage is 0, this gives
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a reasonable acoustic default with a frequency resolution around
|
||||||
|
/// ~ 10 Hz, where nfft is still an integer power of 2.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If `fs` is something odd, i.e. < 1 kHz, or higher than 1 MHz.
|
||||||
|
///
|
||||||
|
pub fn reasonableAcousticDefault(fs: Flt, mode: ApsMode) -> Result<ApsSettings> {
|
||||||
|
if fs < 1e3 || fs > 1e6 {
|
||||||
|
bail!("Sampling frequency for reasonable acoustic data is >= 1 kHz and <= 1 MHz.");
|
||||||
|
}
|
||||||
|
let fs_div_10_rounded = (fs / 10.) as u32;
|
||||||
|
|
||||||
|
// 2^30 is about 1 million. We search for a two-power of an nfft that is
|
||||||
|
// the closest to fs/10. The frequency resolution is about fs/nfft.
|
||||||
|
let nfft = (0..30).map(|i| 2u32.pow(i) - fs_div_10_rounded).fold(
|
||||||
|
// Start wth a value that is always too large
|
||||||
|
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(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return sampling frequency
|
||||||
|
pub fn fs(&self) -> Flt {
|
||||||
|
self.fs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return Nyquist frequency
|
||||||
|
pub fn fnyq(&self) -> Flt {
|
||||||
|
self.fs / 2.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a single-sided frequency array corresponding to points in Power
|
||||||
|
/// spectra computation.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,11 @@ pub struct FFT {
|
|||||||
// nfft stored as float, this is how it is required most often
|
// nfft stored as float, this is how it is required most often
|
||||||
nfftF: Flt,
|
nfftF: Flt,
|
||||||
}
|
}
|
||||||
|
impl Debug for FFT {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Forward FFT engine, lenfth: {self.nfftF}").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FFT {
|
impl FFT {
|
||||||
/// Create new FFT from given nfft
|
/// Create new FFT from given nfft
|
||||||
|
@ -25,6 +25,9 @@ impl FreqWeighting {
|
|||||||
fn all() -> Vec<Self> {
|
fn all() -> Vec<Self> {
|
||||||
Self::iter().collect()
|
Self::iter().collect()
|
||||||
}
|
}
|
||||||
|
fn __str__(&self) -> String {
|
||||||
|
format!("{self}-weighting")
|
||||||
|
}
|
||||||
#[staticmethod]
|
#[staticmethod]
|
||||||
#[pyo3(name = "default")]
|
#[pyo3(name = "default")]
|
||||||
fn default_py() -> Self {
|
fn default_py() -> Self {
|
||||||
|
@ -10,12 +10,16 @@ mod ps;
|
|||||||
mod timebuffer;
|
mod timebuffer;
|
||||||
mod window;
|
mod window;
|
||||||
mod freqweighting;
|
mod freqweighting;
|
||||||
|
mod apssettings;
|
||||||
mod overlap;
|
mod overlap;
|
||||||
|
mod apsmode;
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
|
|
||||||
|
|
||||||
pub use freqweighting::FreqWeighting;
|
pub use freqweighting::FreqWeighting;
|
||||||
pub use overlap::Overlap;
|
pub use overlap::Overlap;
|
||||||
pub use aps::{ApsSettings, ApsSettingsBuilder,ApsMode, AvPowerSpectra};
|
pub use apssettings::{ApsSettings, ApsSettingsBuilder};
|
||||||
|
pub use apsmode::ApsMode;
|
||||||
|
pub use aps::AvPowerSpectra;
|
||||||
pub use ps::{CrossPowerSpecra, PowerSpectra, CPSResult};
|
pub use ps::{CrossPowerSpecra, PowerSpectra, CPSResult};
|
||||||
pub use window::{Window, WindowType};
|
pub use window::{Window, WindowType};
|
||||||
|
@ -85,6 +85,7 @@ impl CrossPowerSpecra for CPSResult {
|
|||||||
/// example the computations of spectrograms, or Welch' method of spectral
|
/// example the computations of spectrograms, or Welch' method of spectral
|
||||||
/// estimation.
|
/// estimation.
|
||||||
///
|
///
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct PowerSpectra {
|
pub struct PowerSpectra {
|
||||||
/// Window used in estimator. The actual Window in here is normalized with
|
/// Window used in estimator. The actual Window in here is normalized with
|
||||||
/// the square root of the Window power. This safes one division when
|
/// the square root of the Window power. This safes one division when
|
||||||
|
@ -8,7 +8,7 @@ use std::collections::VecDeque;
|
|||||||
/// TimeBuffer, storage to add blocks of data in a ring buffer, that can be
|
/// TimeBuffer, storage to add blocks of data in a ring buffer, that can be
|
||||||
/// extracted by blocks of other size. Also, we can keep samples in a buffer to
|
/// extracted by blocks of other size. Also, we can keep samples in a buffer to
|
||||||
/// create, for example, overlapping windows of time data.
|
/// create, for example, overlapping windows of time data.
|
||||||
#[derive(Default)]
|
#[derive(Default, Debug)]
|
||||||
pub struct TimeBuffer {
|
pub struct TimeBuffer {
|
||||||
data: Vec<VecDeque<Flt>>,
|
data: Vec<VecDeque<Flt>>,
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ impl WindowType {
|
|||||||
|
|
||||||
|
|
||||||
/// Window (taper) computed from specified window type.
|
/// Window (taper) computed from specified window type.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
/// The enum from which it is generated
|
/// The enum from which it is generated
|
||||||
pub w: WindowType,
|
pub w: WindowType,
|
||||||
|
Loading…
Reference in New Issue
Block a user