spectra#

Estimation of power spectra and related tools.

Inheritance diagram of acoular.spectra

BaseSpectra

Base class for handling spectral data in Acoular.

PowerSpectra

Provides the cross-spectral matrix of multichannel time-domain data and its eigen-decomposition.

PowerSpectraImport

Provides a dummy class for using pre-calculated CSMs.

class acoular.spectra.BaseSpectra#

Bases: ABCHasStrictTraits

Base class for handling spectral data in Acoular.

This class defines the basic structure and functionality for computing and managing spectral data derived from time-domain signals. It includes properties for configuring the Fast Fourier Transformation (FFT), including overlap, and other parameters critical for spectral analysis.

source = Instance(SamplesGenerator)#

Data source; an instance of SamplesGenerator or derived object.

sample_freq = Delegate('source')#

Sampling frequency of the output signal, delegated from source.

num_channels = Delegate('source')#

Number of time microphones, delegated from source.

window = Map( #

Window function applied during FFT. Can be one of:

  • 'Rectangular' (default)

  • 'Hanning'

  • 'Hamming'

  • 'Bartlett'

  • 'Blackman'

overlap = Map({'None': 1, '50%': 2, '75%': 4, '87.5%': 8}, default_value='None', desc='overlap of FFT blocks')#

Overlap factor for FFT block averaging. One of:

  • 'None' (default)

  • '50%'

  • '75%'

  • '87.5%'

block_size = Enum( #

FFT block size. Must be one of: 128, 256, 512, 1024, … 65536. Default is 1024.

precision = Enum('complex128', 'complex64', desc='precision of the fft')#

Precision of the FFT, corresponding to NumPy dtypes. Default is 'complex128'.

digest = Property(depends_on=['precision', 'block_size', 'window', 'overlap'])#

A unique identifier for the spectra, based on its properties. (read-only)

fftfreq()#

Compute and return the Discrete Fourier Transform sample frequencies.

This method generates the frequency values corresponding to the FFT bins for the configured block_size and sampling frequency from the data source.

Returns:
numpy.ndarray or None

Array of shape ( block_size / 2 + 1,) containing the sample frequencies. If source is not set, returns None.

Examples

Using normally distributed data for time samples as in TimeSamples.

>>> import numpy as np
>>> from acoular import TimeSamples
>>> from acoular.spectra import PowerSpectra
>>>
>>> data = np.random.rand(1000, 4)
>>> ts = TimeSamples(data=data, sample_freq=51200)
>>> print(ts.num_channels, ts.num_samples, ts.sample_freq)
4 1000 51200.0
>>> ps = PowerSpectra(source=ts, block_size=128, window='Blackman')
>>> ps.fftfreq()
array([    0.,   400.,   800.,  1200.,  1600.,  2000.,  2400.,  2800.,
        3200.,  3600.,  4000.,  4400.,  4800.,  5200.,  5600.,  6000.,
        6400.,  6800.,  7200.,  7600.,  8000.,  8400.,  8800.,  9200.,
        9600., 10000., 10400., 10800., 11200., 11600., 12000., 12400.,
       12800., 13200., 13600., 14000., 14400., 14800., 15200., 15600.,
       16000., 16400., 16800., 17200., 17600., 18000., 18400., 18800.,
       19200., 19600., 20000., 20400., 20800., 21200., 21600., 22000.,
       22400., 22800., 23200., 23600., 24000., 24400., 24800., 25200.,
       25600.])
class acoular.spectra.PowerSpectra#

Bases: BaseSpectra

Provides the cross-spectral matrix of multichannel time-domain data and its eigen-decomposition.

This class is designed to compute the cross-spectral matrix (CSM) efficiently using the Welch method [18] with support for windowing and overlapping data segments. It also calculates the eigenvalues and eigenvectors of the CSM, allowing for spectral analysis and advanced signal processing tasks.

Key features:
  • Efficient Calculation: Computes the CSM using FFT-based methods.

  • Caching: Results can be cached in HDF5 files to avoid redundant calculations for identical inputs and parameters.

  • Lazy Evaluation: Calculations are triggered only when attributes like csm, eva, or eve are accessed.

  • Dynamic Input Handling: Automatically recomputes results when the input data or parameters change.

source = Instance(SamplesGenerator)#

The data source for the time-domain samples. It must be an instance of SamplesGenerator or a derived class.

ind_low = Property(_ind_low, desc='index of lowest frequency line')#

Index of lowest frequency line to compute. Default is 1. Only used by objects that fetch the CSM. PowerSpectra computes every frequency line.

ind_high = Property(_ind_high, desc='index of lowest frequency line')#

Index of highest frequency line to compute. Default is -1 (last possible line for default block_size).

cached = Bool(True, desc='cached flag')#

A flag indicating whether the result should be cached in HDF5 files. Default is True.

num_blocks = Property(desc='overall number of FFT blocks')#

The number of FFT blocks used for averaging. This is derived from the block_size and overlap parameters. (read-only)

freq_range = Property(desc='frequency range')#

2-element array with the lowest and highest frequency. If the higher frequency is larger than the max frequency, the max frequency will be the upper bound.

indices = Property(desc='index range')#

The sequence of frequency indices between ind_low and ind_high. (read-only)

basename = Property(depends_on=['source.digest'], desc='basename for cache file')#

The name of the cache file (without the file extension) used for storing results. (read-only)

csm = Property(desc='cross spectral matrix')#

The cross-spectral matrix, represented as an array of shape (n, m, m) of complex values for n frequencies and m channels as in num_channels. (read-only)

eva = Property(desc='eigenvalues of cross spectral matrix')#

The eigenvalues of the CSM, stored as an array of shape (n,) of floats for n frequencies. (read-only)

eve = Property(desc='eigenvectors of cross spectral matrix')#

The eigenvectors of the cross spectral matrix, stored as an array of shape (n, m, m) of floats for n frequencies and m channels as in num_channels. (read-only)

digest = Property( #

A unique identifier for the spectra, based on its properties. (read-only)

h5f = Instance(H5CacheFileBase, transient=True)#

The HDF5 cache file used for storing the results if cached is set to True.

calc_csm()#

Calculate the CSM for the given source data.

This method computes the CSM by performing a block-wise Fast Fourier Transform (FFT) on the source data, applying a window function, and averaging the results. Only the upper triangular part of the matrix is computed for efficiency, and the lower triangular part is constructed via transposition and complex conjugation.

Returns:
numpy.ndarray

The computed cross spectral matrix as an array of shape (n, m, m) of complex values for n frequencies and m channels as in num_channels.

Examples

>>> import numpy as np
>>> from acoular import TimeSamples
>>> from acoular.spectra import PowerSpectra
>>>
>>> data = np.random.rand(1000, 4)
>>> ts = TimeSamples(data=data, sample_freq=51200)
>>> print(ts.num_channels, ts.num_samples, ts.sample_freq)
4 1000 51200.0
>>> ps = PowerSpectra(source=ts, block_size=128, window='Blackman')
>>> ps.csm.shape
(65, 4, 4)
calc_ev()#

Calculate eigenvalues and eigenvectors of the CSM for each frequency.

The eigenvalues represent the spectral power, and the eigenvectors correspond to the principal components of the matrix. This calculation is performed for all frequency slices of the CSM.

Returns:
tuple of numpy.ndarray
A tuple containing:
  • eva (numpy.ndarray): Eigenvalues as a 2D array of shape (n, m), where n is the number of frequencies and m is the number of channels. The datatype depends on the precision.

  • eve (numpy.ndarray): Eigenvectors as a 3D array of shape (n, m, m). The datatype is consistent with the precision of the input data.

Notes

  • The precision of the eigenvalues is determined by precision ('float64' for complex128 precision and 'float32' for complex64 precision).

  • This method assumes the CSM is already computed and accessible via csm.

Examples

>>> import numpy as np
>>> from acoular import TimeSamples
>>> from acoular.spectra import PowerSpectra
>>>
>>> data = np.random.rand(1000, 4)
>>> ts = TimeSamples(data=data, sample_freq=51200)
>>> ps = PowerSpectra(source=ts, block_size=128, window='Hanning')
>>> eva, eve = ps.calc_ev()
>>> print(eva.shape, eve.shape)
(65, 4) (65, 4, 4)
calc_eva()#

Calculate eigenvalues of the CSM.

This method computes and returns the eigenvalues of the CSM for all frequency slices.

Returns:
numpy.ndarray

A 2D array of shape (n, m) containing the eigenvalues for n frequencies and m channels. The datatype depends on precision ('float64' for complex128 precision and 'float32' for complex64 precision).

Notes

This method internally calls calc_ev() and extracts only the eigenvalues.

calc_eve()#

Calculate eigenvectors of the Cross Spectral Matrix (CSM).

This method computes and returns the eigenvectors of the CSM for all frequency slices.

Returns:
numpy.ndarray

A 3D array of shape (n, m, m) containing the eigenvectors for n frequencies and m channels. Each slice eve[f] represents an (m, m) matrix of eigenvectors for frequency f. The datatype matches the precision of the CSM (complex128 or complex64).

Notes

This method internally calls calc_ev() and extracts only the eigenvectors.

synthetic_ev(freq, num=0)#

Retrieve synthetic eigenvalues for a specified frequency or frequency range.

This method calculates the eigenvalues of the CSM for a single frequency or a synthetic frequency range. If num is set to 0, it retrieves the eigenvalues at the exact frequency. Otherwise, it averages eigenvalues across a range determined by freq and num.

Parameters:
freqfloat

The target frequency for which the eigenvalues are calculated. This is the center frequency for synthetic averaging.

numint, optional

The number of subdivisions in the logarithmic frequency space around the center frequency freq.

  • 0 (default): Only the eigenvalues for the exact frequency line are returned.

  • Non-zero:

num

frequency band width

0

single frequency line

1

octave band

3

third-octave band

n

1/n-octave band

Returns:
numpy.ndarray

An array of eigenvalues. If num == 0, the eigenvalues for the single frequency are returned. For num > 0, a summed array of eigenvalues across the synthetic frequency range is returned.

Examples

>>> import numpy as np
>>> from acoular import TimeSamples
>>> from acoular.spectra import PowerSpectra
>>> np.random.seed(0)
>>>
>>> data = np.random.rand(1000, 4)
>>> ts = TimeSamples(data=data, sample_freq=51200)
>>> ps = PowerSpectra(source=ts, block_size=128, window='Hamming')
>>> ps.synthetic_ev(freq=5000, num=5)
array([0.00048803, 0.0010141 , 0.00234248, 0.00457097])
>>> ps.synthetic_ev(freq=5000)
array([0.00022468, 0.0004589 , 0.00088059, 0.00245989])
class acoular.spectra.PowerSpectraImport#

Bases: PowerSpectra

Provides a dummy class for using pre-calculated CSMs.

This class does not calculate the CSM. Instead, the user can inject one or multiple existing CSMs by setting the csm attribute. This can be useful when algorithms shall be evaluated with existing CSMs. The frequency or frequencies contained by the CSM must be set via the frequencies attribute. The attr:num_channels attributes is determined on the basis of the CSM shape. In contrast to the PowerSpectra object, the attributes sample_freq, source, block_size, window, overlap, cached, and num_blocks have no functionality.

csm = Property(desc='cross spectral matrix')#

The cross-spectral matrix stored in an array of shape (n, m, m) of complex for n frequencies and m channels.

frequencies = Union(None, CArray, Float, desc='frequencies included in the cross-spectral matrix')#

The frequencies included in the CSM in ascending order. Accepts list, array, or a single float value.

num_channels = Property(depends_on=['digest'])#

Number of time data channels, inferred from the shape of the CSM.

source = Enum(None, desc='PowerSpectraImport cannot consume time data')#

PowerSpectraImport does not consume time data; source is always None.

sample_freq = Enum(None, desc='sampling frequency')#

Sampling frequency of the signal. Default is None

block_size = Enum(None, desc='PowerSpectraImport does not operate on blocks of time data')#

Block size for FFT, non-functional in this class.

window = Enum(None, desc='PowerSpectraImport does not perform windowing')#

Windowing method, non-functional in this class.

overlap = Enum(None, desc='PowerSpectraImport does not consume time data')#

Overlap between blocks, non-functional in this class.

cached = Enum(False, desc='PowerSpectraImport has no caching capabilities')#

Caching capability, always disabled.

num_blocks = Enum(None, desc='PowerSpectraImport cannot determine the number of blocks')#

Number of FFT blocks, always None.

digest = Property(depends_on=['_csmsum'])#

A unique identifier for the spectra, based on its properties. (read-only)

basename = Property(depends_on=['digest'], desc='basename for cache file')#

Name of the cache file without extension. (read-only)

fftfreq()#

Return the Discrete Fourier Transform sample frequencies.

The method checks the type of frequencies and returns the corresponding frequency array. If frequencies is not defined, a warning is raised.

Returns:
numpy.ndarray

Array containing the frequencies.