Skip to content

Commit 3bffa66

Browse files
committed
Add stft
1 parent 715d19e commit 3bffa66

File tree

6 files changed

+397
-15
lines changed

6 files changed

+397
-15
lines changed

src/analysis/cceps.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,26 @@ use crate::{quantities::{List, ListOrSingle, Lists}, util::TruncateIm};
1010
#[derive(Debug, Clone, Copy, PartialEq, Error)]
1111
pub enum CepsError
1212
{
13-
#[error("Sequence has one or more zero-valued fourier coefficient.")]
13+
#[error("Sequence has one or more zero-valued fourier coefficients.")]
1414
ZeroInFourier
1515
}
1616

17+
/// A trait for calculating the complex cepstrum of a sequence or several sequences.
1718
pub trait CCeps<'a, T, C, N>: Lists<T>
1819
where
1920
T: ComplexFloat,
2021
C: List<T>,
2122
N: Maybe<usize>,
2223
{
24+
/// Computes the complex cepstrum of a sequence or several sequences.
25+
///
26+
/// # Arguments
27+
///
28+
/// * `numtaps`: Number of taps if cepstrum is dynamically sized, otherwise inferred from return type.
29+
///
30+
/// # Returns
31+
///
32+
/// * `cepstrum`: Complex cepstrum in the form of a sequence.
2333
fn cceps(&'a self, numtaps: N) -> Result<Self::RowsMapped<C>, CepsError>;
2434
}
2535

src/analysis/cpsd.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use option_trait::{Maybe, NotVoid, StaticMaybe};
33

44
use crate::{quantities::List, util::MaybeLenEq, analysis::{PWelch, PWelchDetrend}};
55

6+
/// A trait for computing the cross power spectral density of two sequences.
67
pub trait CPsd<T, Y, YY, W, WW, WWW, WL, N, S>: List<T> + MaybeLenEq<YY, true>
78
where
89
T: ComplexFloat,
@@ -15,6 +16,25 @@ where
1516
N: Maybe<usize>,
1617
S: Maybe<bool>
1718
{
19+
/// Computes the cross power spectral density of two sequences using Welch's averaged periodogram method.
20+
///
21+
/// # Arguments
22+
///
23+
/// * `y` - The other sequence.
24+
/// * `window` - A window sequence. If none, a [Hamming](crate::windows::Hamming) window with lenght `window_length` will be used.
25+
/// * `window_length` - A window length if no `window` is given.
26+
/// * `overlap` - Overlap length in samples. If none, defaults to `window_length/2`.
27+
/// * `nfft` - Length used for FFT if no `window` is given.
28+
/// * `sampling_frequency` - Sampling frequency. If none, defaults to `1.0`.
29+
/// * `confidence` - Probability in the range `[0.0, 1.0]` for the confidence intervals for the PSD estimate. If none, defaults to `0.95`.
30+
/// * `detrend` - Method of detrending the signals before or after analysis. If none, [LongMean](PWelchDetrend::LongMean) is used.
31+
/// * `sloppy` - If true, `nfft` will be set to the next power of two. Only applicable if no `window` is given.
32+
/// * `shift` - If true, data will be shifted to center-DC.
33+
///
34+
/// # Returns
35+
///
36+
/// * `pxy` - Cross power spectral density.
37+
/// * `frequencies` - Frequencies of the cross power spectral density.
1838
#[doc(alias = "csd")]
1939
fn cpsd<O, FS, CONF, DT, F>(
2040
self,
@@ -114,9 +134,9 @@ mod test
114134

115135
const M: usize = 500;
116136
let w: [_; M] = Triangular.window_gen((), WindowRange::Symmetric);
117-
let nov = 250;
137+
let noverlap = 250;
118138

119-
let (pxy, f): (_, [_; M/2 + 1]) = x.real_cpsd(y, w, (), nov, (), (), (), (), ());
139+
let (pxy, f): (_, [_; M/2 + 1]) = x.real_cpsd(y, w, (), noverlap, (), (), (), (), ());
120140

121141
plot::plot_curves("P_xy(e^jw)", "plots/pxy_z_cpsd.png", [
122142
&f.zip(pxy.map(|p| 10.0*p.norm().log10()))

src/analysis/filternorm.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,21 @@ use crate::{
99
systems::Tf
1010
};
1111

12+
/// A trait for computing the `Lp`-norm or infinity-norm of a digital filter.
1213
pub trait FilterNorm<'a>: System
1314
{
1415
type Output: ListOrSingle<<Self::Set as ComplexFloat>::Real>;
1516

16-
fn filternorm(&'a self, p: <Self::Set as ComplexFloat>::Real) -> Self::Output;
17+
/// Computes the `Lp`-norm or infinity-norm of a digital filter.
18+
///
19+
/// # Arguments
20+
///
21+
/// * `pnorm` - The norm power. If infinite, the infinity-norm of the filter is returned.
22+
///
23+
/// # Returns
24+
///
25+
/// * `norm` - The `Lp`-norm or infinity-norm of the digital filter.
26+
fn filternorm(&'a self, pnorm: <Self::Set as ComplexFloat>::Real) -> Self::Output;
1727
}
1828

1929
const FILTER_INF_NORM_RES: usize = 1024;
@@ -29,15 +39,15 @@ where
2939
{
3040
type Output = B::RowsMapped<T::Real>;
3141

32-
fn filternorm(&'a self, p: T::Real) -> Self::Output
42+
fn filternorm(&'a self, pnorm: T::Real) -> Self::Output
3343
{
34-
if Float::abs(p) > Float::sqrt(<T::Real as Float>::max_value())
44+
if Float::abs(pnorm) > Float::sqrt(<T::Real as Float>::max_value())
3545
{
3646
let (h, _): (_, [_; FILTER_INF_NORM_RES]) = self.freqz((), false);
3747
h.map_rows_to_owned(|h| h.as_view_slice()
3848
.iter()
3949
.map(|&h| h.abs())
40-
.reduce(if p.is_sign_positive() {Float::max} else {Float::min})
50+
.reduce(if pnorm.is_sign_positive() {Float::max} else {Float::min})
4151
.unwrap_or_else(Zero::zero)
4252
)
4353
}
@@ -48,9 +58,9 @@ where
4858
let n = h.map_rows_to_owned(|h| {
4959
let n = Float::powf(h.as_view_slice()
5060
.iter()
51-
.map(|&h| Float::powf(h.abs(), p))
61+
.map(|&h| Float::powf(h.abs(), pnorm))
5262
.sum::<T::Real>(),
53-
Float::recip(p)
63+
Float::recip(pnorm)
5464
);
5565
if Float::is_infinite(n)
5666
{
@@ -60,7 +70,7 @@ where
6070
});
6171
if inf
6272
{
63-
return self.filternorm(Float::copysign(Float::infinity(), p))
73+
return self.filternorm(Float::copysign(Float::infinity(), pnorm))
6474
}
6575
n
6676
}

src/analysis/specgram.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ where
4949
let x: &[T] = self.as_view_slice();
5050
let window: &[W] = window.as_view_slice();
5151
let n = window.len();
52-
let step = n - overlap;
52+
let step = n - overlap % n;
5353

54-
let s: Vec<_> = (0..x.len() - n).step_by(step)
54+
let s: Vec<_> = (0..x.len() + 1 - n).step_by(step)
5555
.map(|i| {
5656
let mut y: Vec<_> = x[i..].iter()
5757
.zip(window.iter())
@@ -65,7 +65,7 @@ where
6565
let fs = sampling_frequency.into_option()
6666
.unwrap_or_else(FloatConst::TAU);
6767

68-
let t = (0..x.len() - n).step_by(step)
68+
let t = (0..x.len() + 1 - n).step_by(step)
6969
.map(|i| <T::Real as NumCast>::from(i).unwrap()/fs)
7070
.collect();
7171
let f = (0..n).map(|i| <T::Real as NumCast>::from(i).unwrap()/NumCast::from(n).unwrap()*fs)
@@ -82,7 +82,7 @@ where
8282
WW: List<W, Length = usize>,
8383
X: List<T, Length = usize>,
8484
Complex<T::Real>: AddAssign + MulAssign,
85-
//[(); 0 - (X::LENGTH - WW::LENGTH) % L]:
85+
[(); WW::LENGTH - (X::LENGTH - WW::LENGTH)/L - 1]:
8686
{
8787
fn specgram<FS>(
8888
&self,
@@ -157,7 +157,8 @@ where
157157
X: List<T, Length = usize>,
158158
[T::Real; W]: List<T::Real, Length = usize>,
159159
Complex<T::Real>: AddAssign + MulAssign,
160-
[(); <[T::Real; W]>::LENGTH]:
160+
[(); <[T::Real; W]>::LENGTH]:,
161+
[(); <[T::Real; W]>::LENGTH - (X::LENGTH - <[T::Real; W]>::LENGTH)/L - 1]:
161162
{
162163
fn specgram<FS>(
163164
&self,

src/transforms/fourier/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ moddef::moddef!(
1919
idwht,
2020
sdft_kw,
2121
sdft,
22+
stft,
2223
swift
2324
}
2425
);

0 commit comments

Comments
 (0)