Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
cf672dc9d7 | |||
4cd390871a | |||
6cdc182b57 | |||
bfb23ad698 | |||
a986a6b9cd | |||
9caf5fe387 |
145
INSTALLATION.md
145
INSTALLATION.md
@ -1,145 +0,0 @@
|
|||||||
# Installation of LASP - Linux (Ubuntu-based)
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
Run the following on the command line to install all prerequisites on
|
|
||||||
Debian-based Linux, x86-64:
|
|
||||||
|
|
||||||
- `sudo apt install python3-pip libfftw3-3 libopenblas-base libusb-1.0-0 libpulse0`
|
|
||||||
|
|
||||||
|
|
||||||
## Installation from wheel (recommended for non-developers)
|
|
||||||
|
|
||||||
Go to: [LASP releases](https://code.ascee.nl/ASCEE/lasp/releases/latest/) and
|
|
||||||
download the latest `.whl`. Then run:
|
|
||||||
|
|
||||||
- `pip install lasp-*-linux_x86_64.whl`
|
|
||||||
|
|
||||||
## From source (Ubuntu-based)
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
Run the following one-liner:
|
|
||||||
|
|
||||||
- `sudo apt install -y git python3 python3-virtualenv python3-venv libopenblas-dev python3-pip libfftw3-dev libusb-1.0-0-dev libpulse-dev python3-build`
|
|
||||||
|
|
||||||
If building RtAudio with the ALSA backend, you will also require the following packages:
|
|
||||||
|
|
||||||
- `sudo apt install libclalsadrv-dev`
|
|
||||||
|
|
||||||
If building RtAudio with the Jack Audio Connection Kit (JACK) backend, you will also require the following packages:
|
|
||||||
|
|
||||||
- `sudo apt install libjack-jackd2-dev`
|
|
||||||
|
|
||||||
### Download & build
|
|
||||||
|
|
||||||
- `$ git clone --recursive https://code.ascee.nl/ASCEE/lasp.git`
|
|
||||||
- `$ cd lasp`
|
|
||||||
- `pip install -e .`
|
|
||||||
|
|
||||||
# Building and installation for Raspberry Pi (Raspberry Pi OS)
|
|
||||||
|
|
||||||
Run the following on the command line to install all prerequisites on
|
|
||||||
Raspberry Pi OS:
|
|
||||||
|
|
||||||
- `sudo apt install libfftw3-dev libopenblas64-dev libhdf5-dev libclalsadrv-dev`
|
|
||||||
|
|
||||||
In a virtualenv: install `build`
|
|
||||||
|
|
||||||
- `$ pip install build`
|
|
||||||
|
|
||||||
Then run:
|
|
||||||
|
|
||||||
- `$ git clone --recursive https://code.ascee.nl/ASCEE/lasp.git`
|
|
||||||
- `$ cd lasp`
|
|
||||||
- `$ pyproject-build`
|
|
||||||
|
|
||||||
Which will generate a `whl` in the `dist` folder, that is redistributable for Raspberry Pis that run Raspberry Pi OS.
|
|
||||||
|
|
||||||
When installing the `whl`, it appears that H5PY takes quite some time to install. To follow this process, run it it verbose mode.
|
|
||||||
|
|
||||||
|
|
||||||
# Installation - (x86_64) Windows (with WinPython), build with MSYS2
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Download and install [WinPython](https://winpython.github.io)
|
|
||||||
|
|
||||||
## From wheel
|
|
||||||
|
|
||||||
- Download latest wheel from [LASP releases](https://code.ascee.nl/ASCEE/lasp/releases/latest/) and
|
|
||||||
download the latest `.whl`. Then install with `pip`.
|
|
||||||
|
|
||||||
## From source
|
|
||||||
|
|
||||||
- Download and install [MSYS2](https://msys2.org). Make sure to install the
|
|
||||||
x86_64 version.
|
|
||||||
- When unzipping WinPython, make sure to choose a proper and simple path, i.e.
|
|
||||||
C:\winpython
|
|
||||||
- Download and install [Git for Windows](https://git-scm.com)
|
|
||||||
- Open an MSYS2 **MINGW64** terminal, and install some tools we require:
|
|
||||||
- `$ pacman -S git`
|
|
||||||
- Create a new virtualenv:
|
|
||||||
- `$ /c/winpython/<py-distr-dir>/python.exe -m venv venv`
|
|
||||||
- Add the venv-python to the path (eases a lot of commands)
|
|
||||||
- `$ export PATH=$PATH:~/venv/Scripts`
|
|
||||||
- Install `build`:
|
|
||||||
- `$ pip install build`
|
|
||||||
- Clone LASP:
|
|
||||||
- `$ git clone --recurse-submodules https://code.ascee.nl/ascee/lasp && cd lasp`
|
|
||||||
- If run for the first time, we have to install the libraries we depend on in
|
|
||||||
MSYS2 (this only has to be done on a fresh MSYS2 installation):
|
|
||||||
- `$ scripts/install_msys2_builddeps.sh`
|
|
||||||
- Copy over required DLL's to be included in distribution:
|
|
||||||
- `scripts/copy_windows_dlls.sh`
|
|
||||||
- And... build!
|
|
||||||
- `pyproject-build`
|
|
||||||
- Lastly: the generated wheel can be installed in the current virtualenv:
|
|
||||||
- `pip install dist/lasp*.whl`
|
|
||||||
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
|
|
||||||
## Online
|
|
||||||
|
|
||||||
[Online LASP documentation](https://lasp.ascee.nl/).
|
|
||||||
|
|
||||||
## In directory (Linux/Debian)
|
|
||||||
|
|
||||||
`$ sudo apt install doxygen graphviz`
|
|
||||||
`$ pip install doxypypy`
|
|
||||||
|
|
||||||
While still in lasp dir:
|
|
||||||
|
|
||||||
`$ doxygen`
|
|
||||||
|
|
||||||
This will build the documentation. It can be read by:
|
|
||||||
|
|
||||||
`$ <YOUR-BROWSER> doc/html/index.html`
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
|
|
||||||
- See examples directories for IPython notebooks.
|
|
||||||
- Please refer to the [documentation](https://lasp.ascee.nl/) for features.
|
|
||||||
|
|
||||||
|
|
||||||
# Development docs
|
|
||||||
|
|
||||||
## Bumping version number
|
|
||||||
|
|
||||||
When bumping the version number, please update the number in
|
|
||||||
|
|
||||||
- `pyproject.toml`
|
|
||||||
- `CMakeLists.txt`
|
|
||||||
|
|
||||||
Then, create a commit with tag `vX.X.X`, and push it.
|
|
||||||
|
|
||||||
## Updating to latest version (editable mode)
|
|
||||||
|
|
||||||
When updating to the latest version of LASP in editable mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
- $ git pull
|
|
||||||
- $ git submodule update
|
|
||||||
- $ pip install -e . -v
|
|
||||||
```
|
|
160
README.md
160
README.md
@ -1,14 +1,16 @@
|
|||||||
Library for Acoustic Signal Processing
|
Library for Acoustic Signal Processing
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
|
|
||||||
Welcome to LASP: Library for Acoustic Signal Processing. LASP is a C++ library
|
Welcome to LASP: Library for Acoustic Signal Processing. LASP is a C++ library
|
||||||
with a Python interface which is supposed to acquire and process (multi) sensor data in real time on a PC and output results.
|
with a Python interface which is supposed to acquire and process (multi) sensor data in real time on a PC and output results.
|
||||||
|
|
||||||
Current features that are implemented:
|
Current features that are implemented:
|
||||||
|
|
||||||
|
|
||||||
- Communication with data acquisition (DAQ) devices, of which:
|
- Communication with data acquisition (DAQ) devices, of which:
|
||||||
- Internal sound cards via the [RtAudio](http://www.music.mcgill.ca/~gary/rtaudio) backend. Many thanks to Gary P. Scavone et al.
|
- Internal sound cards via the [RtAudio](http://www.music.mcgill.ca/~gary/rtaudio) backend. Many thanks to Gary P. Scavone et al.
|
||||||
- [Measurement Computing](https://www.mccdaq.com) [DT9838A](https://www.mccdaq.com/Products/Sound-Vibration-DAQ/DT9837) signal analyzer.
|
- [Measurement Computing](https://www.mccdaq.com) [DT9838A](https://www.mccdaq.com/Products/Sound-Vibration-DAQ/DT9837) signal analyzer.
|
||||||
- Configuration of DAQ devices: AC coupling, IEPE, sensitivity physical
|
- Configuration of DAQ devices: AC coupling, IEPE, sensitivity physical
|
||||||
quantities.
|
quantities.
|
||||||
- Recording of signals from these DAQ devices, and storing in a HDF5 file.
|
- Recording of signals from these DAQ devices, and storing in a HDF5 file.
|
||||||
@ -29,8 +31,10 @@ Current features that are implemented:
|
|||||||
- Spectra data smoothing algorithms
|
- Spectra data smoothing algorithms
|
||||||
- Sensor calibration for microphones
|
- Sensor calibration for microphones
|
||||||
|
|
||||||
|
|
||||||
Future features (wish-list)
|
Future features (wish-list)
|
||||||
|
|
||||||
|
|
||||||
- Conventional and delay-and-sum beam-forming algorithms
|
- Conventional and delay-and-sum beam-forming algorithms
|
||||||
- Impedance tube measurement processing
|
- Impedance tube measurement processing
|
||||||
|
|
||||||
@ -38,7 +42,151 @@ For now, the source code is well-documented on [lasp.ascee.nl](https://lasp.asce
|
|||||||
additional documentation (the math behind it). This is maintained
|
additional documentation (the math behind it). This is maintained
|
||||||
in a sister repository [lasp-doc](https://code.ascee.nl/ascee/lasp-doc).
|
in a sister repository [lasp-doc](https://code.ascee.nl/ascee/lasp-doc).
|
||||||
|
|
||||||
For installation instructions, please visit
|
If you have any question(s), please feel free to contact us: [email](info@ascee.nl).
|
||||||
[INSTALLATON.md](https://code.ascee.nl/ASCEE/lasp/src/branch/master/INSTALLATION.md).
|
|
||||||
If you have any question(s), please feel free to contact us:
|
|
||||||
[email](mailto:info@ascee.nl).
|
# Installation - Linux (Ubuntu-based)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Run the following on the command line to install all prerequisites on
|
||||||
|
Debian-based Linux, x86-64:
|
||||||
|
|
||||||
|
- `sudo apt install python3-pip libfftw3-3 libopenblas-base libusb-1.0-0 libpulse0`
|
||||||
|
|
||||||
|
|
||||||
|
## Installation from wheel (recommended for non-developers)
|
||||||
|
|
||||||
|
Go to: [LASP releases](https://code.ascee.nl/ASCEE/lasp/releases/latest/) and
|
||||||
|
download the latest `.whl`. Then run:
|
||||||
|
|
||||||
|
- `pip install lasp-*-linux_x86_64.whl`
|
||||||
|
|
||||||
|
## From source (Ubuntu-based)
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
Run the following one-liner:
|
||||||
|
|
||||||
|
- `sudo apt install -y git python3 python3-virtualenv python3-venv libopenblas-dev python3-pip libfftw3-dev libusb-1.0-0-dev libpulse-dev python3-build`
|
||||||
|
|
||||||
|
If building RtAudio with the ALSA backend, you will also require the following packages:
|
||||||
|
|
||||||
|
- `sudo apt install libclalsadrv-dev`
|
||||||
|
|
||||||
|
If building RtAudio with the Jack Audio Connection Kit (JACK) backend, you will also require the following packages:
|
||||||
|
|
||||||
|
- `sudo apt install libjack-jackd2-dev`
|
||||||
|
|
||||||
|
### Download & build
|
||||||
|
|
||||||
|
- `$ git clone --recursive https://code.ascee.nl/ASCEE/lasp.git`
|
||||||
|
- `$ cd lasp`
|
||||||
|
- `pip install -e .`
|
||||||
|
|
||||||
|
# Building and installation for Raspberry Pi (Raspberry Pi OS)
|
||||||
|
|
||||||
|
Run the following on the command line to install all prerequisites on
|
||||||
|
Raspberry Pi OS:
|
||||||
|
|
||||||
|
- `sudo apt install libfftw3-dev libopenblas64-dev libhdf5-dev libclalsadrv-dev`
|
||||||
|
|
||||||
|
In a virtualenv: install `build`
|
||||||
|
|
||||||
|
- `$ pip install build`
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
|
||||||
|
- `$ git clone --recursive https://code.ascee.nl/ASCEE/lasp.git`
|
||||||
|
- `$ cd lasp`
|
||||||
|
- `$ pyproject-build`
|
||||||
|
|
||||||
|
Which will generate a `whl` in the `dist` folder, that is redistributable for Raspberry Pis that run Raspberry Pi OS.
|
||||||
|
|
||||||
|
When installing the `whl`, it appears that H5PY takes quite some time to install. To follow this process, run it it verbose mode.
|
||||||
|
|
||||||
|
|
||||||
|
# Installation - (x86_64) Windows (with WinPython), build with MSYS2
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Download and install [WinPython](https://winpython.github.io)
|
||||||
|
|
||||||
|
## From wheel
|
||||||
|
|
||||||
|
- Download latest wheel from [LASP releases](https://code.ascee.nl/ASCEE/lasp/releases/latest/) and
|
||||||
|
download the latest `.whl`. Then install with `pip`.
|
||||||
|
|
||||||
|
## From source
|
||||||
|
|
||||||
|
- Download and install [MSYS2](https://msys2.org). Make sure to install the
|
||||||
|
x86_64 version.
|
||||||
|
- When unzipping WinPython, make sure to choose a proper and simple path, i.e.
|
||||||
|
C:\winpython
|
||||||
|
- Download and install [Git for Windows](https://git-scm.com)
|
||||||
|
- Open an MSYS2 **MINGW64** terminal, and install some tools we require:
|
||||||
|
- `$ pacman -S git`
|
||||||
|
- Create a new virtualenv:
|
||||||
|
- `$ /c/winpython/<py-distr-dir>/python.exe -m venv venv`
|
||||||
|
- Add the venv-python to the path (eases a lot of commands)
|
||||||
|
- `$ export PATH=$PATH:~/venv/Scripts`
|
||||||
|
- Install `build`:
|
||||||
|
- `$ pip install build`
|
||||||
|
- Clone LASP:
|
||||||
|
- `$ git clone --recurse-submodules https://code.ascee.nl/ascee/lasp && cd lasp`
|
||||||
|
- If run for the first time, we have to install the libraries we depend on in
|
||||||
|
MSYS2 (this only has to be done on a fresh MSYS2 installation):
|
||||||
|
- `$ scripts/install_msys2_builddeps.sh`
|
||||||
|
- Copy over required DLL's to be included in distribution:
|
||||||
|
- `scripts/copy_windows_dlls.sh`
|
||||||
|
- And... build!
|
||||||
|
- `pyproject-build`
|
||||||
|
- Lastly: the generated wheel can be installed in the current virtualenv:
|
||||||
|
- `pip install dist/lasp*.whl`
|
||||||
|
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
## Online
|
||||||
|
|
||||||
|
[Online LASP documentation](https://lasp.ascee.nl/).
|
||||||
|
|
||||||
|
## In directory (Linux/Debian)
|
||||||
|
|
||||||
|
`$ sudo apt install doxygen graphviz`
|
||||||
|
`$ pip install doxypypy`
|
||||||
|
|
||||||
|
While still in lasp dir:
|
||||||
|
|
||||||
|
`$ doxygen`
|
||||||
|
|
||||||
|
This will build the documentation. It can be read by:
|
||||||
|
|
||||||
|
`$ <YOUR-BROWSER> doc/html/index.html`
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
- See examples directories for IPython notebooks.
|
||||||
|
- Please refer to the [documentation](https://lasp.ascee.nl/) for features.
|
||||||
|
|
||||||
|
|
||||||
|
# Development docs
|
||||||
|
|
||||||
|
## Bumping version number
|
||||||
|
|
||||||
|
When bumping the version number, please update the number in
|
||||||
|
|
||||||
|
- `pyproject.toml`
|
||||||
|
- `CMakeLists.txt`
|
||||||
|
|
||||||
|
Then, create a commit with tag `vX.X.X`, and push it.
|
||||||
|
|
||||||
|
## Updating to latest version (editable mode)
|
||||||
|
|
||||||
|
When updating to the latest version of LASP in editable mode:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
- $ git pull
|
||||||
|
- $ git submodule update
|
||||||
|
- $ pip install -e . -v
|
||||||
|
```
|
@ -10,7 +10,8 @@ PowerSpectra::PowerSpectra(const us nfft, const Window::WindowType w)
|
|||||||
: PowerSpectra(Window::create(w, nfft)) {}
|
: PowerSpectra(Window::create(w, nfft)) {}
|
||||||
|
|
||||||
PowerSpectra::PowerSpectra(const vd &window)
|
PowerSpectra::PowerSpectra(const vd &window)
|
||||||
: nfft(window.size()), _fft(nfft), _window(window) {
|
: nfft(window.size()), _fft(nfft), _window(window)
|
||||||
|
{
|
||||||
|
|
||||||
d win_pow = arma::sum(window % window) / window.size();
|
d win_pow = arma::sum(window % window) / window.size();
|
||||||
|
|
||||||
@ -21,7 +22,8 @@ PowerSpectra::PowerSpectra(const vd &window)
|
|||||||
DEBUGTRACE_PRINT(win_pow);
|
DEBUGTRACE_PRINT(win_pow);
|
||||||
}
|
}
|
||||||
|
|
||||||
ccube PowerSpectra::compute(const dmat &input) {
|
ccube PowerSpectra::compute(const dmat &input)
|
||||||
|
{
|
||||||
|
|
||||||
/// Run very often. Silence this one.
|
/// Run very often. Silence this one.
|
||||||
/* DEBUGTRACE_ENTER; */
|
/* DEBUGTRACE_ENTER; */
|
||||||
@ -34,11 +36,13 @@ ccube PowerSpectra::compute(const dmat &input) {
|
|||||||
cmat rfft = _fft.fft(input_tmp);
|
cmat rfft = _fft.fft(input_tmp);
|
||||||
|
|
||||||
ccube output(rfft.n_rows, input.n_cols, input.n_cols);
|
ccube output(rfft.n_rows, input.n_cols, input.n_cols);
|
||||||
for (us i = 0; i < input.n_cols; i++) {
|
for (us i = 0; i < input.n_cols; i++)
|
||||||
|
{
|
||||||
/// This one can be run in parallel without any problem. Note that it is
|
/// This one can be run in parallel without any problem. Note that it is
|
||||||
/// the inner loop that is run in parallel.
|
/// the inner loop that is run in parallel.
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (us j = 0; j < input.n_cols; j++) {
|
for (us j = 0; j < input.n_cols; j++)
|
||||||
|
{
|
||||||
output.slice(j).col(i) =
|
output.slice(j).col(i) =
|
||||||
_scale_fac * (rfft.col(i) % arma::conj(rfft.col(j)));
|
_scale_fac * (rfft.col(i) % arma::conj(rfft.col(j)));
|
||||||
|
|
||||||
@ -52,37 +56,46 @@ ccube PowerSpectra::compute(const dmat &input) {
|
|||||||
AvPowerSpectra::AvPowerSpectra(const us nfft, const Window::WindowType w,
|
AvPowerSpectra::AvPowerSpectra(const us nfft, const Window::WindowType w,
|
||||||
const d overlap_percentage,
|
const d overlap_percentage,
|
||||||
const d time_constant_times_fs)
|
const d time_constant_times_fs)
|
||||||
: _ps(nfft, w) {
|
: _ps(nfft, w)
|
||||||
|
{
|
||||||
|
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
if (overlap_percentage >= 100 || overlap_percentage < 0) {
|
if (overlap_percentage >= 100 || overlap_percentage < 0)
|
||||||
|
{
|
||||||
throw rte("Overlap percentage should be >= 0 and < 100");
|
throw rte("Overlap percentage should be >= 0 and < 100");
|
||||||
}
|
}
|
||||||
|
|
||||||
_overlap_keep = (nfft * overlap_percentage) / 100;
|
_overlap_keep = (nfft * overlap_percentage) / 100;
|
||||||
DEBUGTRACE_PRINT(_overlap_keep);
|
DEBUGTRACE_PRINT(_overlap_keep);
|
||||||
if (_overlap_keep >= nfft) {
|
if (_overlap_keep >= nfft)
|
||||||
|
{
|
||||||
throw rte("Overlap is too high. Results in no jump. Please "
|
throw rte("Overlap is too high. Results in no jump. Please "
|
||||||
"choose a smaller overlap percentage or a higher nfft");
|
"choose a smaller overlap percentage or a higher nfft");
|
||||||
}
|
}
|
||||||
if (time_constant_times_fs < 0) {
|
if (time_constant_times_fs < 0)
|
||||||
|
{
|
||||||
_mode = Mode::Averaging;
|
_mode = Mode::Averaging;
|
||||||
} else if (time_constant_times_fs == 0) {
|
}
|
||||||
|
else if (time_constant_times_fs == 0)
|
||||||
|
{
|
||||||
_mode = Mode::Spectrogram;
|
_mode = Mode::Spectrogram;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_mode = Mode::Leaking;
|
_mode = Mode::Leaking;
|
||||||
_alpha = d_exp(-static_cast<d>((nfft - _overlap_keep)/time_constant_times_fs));
|
_alpha = d_exp(-static_cast<d>((nfft - _overlap_keep) / time_constant_times_fs));
|
||||||
DEBUGTRACE_PRINT(_alpha);
|
DEBUGTRACE_PRINT(_alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void AvPowerSpectra::reset() {
|
void AvPowerSpectra::reset()
|
||||||
|
{
|
||||||
_timeBuf.reset();
|
_timeBuf.reset();
|
||||||
_est.reset();
|
_est.reset();
|
||||||
n_averages=0;
|
_n_averages = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ccube AvPowerSpectra::compute(const dmat &timedata) {
|
ccube AvPowerSpectra::compute(const dmat &timedata)
|
||||||
|
{
|
||||||
|
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
DEBUGTRACE_PRINT(timedata.n_rows);
|
DEBUGTRACE_PRINT(timedata.n_rows);
|
||||||
@ -91,32 +104,42 @@ ccube AvPowerSpectra::compute(const dmat &timedata) {
|
|||||||
_timeBuf.push(timedata);
|
_timeBuf.push(timedata);
|
||||||
|
|
||||||
bool run_once = false;
|
bool run_once = false;
|
||||||
while (_timeBuf.size() >= _ps.nfft) {
|
while (_timeBuf.size() >= _ps.nfft)
|
||||||
|
{
|
||||||
|
|
||||||
DEBUGTRACE_PRINT((int)_mode);
|
DEBUGTRACE_PRINT((int)_mode);
|
||||||
|
|
||||||
dmat samples = _timeBuf.pop(_ps.nfft, _overlap_keep);
|
dmat samples = _timeBuf.pop(_ps.nfft, _overlap_keep);
|
||||||
|
|
||||||
switch (_mode) {
|
switch (_mode)
|
||||||
|
{
|
||||||
case (Mode::Spectrogram):
|
case (Mode::Spectrogram):
|
||||||
DEBUGTRACE_PRINT("Spectrogram mode");
|
DEBUGTRACE_PRINT("Spectrogram mode");
|
||||||
_est = _ps.compute(samples);
|
_est = _ps.compute(samples);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case (Mode::Averaging):
|
case (Mode::Averaging):
|
||||||
DEBUGTRACE_PRINT("Averaging mode");
|
DEBUGTRACE_PRINT("Averaging mode");
|
||||||
n_averages++;
|
_n_averages++;
|
||||||
if (n_averages == 1) {
|
if (_n_averages == 1)
|
||||||
|
{
|
||||||
_est = _ps.compute(samples);
|
_est = _ps.compute(samples);
|
||||||
} else {
|
|
||||||
_est = (static_cast<d>(n_averages - 1) / n_averages) * _est +
|
}
|
||||||
_ps.compute(samples) / n_averages;
|
else
|
||||||
|
{
|
||||||
|
_est = (static_cast<d>(_n_averages - 1) / _n_averages) * _est +
|
||||||
|
_ps.compute(samples) / _n_averages;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case (Mode::Leaking):
|
case (Mode::Leaking):
|
||||||
DEBUGTRACE_PRINT("Leaking mode");
|
DEBUGTRACE_PRINT("Leaking mode");
|
||||||
if (arma::size(_est) == arma::size(0, 0, 0)) {
|
if (arma::size(_est) == arma::size(0, 0, 0))
|
||||||
|
{
|
||||||
_est = _ps.compute(samples);
|
_est = _ps.compute(samples);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_est = _alpha * _est + (1 - _alpha) * _ps.compute(samples);
|
_est = _alpha * _est + (1 - _alpha) * _ps.compute(samples);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -128,6 +151,149 @@ ccube AvPowerSpectra::compute(const dmat &timedata) {
|
|||||||
/// Othewise, we return an empty ccube.
|
/// Othewise, we return an empty ccube.
|
||||||
return run_once ? _est : ccube();
|
return run_once ? _est : ccube();
|
||||||
}
|
}
|
||||||
ccube AvPowerSpectra::get_est() const {
|
ccube AvPowerSpectra::get_est() const
|
||||||
|
{
|
||||||
return _est;
|
return _est;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AvSweepPowerSpectra::AvSweepPowerSpectra(const us nfft, const Window::WindowType w,
|
||||||
|
const d overlap_percentage,
|
||||||
|
const d time_constant_times_fs,
|
||||||
|
const vd *A)
|
||||||
|
: _nfft(nfft), _ps(nfft, w)
|
||||||
|
{
|
||||||
|
|
||||||
|
DEBUGTRACE_ENTER;
|
||||||
|
if (overlap_percentage >= 100 || overlap_percentage < 0)
|
||||||
|
{
|
||||||
|
throw rte("Overlap percentage should be >= 0 and < 100");
|
||||||
|
}
|
||||||
|
if(A == nullptr) {
|
||||||
|
_A = vd(nfft, arma::fill::ones);
|
||||||
|
} else {
|
||||||
|
if(A->n_elem != (nfft / 2 + 1)) {
|
||||||
|
throw rte("Invalid length given for amplitude coefficients");
|
||||||
|
}
|
||||||
|
_A = arma::square(*A);
|
||||||
|
}
|
||||||
|
|
||||||
|
_overlap_keep = (nfft * overlap_percentage) / 100;
|
||||||
|
DEBUGTRACE_PRINT(_overlap_keep);
|
||||||
|
if (_overlap_keep >= nfft)
|
||||||
|
{
|
||||||
|
throw rte("Overlap is too high. Results in no jump. Please "
|
||||||
|
"choose a smaller overlap percentage or a higher nfft");
|
||||||
|
}
|
||||||
|
if (time_constant_times_fs < 0)
|
||||||
|
{
|
||||||
|
_mode = Mode::Averaging;
|
||||||
|
}
|
||||||
|
else if (time_constant_times_fs == 0)
|
||||||
|
{
|
||||||
|
_mode = Mode::Spectrogram;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_mode = Mode::Leaking;
|
||||||
|
_alpha = d_exp(-static_cast<d>((nfft - _overlap_keep) / time_constant_times_fs));
|
||||||
|
DEBUGTRACE_PRINT(_alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void AvSweepPowerSpectra::reset()
|
||||||
|
{
|
||||||
|
_timeBuf.reset();
|
||||||
|
_est.reset();
|
||||||
|
_n_averages.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
ccube AvSweepPowerSpectra::compute(const dmat &timedata)
|
||||||
|
{
|
||||||
|
|
||||||
|
DEBUGTRACE_ENTER;
|
||||||
|
DEBUGTRACE_PRINT(timedata.n_rows);
|
||||||
|
DEBUGTRACE_PRINT(_ps.nfft);
|
||||||
|
|
||||||
|
_timeBuf.push(timedata);
|
||||||
|
|
||||||
|
bool run_once = false;
|
||||||
|
while (_timeBuf.size() >= _ps.nfft)
|
||||||
|
{
|
||||||
|
|
||||||
|
DEBUGTRACE_PRINT((int)_mode);
|
||||||
|
|
||||||
|
dmat samples = _timeBuf.pop(_ps.nfft, _overlap_keep);
|
||||||
|
|
||||||
|
us ncols = timedata.n_cols;
|
||||||
|
|
||||||
|
switch (_mode)
|
||||||
|
{
|
||||||
|
case (Mode::Spectrogram):
|
||||||
|
DEBUGTRACE_PRINT("Spectrogram mode");
|
||||||
|
_est = _ps.compute(samples);
|
||||||
|
break;
|
||||||
|
case (Mode::Averaging):
|
||||||
|
{
|
||||||
|
DEBUGTRACE_PRINT("Averaging mode");
|
||||||
|
|
||||||
|
ccube temp = _ps.compute(samples);
|
||||||
|
us nfreq = arma::size(temp)[0];
|
||||||
|
|
||||||
|
if (_est.size() == 0) {
|
||||||
|
// Initialize empty
|
||||||
|
_est = ccube(nfreq, ncols, ncols, arma::fill::zeros);
|
||||||
|
_n_averages = vd(nfreq, arma::fill::zeros);
|
||||||
|
}
|
||||||
|
|
||||||
|
vd corr_pow = arma::real(temp.slice(0).col(0)) / _A;
|
||||||
|
|
||||||
|
d threshold = pow(10.0, log10(arma::max(corr_pow)*arma::median(corr_pow))/2.0);
|
||||||
|
// d threshold = 1e-13;
|
||||||
|
|
||||||
|
for (us f=0; f < nfreq; f++)
|
||||||
|
{
|
||||||
|
if (real(temp(f, 0, 0)) / _A(f) > threshold)
|
||||||
|
{
|
||||||
|
_n_averages[f]++;
|
||||||
|
if (_n_averages[f] == 1)
|
||||||
|
{
|
||||||
|
_est.row(f) = temp.row(f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_est.row(f) = (static_cast<d>(_n_averages[f] - 1) / _n_averages[f]) * _est.row(f) +
|
||||||
|
temp.row(f) / _n_averages[f];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (Mode::Leaking):
|
||||||
|
DEBUGTRACE_PRINT("Leaking mode");
|
||||||
|
if (arma::size(_est) == arma::size(0, 0, 0))
|
||||||
|
{
|
||||||
|
_est = _ps.compute(samples);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_est = _alpha * _est + (1 - _alpha) * _ps.compute(samples);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // end switch mode
|
||||||
|
run_once = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of current estimator in case we have done one update.
|
||||||
|
/// Othewise, we return an empty ccube.
|
||||||
|
return run_once ? _est : ccube();
|
||||||
|
}
|
||||||
|
|
||||||
|
ccube AvSweepPowerSpectra::get_est() const
|
||||||
|
{
|
||||||
|
return _est;
|
||||||
|
}
|
||||||
|
|
||||||
|
vd AvSweepPowerSpectra::get_A() const
|
||||||
|
{
|
||||||
|
return _A;
|
||||||
|
}
|
||||||
|
@ -81,7 +81,7 @@ class AvPowerSpectra {
|
|||||||
|
|
||||||
Mode _mode;
|
Mode _mode;
|
||||||
d _alpha; // Only valid in case of 'Leaking'
|
d _alpha; // Only valid in case of 'Leaking'
|
||||||
us n_averages = 0; // Only valid in case of 'Averaging'
|
us _n_averages = 0; // Only valid in case of 'Averaging'
|
||||||
|
|
||||||
PowerSpectra _ps;
|
PowerSpectra _ps;
|
||||||
/**
|
/**
|
||||||
@ -165,3 +165,112 @@ public:
|
|||||||
us exactOverlapSamples() const { return _ps.nfft - _overlap_keep; }
|
us exactOverlapSamples() const { return _ps.nfft - _overlap_keep; }
|
||||||
};
|
};
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Estimate cross-power spectra using Welch' method of spectral
|
||||||
|
* estimation. The exact amount of overlap in Welch' method is rounded up to a
|
||||||
|
* certain amount of samples. This class is specifically for sweeps and will ignore
|
||||||
|
* fft blocks where the auto-power of the "reference" signal is below the average
|
||||||
|
* when in averaging mode.
|
||||||
|
*/
|
||||||
|
class AvSweepPowerSpectra {
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
Averaging = 0, // Averaging all time date
|
||||||
|
Leaking = 1, // Exponential weighting of an "instantaneous cps"
|
||||||
|
Spectrogram = 2 // Instantenous spectrum, no averaging
|
||||||
|
};
|
||||||
|
|
||||||
|
Mode _mode;
|
||||||
|
d _alpha; // Only valid in case of 'Leaking'
|
||||||
|
us _nfft;
|
||||||
|
vd _n_averages { 1, arma::fill::zeros}; // Only valid in case of 'Averaging'
|
||||||
|
vd _A; // squared amplitude envelope
|
||||||
|
|
||||||
|
PowerSpectra _ps;
|
||||||
|
/**
|
||||||
|
* @brief Current estimate of cross-spectral density
|
||||||
|
*/
|
||||||
|
ccube _est;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Buffer of storage of time data.
|
||||||
|
*/
|
||||||
|
TimeBuffer _timeBuf;
|
||||||
|
/**
|
||||||
|
* @brief The amount of samples to keep in the overlap
|
||||||
|
*/
|
||||||
|
us _overlap_keep;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Initalize averaged power spectra computer. If a time constant is
|
||||||
|
* given > 0, it is used in a kind of exponential weighting.
|
||||||
|
*
|
||||||
|
* @param nfft The fft length
|
||||||
|
* @param w The window type.
|
||||||
|
* @param overlap_percentage A number 0 < overlap_percentage <= 100. It
|
||||||
|
* determines the amount of overlap used in Welch' method. A typical value is
|
||||||
|
* 50 %, i.e. 50.
|
||||||
|
* @param fs_tau Value should either be < 0, indicating that the
|
||||||
|
* estimate is averages over all time data.
|
||||||
|
* For a value = 0 the instantaneous power spectrum is returned, which can be
|
||||||
|
* interpreted as the spectrogram. For a value > 0 a exponential forgetting is
|
||||||
|
* used, where the value is used as the time constant such that the decay
|
||||||
|
* follows approximately the trend exp(-n/fs_tau), where n is the
|
||||||
|
* sample number in the power spectra. To choose 'fast' time weighting, set
|
||||||
|
* time_constant to the value of fs*0.125, where fs denotes the sampling
|
||||||
|
* frequency.
|
||||||
|
**/
|
||||||
|
AvSweepPowerSpectra(const us nfft = 2048,
|
||||||
|
const Window::WindowType w = Window::WindowType::Hann,
|
||||||
|
const d overlap_percentage = 50.,
|
||||||
|
const d fs_tau = -1,
|
||||||
|
const vd *A = nullptr); // amplitude envelope
|
||||||
|
|
||||||
|
AvSweepPowerSpectra(const AvSweepPowerSpectra &) = default;
|
||||||
|
AvSweepPowerSpectra &operator=(const AvSweepPowerSpectra &) = default;
|
||||||
|
AvSweepPowerSpectra (AvSweepPowerSpectra&& ) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset to empty state. Clears the time buffer and sets estimator to
|
||||||
|
* empty.
|
||||||
|
*/
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute an update of the power spectra based on given time data.
|
||||||
|
* Note that the number of channels is determined from the first time this
|
||||||
|
* function is called. If a later call has an incompatible number of
|
||||||
|
* channels, a runtime error is thrown.
|
||||||
|
*
|
||||||
|
* @param timedata
|
||||||
|
*
|
||||||
|
* @return a copy of the latest estimate of the power spectra. an
|
||||||
|
* update is only given if the amount of new time data is enough to compute a
|
||||||
|
* new estimate. if no new estimate is available, it returns an empty ccube.
|
||||||
|
* Note that the latest available estimate can be obtained using get_est().
|
||||||
|
* */
|
||||||
|
ccube compute(const dmat &timedata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the latest estimate of cps (cross-power spectra.
|
||||||
|
*
|
||||||
|
* @return a copy of the latest estimate of the power spectra. an
|
||||||
|
* update is only given if the amount of new time data is enough to compute a
|
||||||
|
* new estimate. If no estimate is available, it returns an empty ccube.
|
||||||
|
* */
|
||||||
|
ccube get_est() const;
|
||||||
|
|
||||||
|
vd get_A() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The overlap is rounded to a certain amount of time samples. This
|
||||||
|
* function returns that value.
|
||||||
|
*
|
||||||
|
* @return The amount of samples in overlapping.
|
||||||
|
*/
|
||||||
|
us exactOverlapSamples() const { return _ps.nfft - _overlap_keep; }
|
||||||
|
};
|
||||||
|
/** @} */
|
||||||
|
@ -38,6 +38,7 @@ protected:
|
|||||||
int _interruption_frame_count = 0;
|
int _interruption_frame_count = 0;
|
||||||
|
|
||||||
virtual void resetImpl() = 0;
|
virtual void resetImpl() = 0;
|
||||||
|
virtual void resetPos() = 0;
|
||||||
virtual vd genSignalUnscaled(const us nframes) = 0;
|
virtual vd genSignalUnscaled(const us nframes) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -73,9 +74,9 @@ public:
|
|||||||
* @brief Mute the signal. Passes through the DC offset. No lock is hold. If
|
* @brief Mute the signal. Passes through the DC offset. No lock is hold. If
|
||||||
* it just works one block later, than that is just the case.
|
* it just works one block later, than that is just the case.
|
||||||
*
|
*
|
||||||
* @param mute if tre
|
* @param mute if true
|
||||||
*/
|
*/
|
||||||
void setMute(bool mute = true) { _muted = mute; _interruption_frame_count=0; }
|
void setMute(bool mute = true) { _muted = mute; _interruption_frame_count=0; resetPos();}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the level of the signal generator
|
* @brief Set the level of the signal generator
|
||||||
|
@ -24,60 +24,85 @@ DEBUGTRACE_VARIABLES;
|
|||||||
|
|
||||||
Noise::Noise(){DEBUGTRACE_ENTER}
|
Noise::Noise(){DEBUGTRACE_ENTER}
|
||||||
|
|
||||||
vd Noise::genSignalUnscaled(us nframes) {
|
vd Noise::genSignalUnscaled(us nframes)
|
||||||
|
{
|
||||||
return arma::randn<vd>(nframes);
|
return arma::randn<vd>(nframes);
|
||||||
}
|
}
|
||||||
void Noise::resetImpl() {}
|
void Noise::resetImpl() {}
|
||||||
|
void Noise::resetPos() {}
|
||||||
|
|
||||||
Sine::Sine(const d freq) : omg(2 * arma::datum::pi * freq) { DEBUGTRACE_ENTER; }
|
Sine::Sine(const d freq) : omg(2 * arma::datum::pi * freq) { DEBUGTRACE_ENTER; }
|
||||||
|
|
||||||
vd Sine::genSignalUnscaled(const us nframes) {
|
vd Sine::genSignalUnscaled(const us nframes)
|
||||||
|
{
|
||||||
/* DEBUGTRACE_ENTER; */
|
/* DEBUGTRACE_ENTER; */
|
||||||
slock lck(_mtx);
|
slock lck(_mtx);
|
||||||
const d pi = arma::datum::pi;
|
const d pi = arma::datum::pi;
|
||||||
vd phase_vec =
|
vd phase_vec =
|
||||||
arma::linspace(phase, phase + omg * (nframes - 1) / _fs, nframes);
|
arma::linspace(phase, phase + omg * (nframes - 1) / _fs, nframes);
|
||||||
phase += omg * nframes / _fs;
|
phase += omg * nframes / _fs;
|
||||||
while (phase > 2 * arma::datum::pi) {
|
while (phase > 2 * arma::datum::pi)
|
||||||
|
{
|
||||||
phase -= 2 * pi;
|
phase -= 2 * pi;
|
||||||
}
|
}
|
||||||
return arma::sin(phase_vec);
|
return arma::sin(phase_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
vd Periodic::genSignalUnscaled(const us nframes) {
|
vd Periodic::genSignalUnscaled(const us nframes)
|
||||||
|
{
|
||||||
vd res(nframes);
|
vd res(nframes);
|
||||||
slock lck(_mtx);
|
slock lck(_mtx);
|
||||||
if (_signal.size() == 0) {
|
if (_signal.size() == 0)
|
||||||
|
{
|
||||||
throw rte("No signal defined while calling");
|
throw rte("No signal defined while calling");
|
||||||
}
|
}
|
||||||
for (us i = 0; i < nframes; i++) {
|
if (_signal.size() != A_.size())
|
||||||
res(i) = _signal[_cur_pos];
|
{
|
||||||
|
std::cout << "Seq size: " << _signal.size() << ", A size: " << A_.size() << "\n";
|
||||||
|
throw rte("Sequence and amplitude envelopes have different lengths");
|
||||||
|
}
|
||||||
|
for (us i = 0; i < nframes; i++)
|
||||||
|
{
|
||||||
|
res(i) = A_[_cur_pos] * _signal[_cur_pos];
|
||||||
_cur_pos++;
|
_cur_pos++;
|
||||||
_cur_pos %= _signal.size();
|
_cur_pos %= _signal.size();
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Periodic::setA(const vd &A)
|
||||||
|
{
|
||||||
|
A_ = A;
|
||||||
|
}
|
||||||
|
|
||||||
Sweep::Sweep(const d fl, const d fu, const d Ts, const d Tq, const us flags)
|
Sweep::Sweep(const d fl, const d fu, const d Ts, const d Tq, const us flags)
|
||||||
: fl_(fl), fu_(fu), Ts(Ts), Tq(Tq), flags(flags) {
|
: fl_(fl), fu_(fu), Ts(Ts), Tq(Tq), flags(flags)
|
||||||
if (fl <= 0 || fu < fl || Ts <= 0) {
|
{
|
||||||
|
if (fl <= 0 || fu < fl || Ts <= 0)
|
||||||
|
{
|
||||||
throw rte("Invalid sweep parameters");
|
throw rte("Invalid sweep parameters");
|
||||||
}
|
}
|
||||||
if ((flags & ForwardSweep) && (flags & BackwardSweep)) {
|
if ((flags & ForwardSweep) && (flags & BackwardSweep))
|
||||||
|
{
|
||||||
throw rte(
|
throw rte(
|
||||||
"Both forward and backward sweep flag set. Please only set either one "
|
"Both forward and backward sweep flag set. Please only set either one "
|
||||||
"or none for a continuous sweep");
|
"or none for a continuous sweep");
|
||||||
}
|
}
|
||||||
if ((flags & LinearSweep) && (flags & LogSweep)) {
|
if ((flags & LinearSweep) && (flags & LogSweep))
|
||||||
|
{
|
||||||
throw rte(
|
throw rte(
|
||||||
"Both logsweep and linear sweep flag set. Please only set either one.");
|
"Both logsweep and linear sweep flag set. Please only set either one.");
|
||||||
}
|
}
|
||||||
if (!((flags & LinearSweep) || (flags & LogSweep))) {
|
if (!((flags & LinearSweep) || (flags & LogSweep)))
|
||||||
|
{
|
||||||
throw rte("Either LinearSweep or LogSweep should be given as flag");
|
throw rte("Either LinearSweep or LogSweep should be given as flag");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sweep::resetImpl() {
|
void Sweep::resetImpl()
|
||||||
|
{
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
slock lck(_mtx);
|
slock lck(_mtx);
|
||||||
|
|
||||||
@ -86,7 +111,7 @@ void Sweep::resetImpl() {
|
|||||||
bool forward_sweep = flags & ForwardSweep;
|
bool forward_sweep = flags & ForwardSweep;
|
||||||
bool backward_sweep = flags & BackwardSweep;
|
bool backward_sweep = flags & BackwardSweep;
|
||||||
|
|
||||||
const d Dt = 1 / _fs; // Deltat
|
const d Dt = 1 / _fs; // Deltat
|
||||||
|
|
||||||
// Estimate N, the number of samples in the sweep part (non-quiescent part):
|
// Estimate N, the number of samples in the sweep part (non-quiescent part):
|
||||||
const us Ns = (us)(Ts * _fs);
|
const us Ns = (us)(Ts * _fs);
|
||||||
@ -94,14 +119,18 @@ void Sweep::resetImpl() {
|
|||||||
const us N = Ns + Nq;
|
const us N = Ns + Nq;
|
||||||
|
|
||||||
_signal = vd(N, arma::fill::zeros);
|
_signal = vd(N, arma::fill::zeros);
|
||||||
|
fn_ = vd(N, arma::fill::zeros);
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|
||||||
d fl, fu;
|
d fl, fu;
|
||||||
/* Swap fl and fu for a backward sweep */
|
/* Swap fl and fu for a backward sweep */
|
||||||
if (backward_sweep) {
|
if (backward_sweep)
|
||||||
|
{
|
||||||
fu = fl_;
|
fu = fl_;
|
||||||
fl = fu_;
|
fl = fu_;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
/* Case of continuous sweep, or forward sweep */
|
/* Case of continuous sweep, or forward sweep */
|
||||||
fl = fl_;
|
fl = fl_;
|
||||||
fu = fu_;
|
fu = fu_;
|
||||||
@ -110,8 +139,10 @@ void Sweep::resetImpl() {
|
|||||||
d phase = 0;
|
d phase = 0;
|
||||||
|
|
||||||
/* Linear sweep */
|
/* Linear sweep */
|
||||||
if (flags & LinearSweep) {
|
if (flags & LinearSweep)
|
||||||
if (forward_sweep || backward_sweep) {
|
{
|
||||||
|
if (forward_sweep || backward_sweep)
|
||||||
|
{
|
||||||
/* Forward or backward sweep */
|
/* Forward or backward sweep */
|
||||||
/* TRACE(15, "Forward or backward sweep"); */
|
/* TRACE(15, "Forward or backward sweep"); */
|
||||||
us K = (us)(Dt * (fl * Ns + 0.5 * (Ns - 1) * (fu - fl)));
|
us K = (us)(Dt * (fl * Ns + 0.5 * (Ns - 1) * (fu - fl)));
|
||||||
@ -120,12 +151,16 @@ void Sweep::resetImpl() {
|
|||||||
/* iVARTRACE(15, K); */
|
/* iVARTRACE(15, K); */
|
||||||
/* dVARTRACE(15, eps); */
|
/* dVARTRACE(15, eps); */
|
||||||
|
|
||||||
for (us n = 0; n < Ns; n++) {
|
for (us n = 0; n < Ns; n++)
|
||||||
|
{
|
||||||
_signal(n) = d_sin(phase);
|
_signal(n) = d_sin(phase);
|
||||||
d fn = fl + ((d)n) / Ns * (fu + eps - fl);
|
d fn = fl + ((d)n) / Ns * (fu + eps - fl);
|
||||||
|
fn_(n) = fn;
|
||||||
phase += 2 * arma::datum::pi * Dt * fn;
|
phase += 2 * arma::datum::pi * Dt * fn;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
/* Continous sweep */
|
/* Continous sweep */
|
||||||
/* TRACE(15, "continuous sweep"); */
|
/* TRACE(15, "continuous sweep"); */
|
||||||
|
|
||||||
@ -150,18 +185,24 @@ void Sweep::resetImpl() {
|
|||||||
/* dVARTRACE(15, eps); */
|
/* dVARTRACE(15, eps); */
|
||||||
d phase = 0;
|
d phase = 0;
|
||||||
|
|
||||||
for (us n = 0; n <= Ns; n++) {
|
for (us n = 0; n < Ns; n++)
|
||||||
|
{
|
||||||
/* iVARTRACE(17, n); */
|
/* iVARTRACE(17, n); */
|
||||||
if (n < N) {
|
if (n < N)
|
||||||
|
{
|
||||||
_signal[n] = d_sin(phase);
|
_signal[n] = d_sin(phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
d fn;
|
d fn;
|
||||||
if (n <= Nf) {
|
if (n <= Nf)
|
||||||
|
{
|
||||||
fn = fl + ((d)n) / Nf * (fu - fl);
|
fn = fl + ((d)n) / Nf * (fu - fl);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fn = fu - ((d)n - Nf) / Nb * (fu + eps - fl);
|
fn = fu - ((d)n - Nf) / Nb * (fu + eps - fl);
|
||||||
}
|
}
|
||||||
|
fn_(n) = fn;
|
||||||
/* dbgassert(fn >= 0, "BUG"); */
|
/* dbgassert(fn >= 0, "BUG"); */
|
||||||
|
|
||||||
phase += 2 * number_pi * Dt * fn;
|
phase += 2 * number_pi * Dt * fn;
|
||||||
@ -169,9 +210,12 @@ void Sweep::resetImpl() {
|
|||||||
/* This should be a very small number!! */
|
/* This should be a very small number!! */
|
||||||
/* dVARTRACE(15, phase); */
|
/* dVARTRACE(15, phase); */
|
||||||
}
|
}
|
||||||
} else if (flags & LogSweep) {
|
}
|
||||||
|
else if (flags & LogSweep)
|
||||||
|
{
|
||||||
DEBUGTRACE_PRINT("Log sweep");
|
DEBUGTRACE_PRINT("Log sweep");
|
||||||
if (forward_sweep || backward_sweep) {
|
if (forward_sweep || backward_sweep)
|
||||||
|
{
|
||||||
/* Forward or backward sweep */
|
/* Forward or backward sweep */
|
||||||
DEBUGTRACE_PRINT("Forward or backward sweep");
|
DEBUGTRACE_PRINT("Forward or backward sweep");
|
||||||
d k1 = (fu / fl);
|
d k1 = (fu / fl);
|
||||||
@ -180,7 +224,8 @@ void Sweep::resetImpl() {
|
|||||||
|
|
||||||
/* Iterate k to the right solution */
|
/* Iterate k to the right solution */
|
||||||
d E;
|
d E;
|
||||||
for (us iter = 0; iter < 10; iter++) {
|
for (us iter = 0; iter < 10; iter++)
|
||||||
|
{
|
||||||
E = 1 + K / (Dt * fl) * (d_pow(k, 1.0 / Ns) - 1) - k;
|
E = 1 + K / (Dt * fl) * (d_pow(k, 1.0 / Ns) - 1) - k;
|
||||||
d dEdk = K / (Dt * fl) * d_pow(k, 1.0 / Ns) / (Ns * k) - 1;
|
d dEdk = K / (Dt * fl) * d_pow(k, 1.0 / Ns) / (Ns * k) - 1;
|
||||||
k -= E / dEdk;
|
k -= E / dEdk;
|
||||||
@ -191,12 +236,16 @@ void Sweep::resetImpl() {
|
|||||||
DEBUGTRACE_PRINT(k);
|
DEBUGTRACE_PRINT(k);
|
||||||
DEBUGTRACE_PRINT(E);
|
DEBUGTRACE_PRINT(E);
|
||||||
|
|
||||||
for (us n = 0; n < Ns; n++) {
|
for (us n = 0; n < Ns; n++)
|
||||||
|
{
|
||||||
_signal[n] = d_sin(phase);
|
_signal[n] = d_sin(phase);
|
||||||
d fn = fl * d_pow(k, ((d)n) / Ns);
|
d fn = fl * d_pow(k, ((d)n) / Ns);
|
||||||
|
fn_(n) = fn;
|
||||||
phase += 2 * number_pi * Dt * fn;
|
phase += 2 * number_pi * Dt * fn;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
DEBUGTRACE_PRINT("Continuous sweep");
|
DEBUGTRACE_PRINT("Continuous sweep");
|
||||||
|
|
||||||
const us Nf = Ns / 2;
|
const us Nf = Ns / 2;
|
||||||
@ -212,7 +261,8 @@ void Sweep::resetImpl() {
|
|||||||
|
|
||||||
/* Newton iterations to converge k to the value such that the sweep is
|
/* Newton iterations to converge k to the value such that the sweep is
|
||||||
* continuous */
|
* continuous */
|
||||||
for (us iter = 0; iter < NITER_NEWTON; iter++) {
|
for (us iter = 0; iter < NITER_NEWTON; iter++)
|
||||||
|
{
|
||||||
E = (k - 1) / (d_pow(k, 1.0 / Nf) - 1) +
|
E = (k - 1) / (d_pow(k, 1.0 / Nf) - 1) +
|
||||||
(k - 1) / (1 - d_pow(k, -1.0 / Nb)) - K / Dt / fl;
|
(k - 1) / (1 - d_pow(k, -1.0 / Nb)) - K / Dt / fl;
|
||||||
DEBUGTRACE_PRINT(E);
|
DEBUGTRACE_PRINT(E);
|
||||||
@ -236,29 +286,37 @@ void Sweep::resetImpl() {
|
|||||||
DEBUGTRACE_PRINT(k);
|
DEBUGTRACE_PRINT(k);
|
||||||
DEBUGTRACE_PRINT(E);
|
DEBUGTRACE_PRINT(E);
|
||||||
|
|
||||||
for (us n = 0; n <= Ns; n++) {
|
for (us n = 0; n < Ns; n++)
|
||||||
|
{
|
||||||
/* iVARTRACE(17, n); */
|
/* iVARTRACE(17, n); */
|
||||||
if (n < Ns) {
|
if (n < Ns)
|
||||||
|
{
|
||||||
_signal[n] = d_sin(phase);
|
_signal[n] = d_sin(phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
d fn;
|
d fn;
|
||||||
if (n <= Nf) {
|
if (n <= Nf)
|
||||||
|
{
|
||||||
fn = fl * d_pow(k, ((d)n) / Nf);
|
fn = fl * d_pow(k, ((d)n) / Nf);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
fn = fl * k * d_pow(1 / k, ((d)n - Nf) / Nb);
|
fn = fl * k * d_pow(1 / k, ((d)n - Nf) / Nb);
|
||||||
}
|
}
|
||||||
|
fn_(n) = fn;
|
||||||
/* dbgassert(fn >= 0, "BUG"); */
|
/* dbgassert(fn >= 0, "BUG"); */
|
||||||
|
|
||||||
phase += 2 * number_pi * Dt * fn;
|
phase += 2 * number_pi * Dt * fn;
|
||||||
while (phase > 2 * number_pi) phase -= 2 * number_pi;
|
while (phase > 2 * number_pi)
|
||||||
|
phase -= 2 * number_pi;
|
||||||
/* dVARTRACE(17, phase); */
|
/* dVARTRACE(17, phase); */
|
||||||
}
|
}
|
||||||
/* This should be a very small number!! */
|
/* This should be a very small number!! */
|
||||||
DEBUGTRACE_PRINT(phase);
|
DEBUGTRACE_PRINT(phase);
|
||||||
}
|
}
|
||||||
} // End of log sweep
|
} // End of log sweep
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// Either log or linear sweep had to be given as flags.
|
// Either log or linear sweep had to be given as flags.
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ class Noise : public Siggen {
|
|||||||
d level_linear;
|
d level_linear;
|
||||||
virtual vd genSignalUnscaled(const us nframes) override;
|
virtual vd genSignalUnscaled(const us nframes) override;
|
||||||
void resetImpl() override;
|
void resetImpl() override;
|
||||||
|
void resetPos() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@ -39,6 +40,7 @@ class Sine : public Siggen {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resetImpl() override final { phase = 0; }
|
void resetImpl() override final { phase = 0; }
|
||||||
|
void resetPos() override final { phase = 0; }
|
||||||
virtual vd genSignalUnscaled(const us nframes) override final;
|
virtual vd genSignalUnscaled(const us nframes) override final;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -58,6 +60,7 @@ class Sine : public Siggen {
|
|||||||
* periodic as the frequency can be any floating point value.
|
* periodic as the frequency can be any floating point value.
|
||||||
*/
|
*/
|
||||||
class Periodic: public Siggen {
|
class Periodic: public Siggen {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
vd _signal { 1, arma::fill::zeros};
|
vd _signal { 1, arma::fill::zeros};
|
||||||
us _cur_pos = 0;
|
us _cur_pos = 0;
|
||||||
@ -68,8 +71,16 @@ class Periodic: public Siggen {
|
|||||||
* @return As stated above
|
* @return As stated above
|
||||||
*/
|
*/
|
||||||
vd getSequence() const { return _signal; }
|
vd getSequence() const { return _signal; }
|
||||||
|
vd A_ { 1, arma::fill::ones};
|
||||||
|
|
||||||
|
void setA(const vd& A);
|
||||||
|
|
||||||
|
void resetPos() override final { _cur_pos = 0; }
|
||||||
|
|
||||||
virtual vd genSignalUnscaled(const us nframes) override final;
|
virtual vd genSignalUnscaled(const us nframes) override final;
|
||||||
|
|
||||||
|
vd getA() const { return A_; }
|
||||||
|
|
||||||
~Periodic() = default;
|
~Periodic() = default;
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -81,15 +92,18 @@ class Sweep : public Periodic {
|
|||||||
d fl_, fu_, Ts, Tq;
|
d fl_, fu_, Ts, Tq;
|
||||||
us index;
|
us index;
|
||||||
us flags;
|
us flags;
|
||||||
|
vd fn_ { 1, arma::fill::zeros};
|
||||||
|
|
||||||
void resetImpl() override;
|
void resetImpl() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr int ForwardSweep = 1 << 0;
|
static constexpr int ForwardSweep = 1 << 0;
|
||||||
static constexpr int BackwardSweep = 1 << 1;
|
static constexpr int BackwardSweep = 1 << 1;
|
||||||
static constexpr int LinearSweep = 1 << 2;
|
static constexpr int LinearSweep = 1 << 2;
|
||||||
static constexpr int LogSweep = 1 << 3;
|
static constexpr int LogSweep = 1 << 3;
|
||||||
|
|
||||||
|
vd getfn() const { return fn_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a sweep signal
|
* Create a sweep signal
|
||||||
*
|
*
|
||||||
|
@ -29,27 +29,28 @@ using rte = std::runtime_error;
|
|||||||
* @param m The Python module to add classes and methods to
|
* @param m The Python module to add classes and methods to
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void init_dsp(py::module &m) {
|
void init_dsp(py::module &m)
|
||||||
|
{
|
||||||
py::class_<Fft> fft(m, "Fft");
|
py::class_<Fft> fft(m, "Fft");
|
||||||
fft.def(py::init<us>());
|
fft.def(py::init<us>());
|
||||||
fft.def("fft", [](Fft &f, dpyarray dat) {
|
fft.def("fft", [](Fft &f, dpyarray dat)
|
||||||
|
{
|
||||||
if (dat.ndim() == 1) {
|
if (dat.ndim() == 1) {
|
||||||
return ColToNpy<c>(f.fft(NpyToCol<d, false>(dat)));
|
return ColToNpy<c>(f.fft(NpyToCol<d, false>(dat)));
|
||||||
} else if (dat.ndim() == 2) {
|
} else if (dat.ndim() == 2) {
|
||||||
return MatToNpy<c>(f.fft(NpyToMat<d, false>(dat)));
|
return MatToNpy<c>(f.fft(NpyToMat<d, false>(dat)));
|
||||||
} else {
|
} else {
|
||||||
throw rte("Invalid dimensions of array");
|
throw rte("Invalid dimensions of array");
|
||||||
}
|
} });
|
||||||
});
|
fft.def("ifft", [](Fft &f, cpyarray dat)
|
||||||
fft.def("ifft", [](Fft &f, cpyarray dat) {
|
{
|
||||||
if (dat.ndim() == 1) {
|
if (dat.ndim() == 1) {
|
||||||
return ColToNpy<d>(f.ifft(NpyToCol<c, false>(dat)));
|
return ColToNpy<d>(f.ifft(NpyToCol<c, false>(dat)));
|
||||||
} else if (dat.ndim() == 2) {
|
} else if (dat.ndim() == 2) {
|
||||||
return MatToNpy<d>(f.ifft(NpyToMat<c, false>(dat)));
|
return MatToNpy<d>(f.ifft(NpyToMat<c, false>(dat)));
|
||||||
} else {
|
} else {
|
||||||
throw rte("Invalid dimensions of array");
|
throw rte("Invalid dimensions of array");
|
||||||
}
|
} });
|
||||||
});
|
|
||||||
|
|
||||||
fft.def_static("load_fft_wisdom", &Fft::load_fft_wisdom);
|
fft.def_static("load_fft_wisdom", &Fft::load_fft_wisdom);
|
||||||
fft.def_static("store_fft_wisdom", &Fft::store_fft_wisdom);
|
fft.def_static("store_fft_wisdom", &Fft::store_fft_wisdom);
|
||||||
@ -71,41 +72,39 @@ void init_dsp(py::module &m) {
|
|||||||
/// SeriesBiquad
|
/// SeriesBiquad
|
||||||
py::class_<SeriesBiquad, std::shared_ptr<SeriesBiquad>> sbq(m, "SeriesBiquad",
|
py::class_<SeriesBiquad, std::shared_ptr<SeriesBiquad>> sbq(m, "SeriesBiquad",
|
||||||
filter);
|
filter);
|
||||||
sbq.def(py::init([](dpyarray filter) {
|
sbq.def(py::init([](dpyarray filter)
|
||||||
return std::make_shared<SeriesBiquad>(NpyToCol<d, false>(filter));
|
{ return std::make_shared<SeriesBiquad>(NpyToCol<d, false>(filter)); }));
|
||||||
}));
|
sbq.def("filter", [](SeriesBiquad &s, dpyarray input)
|
||||||
sbq.def("filter", [](SeriesBiquad &s, dpyarray input) {
|
{
|
||||||
vd res = NpyToCol<d, true>(input);
|
vd res = NpyToCol<d, true>(input);
|
||||||
s.filter(res);
|
s.filter(res);
|
||||||
return ColToNpy<d>(res);
|
return ColToNpy<d>(res); });
|
||||||
});
|
|
||||||
|
|
||||||
/// BiquadBank
|
/// BiquadBank
|
||||||
py::class_<BiquadBank, Filter, std::shared_ptr<BiquadBank>> bqb(m,
|
py::class_<BiquadBank, Filter, std::shared_ptr<BiquadBank>> bqb(m,
|
||||||
"BiquadBank");
|
"BiquadBank");
|
||||||
bqb.def(py::init([](dpyarray coefs) {
|
bqb.def(py::init([](dpyarray coefs)
|
||||||
return std::make_shared<BiquadBank>(NpyToMat<d, false>(coefs));
|
{ return std::make_shared<BiquadBank>(NpyToMat<d, false>(coefs)); }));
|
||||||
}));
|
bqb.def(py::init([](dpyarray coefs, dpyarray gains)
|
||||||
bqb.def(py::init([](dpyarray coefs, dpyarray gains) {
|
{
|
||||||
vd gains_arma = NpyToMat<d, false>(gains);
|
vd gains_arma = NpyToMat<d, false>(gains);
|
||||||
return std::make_shared<BiquadBank>(NpyToMat<d, false>(coefs), &gains_arma);
|
return std::make_shared<BiquadBank>(NpyToMat<d, false>(coefs), &gains_arma); }));
|
||||||
}));
|
|
||||||
|
|
||||||
bqb.def("setGains",
|
bqb.def("setGains",
|
||||||
[](BiquadBank &b, dpyarray gains) { b.setGains(NpyToCol(gains)); });
|
[](BiquadBank &b, dpyarray gains)
|
||||||
bqb.def("filter", [](BiquadBank &b, dpyarray input) {
|
{ b.setGains(NpyToCol(gains)); });
|
||||||
|
bqb.def("filter", [](BiquadBank &b, dpyarray input)
|
||||||
|
{
|
||||||
// Yes, a copy here
|
// Yes, a copy here
|
||||||
vd inout = NpyToCol<d, true>(input);
|
vd inout = NpyToCol<d, true>(input);
|
||||||
b.filter(inout);
|
b.filter(inout);
|
||||||
return ColToNpy(inout);
|
return ColToNpy(inout); });
|
||||||
});
|
|
||||||
|
|
||||||
/// PowerSpectra
|
/// PowerSpectra
|
||||||
py::class_<PowerSpectra> ps(m, "PowerSpectra");
|
py::class_<PowerSpectra> ps(m, "PowerSpectra");
|
||||||
ps.def(py::init<const us, const Window::WindowType>());
|
ps.def(py::init<const us, const Window::WindowType>());
|
||||||
ps.def("compute", [](PowerSpectra &ps, dpyarray input) {
|
ps.def("compute", [](PowerSpectra &ps, dpyarray input)
|
||||||
return CubeToNpy<c>(ps.compute(NpyToMat<d, false>(input)));
|
{ return CubeToNpy<c>(ps.compute(NpyToMat<d, false>(input))); });
|
||||||
});
|
|
||||||
|
|
||||||
/// AvPowerSpectra
|
/// AvPowerSpectra
|
||||||
py::class_<AvPowerSpectra> aps(m, "AvPowerSpectra");
|
py::class_<AvPowerSpectra> aps(m, "AvPowerSpectra");
|
||||||
@ -114,7 +113,8 @@ void init_dsp(py::module &m) {
|
|||||||
py::arg("windowType") = Window::WindowType::Hann,
|
py::arg("windowType") = Window::WindowType::Hann,
|
||||||
py::arg("overlap_percentage") = 50.0, py::arg("time_constant") = -1);
|
py::arg("overlap_percentage") = 50.0, py::arg("time_constant") = -1);
|
||||||
|
|
||||||
aps.def("compute", [](AvPowerSpectra &aps, dpyarray timedata) {
|
aps.def("compute", [](AvPowerSpectra &aps, dpyarray timedata)
|
||||||
|
{
|
||||||
std::optional<ccube> res;
|
std::optional<ccube> res;
|
||||||
dmat timedata_mat = NpyToMat<d, false>(timedata);
|
dmat timedata_mat = NpyToMat<d, false>(timedata);
|
||||||
{
|
{
|
||||||
@ -122,44 +122,97 @@ void init_dsp(py::module &m) {
|
|||||||
res = aps.compute(timedata_mat);
|
res = aps.compute(timedata_mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CubeToNpy<c>(res.value_or(ccube(0, 0, 0)));
|
return CubeToNpy<c>(res.value_or(ccube(0, 0, 0))); });
|
||||||
});
|
aps.def("get_est", [](const AvPowerSpectra &ps)
|
||||||
aps.def("get_est", [](const AvPowerSpectra &ps) {
|
{
|
||||||
ccube est = ps.get_est();
|
ccube est = ps.get_est();
|
||||||
return CubeToNpy<c>(est);
|
return CubeToNpy<c>(est); });
|
||||||
});
|
|
||||||
|
/// AvSweepPowerSpectra
|
||||||
|
py::class_<AvSweepPowerSpectra, std::unique_ptr<AvSweepPowerSpectra>> asps(m, "AvSweepPowerSpectra");
|
||||||
|
asps.def(py::init([](const us nfft,
|
||||||
|
const Window::WindowType windowtype,
|
||||||
|
const d overlap_percentage,
|
||||||
|
const d time_constant,
|
||||||
|
const dpyarray A)
|
||||||
|
{
|
||||||
|
std::unique_ptr<vd> theA = std::make_unique<vd>(NpyToCol<d, false>(A));
|
||||||
|
|
||||||
|
return std::make_unique<AvSweepPowerSpectra>(
|
||||||
|
nfft,
|
||||||
|
windowtype,
|
||||||
|
overlap_percentage,
|
||||||
|
time_constant,
|
||||||
|
theA.get());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
|
asps.def("compute", [](AvSweepPowerSpectra &asps, dpyarray timedata)
|
||||||
|
{
|
||||||
|
std::optional<ccube> res;
|
||||||
|
dmat timedata_mat = NpyToMat<d, false>(timedata);
|
||||||
|
{
|
||||||
|
py::gil_scoped_release release;
|
||||||
|
res = asps.compute(timedata_mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CubeToNpy<c>(res.value_or(ccube(0, 0, 0))); });
|
||||||
|
asps.def("get_est", [](const AvSweepPowerSpectra &sps)
|
||||||
|
{
|
||||||
|
ccube est = sps.get_est();
|
||||||
|
return CubeToNpy<c>(est); });
|
||||||
|
|
||||||
|
asps.def("get_A", [](const AvSweepPowerSpectra &sps)
|
||||||
|
{
|
||||||
|
vd A = sps.get_A();
|
||||||
|
return ColToNpy<d>(A); });
|
||||||
|
|
||||||
py::class_<SLM> slm(m, "cppSLM");
|
py::class_<SLM> slm(m, "cppSLM");
|
||||||
|
|
||||||
slm.def_static("fromBiquads", [](const d fs, const d Lref, const us ds,
|
slm.def_static("fromBiquads", [](const d fs, const d Lref, const us ds,
|
||||||
const d tau, dpyarray bandpass) {
|
const d tau, dpyarray bandpass)
|
||||||
return SLM::fromBiquads(fs, Lref, ds, tau, NpyToMat<d, false>(bandpass));
|
{
|
||||||
});
|
return SLM::fromBiquads(fs, Lref, ds, tau, NpyToMat<d, false>(bandpass)); });
|
||||||
|
|
||||||
slm.def_static("fromBiquads", [](const d fs, const d Lref, const us ds,
|
slm.def_static("fromBiquads", [](const d fs, const d Lref, const us ds,
|
||||||
const d tau, dpyarray prefilter,
|
const d tau, dpyarray prefilter,
|
||||||
py::array_t<d> bandpass) {
|
py::array_t<d> bandpass)
|
||||||
|
{
|
||||||
return SLM::fromBiquads(fs, Lref, ds, tau, NpyToCol<d, false>(prefilter),
|
return SLM::fromBiquads(fs, Lref, ds, tau, NpyToCol<d, false>(prefilter),
|
||||||
NpyToMat<d, false>(bandpass));
|
NpyToMat<d, false>(bandpass)); });
|
||||||
});
|
|
||||||
|
|
||||||
slm.def("run", [](SLM &slm, dpyarray in) {
|
slm.def("run", [](SLM &slm, dpyarray in)
|
||||||
return MatToNpy<d>(slm.run(NpyToCol<d, false>(in)));
|
{
|
||||||
});
|
return MatToNpy<d>(slm.run(NpyToCol<d, false>(in))); });
|
||||||
slm.def("Pm", [](const SLM &slm) { return ColToNpy<d>(slm.Pm); });
|
slm.def("Pm", [](const SLM &slm)
|
||||||
slm.def("Pmax", [](const SLM &slm) { return ColToNpy<d>(slm.Pmax); });
|
{
|
||||||
slm.def("Ppeak", [](const SLM &slm) { return ColToNpy<d>(slm.Ppeak); });
|
return ColToNpy<d>(slm.Pm); });
|
||||||
|
slm.def("Pmax", [](const SLM &slm)
|
||||||
|
{
|
||||||
|
return ColToNpy<d>(slm.Pmax); });
|
||||||
|
slm.def("Ppeak", [](const SLM &slm)
|
||||||
|
{
|
||||||
|
return ColToNpy<d>(slm.Ppeak); });
|
||||||
|
|
||||||
slm.def("Leq", [](const SLM &slm) { return ColToNpy<d>(slm.Leq()); });
|
slm.def("Leq", [](const SLM &slm)
|
||||||
slm.def("Lmax", [](const SLM &slm) { return ColToNpy<d>(slm.Lmax()); });
|
{
|
||||||
slm.def("Lpeak", [](const SLM &slm) { return ColToNpy<d>(slm.Lpeak()); });
|
return ColToNpy<d>(slm.Leq()); });
|
||||||
|
slm.def("Lmax", [](const SLM &slm)
|
||||||
|
{
|
||||||
|
return ColToNpy<d>(slm.Lmax()); });
|
||||||
|
slm.def("Lpeak", [](const SLM &slm)
|
||||||
|
{
|
||||||
|
return ColToNpy<d>(slm.Lpeak()); });
|
||||||
slm.def_static("suggestedDownSamplingFac", &SLM::suggestedDownSamplingFac);
|
slm.def_static("suggestedDownSamplingFac", &SLM::suggestedDownSamplingFac);
|
||||||
|
|
||||||
// Frequency smoother
|
// Frequency smoother
|
||||||
m.def("freqSmooth", [](dpyarray freq, dpyarray X, unsigned w) {
|
m.def("freqSmooth", [](dpyarray freq, dpyarray X, unsigned w)
|
||||||
|
{
|
||||||
vd freqa = NpyToCol<d, false>(freq);
|
vd freqa = NpyToCol<d, false>(freq);
|
||||||
vd Xa = NpyToCol<d, false>(X);
|
vd Xa = NpyToCol<d, false>(X);
|
||||||
return ColToNpy(freqSmooth(freqa, Xa, w));
|
return ColToNpy(freqSmooth(freqa, Xa, w)); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -43,11 +43,22 @@ void init_siggen(py::module &m) {
|
|||||||
|
|
||||||
py::class_<Periodic, std::shared_ptr<Periodic>> periodic(m, "Periodic",
|
py::class_<Periodic, std::shared_ptr<Periodic>> periodic(m, "Periodic",
|
||||||
siggen);
|
siggen);
|
||||||
|
periodic.def("setA",
|
||||||
|
[](Periodic &p, const dpyarray A) {
|
||||||
|
p.setA(NpyToCol<d, false>(A));
|
||||||
|
});
|
||||||
|
|
||||||
periodic.def("getSequence",
|
periodic.def("getSequence",
|
||||||
[](const Sweep &s) { return ColToNpy<d>(s.getSequence()); });
|
[](const Sweep &s) { return ColToNpy<d>(s.getSequence()); });
|
||||||
|
|
||||||
|
periodic.def("getA",
|
||||||
|
[](const Sweep &s) { return ColToNpy<d>(s.getA()); });
|
||||||
|
|
||||||
|
|
||||||
py::class_<Sweep, std::shared_ptr<Sweep>> sweep(m, "Sweep", periodic);
|
py::class_<Sweep, std::shared_ptr<Sweep>> sweep(m, "Sweep", periodic);
|
||||||
sweep.def(py::init<const d, const d, const d, const d, const us>());
|
sweep.def(py::init<const d, const d, const d, const d, const us>());
|
||||||
|
sweep.def("getfn",
|
||||||
|
[](const Sweep &s) { return ColToNpy<d>(s.getfn()); });
|
||||||
sweep.def_readonly_static("ForwardSweep", &Sweep::ForwardSweep);
|
sweep.def_readonly_static("ForwardSweep", &Sweep::ForwardSweep);
|
||||||
sweep.def_readonly_static("BackwardSweep", &Sweep::BackwardSweep);
|
sweep.def_readonly_static("BackwardSweep", &Sweep::BackwardSweep);
|
||||||
sweep.def_readonly_static("LinearSweep", &Sweep::LinearSweep);
|
sweep.def_readonly_static("LinearSweep", &Sweep::LinearSweep);
|
||||||
|
@ -10,11 +10,11 @@ from .lasp_cpp import *
|
|||||||
|
|
||||||
# from .lasp_imptube import * # TwoMicImpedanceTube
|
# from .lasp_imptube import * # TwoMicImpedanceTube
|
||||||
from .lasp_measurement import * # Measurement, scaleBlockSens
|
from .lasp_measurement import * # Measurement, scaleBlockSens
|
||||||
from .lasp_octavefilter import * # OverallFilterBank, SosOctaveFilterBank, SosThirdOctaveFilterBank
|
from .lasp_octavefilter import *
|
||||||
from .lasp_slm import * # SLM, Dummy
|
from .lasp_slm import * # SLM, Dummy
|
||||||
from .lasp_record import * # RecordStatus, Recording
|
from .lasp_record import * # RecordStatus, Recording
|
||||||
from .lasp_daqconfigs import * # DaqConfigurations
|
from .lasp_daqconfigs import *
|
||||||
from .lasp_measurementset import * # MeasurementSet
|
from .lasp_measurementset import *
|
||||||
|
|
||||||
# from .lasp_siggen import * # SignalType, NoiseType, SiggenMessage, SiggenData, Siggen
|
# from .lasp_siggen import * # SignalType, NoiseType, SiggenMessage, SiggenData, Siggen
|
||||||
# from .lasp_weighcal import * # WeighCal
|
# from .lasp_weighcal import * # WeighCal
|
||||||
|
@ -15,10 +15,13 @@ from scipy.io import wavfile
|
|||||||
import os, time, wave, logging
|
import os, time, wave, logging
|
||||||
from .lasp_common import SIQtys, Qty, getFreq
|
from .lasp_common import SIQtys, Qty, getFreq
|
||||||
from .lasp_version import LASP_VERSION_MAJOR, LASP_VERSION_MINOR
|
from .lasp_version import LASP_VERSION_MAJOR, LASP_VERSION_MINOR
|
||||||
from .lasp_cpp import Window, DaqChannel, AvPowerSpectra
|
from .lasp_cpp import Window, DaqChannel, AvPowerSpectra, AvSweepPowerSpectra
|
||||||
from typing import List
|
from typing import List
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from .lasp_config import ones
|
from .lasp_config import ones
|
||||||
|
|
||||||
|
from scipy.interpolate import CubicSpline
|
||||||
|
|
||||||
"""!
|
"""!
|
||||||
Author: J.A. de Jong - ASCEE
|
Author: J.A. de Jong - ASCEE
|
||||||
|
|
||||||
@ -77,9 +80,9 @@ class MeasurementType(Enum):
|
|||||||
Measurement flags related to the measurement. Stored as bit flags in the measurement file. This is for possible changes in the API later.
|
Measurement flags related to the measurement. Stored as bit flags in the measurement file. This is for possible changes in the API later.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Not specific measurement type
|
# Not specific measurement type
|
||||||
NotSpecific = 0
|
NotSpecific = 0
|
||||||
|
|
||||||
# Measurement serves as an insertion loss reference measurement
|
# Measurement serves as an insertion loss reference measurement
|
||||||
ILReference = 1 << 0
|
ILReference = 1 << 0
|
||||||
|
|
||||||
@ -306,6 +309,16 @@ class Measurement:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
self._type_int = 0
|
self._type_int = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
dat = {}
|
||||||
|
|
||||||
|
for key in f['siggen']['Data'].attrs:
|
||||||
|
dat[key] = f['siggen']['Data'].attrs[key]
|
||||||
|
|
||||||
|
self._siggen = {'Type': f['siggen'].attrs["Type"], 'Data': dat}
|
||||||
|
except KeyError:
|
||||||
|
self._siggen = {'Type': 'Muted', 'Data': {}}
|
||||||
|
|
||||||
# Due to a previous bug, the channel names were not stored
|
# Due to a previous bug, the channel names were not stored
|
||||||
# consistently, i.e. as 'channel_names' and later camelcase.
|
# consistently, i.e. as 'channel_names' and later camelcase.
|
||||||
try:
|
try:
|
||||||
@ -571,11 +584,28 @@ class Measurement:
|
|||||||
"""
|
"""
|
||||||
self.setAttribute("type_int", type_.value)
|
self.setAttribute("type_int", type_.value)
|
||||||
|
|
||||||
|
def setSiggen(self, siggen_: dict):
|
||||||
|
"""
|
||||||
|
Set the signal generator data
|
||||||
|
"""
|
||||||
|
with self.file("r+") as f:
|
||||||
|
siggen_group = f.create_group("siggen", track_order=True)
|
||||||
|
siggen_group.attrs['Type'] = siggen_['Type']
|
||||||
|
data_group = siggen_group.create_group("Data", track_order=True)
|
||||||
|
for key in siggen_['Data'].keys():
|
||||||
|
data_group.attrs[key] = siggen_['Data'][key]
|
||||||
|
|
||||||
def measurementType(self):
|
def measurementType(self):
|
||||||
"""
|
"""
|
||||||
Returns type of measurement
|
Returns type of measurement
|
||||||
"""
|
"""
|
||||||
return MeasurementType(self._type_int)
|
return MeasurementType(self._type_int)
|
||||||
|
|
||||||
|
def signalGenerator(self):
|
||||||
|
"""
|
||||||
|
Returns signal generator data
|
||||||
|
"""
|
||||||
|
return self._siggen
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
@ -814,9 +844,16 @@ class Measurement:
|
|||||||
channels = list(range(self.nchannels))
|
channels = list(range(self.nchannels))
|
||||||
|
|
||||||
nchannels = len(channels)
|
nchannels = len(channels)
|
||||||
aps = AvPowerSpectra(nfft, window, overlap)
|
|
||||||
freq = getFreq(self.samplerate, nfft)
|
freq = getFreq(self.samplerate, nfft)
|
||||||
|
|
||||||
|
if self._siggen['Type'] == 'Sweep':
|
||||||
|
iA = CubicSpline(self._siggen['Data']['fA'], self._siggen['Data']['A'])
|
||||||
|
|
||||||
|
aps = AvSweepPowerSpectra(nfft, window, overlap, -1, iA(freq))
|
||||||
|
else:
|
||||||
|
aps = AvPowerSpectra(nfft, window, overlap)
|
||||||
|
|
||||||
for data in self.iterData(channels, **kwargs):
|
for data in self.iterData(channels, **kwargs):
|
||||||
CS = aps.compute(data)
|
CS = aps.compute(data)
|
||||||
|
|
||||||
@ -1005,7 +1042,8 @@ class Measurement:
|
|||||||
|
|
||||||
# Convert range to [-1, 1]
|
# Convert range to [-1, 1]
|
||||||
# TODO: this is wrong for float data where full scale > 1
|
# TODO: this is wrong for float data where full scale > 1
|
||||||
sensone = np.ones_like(self.sensitivity)
|
sensone = np.ones(data.shape[1])
|
||||||
|
|
||||||
data = scaleBlockSens(data, sensone)
|
data = scaleBlockSens(data, sensone)
|
||||||
|
|
||||||
if dtype == "int16" or dtype == "int32":
|
if dtype == "int16" or dtype == "int32":
|
||||||
@ -1156,8 +1194,11 @@ class Measurement:
|
|||||||
if data.ndim != 2:
|
if data.ndim != 2:
|
||||||
data = data[:, np.newaxis]
|
data = data[:, np.newaxis]
|
||||||
|
|
||||||
if not (isinstance(sensitivity, np.ndarray) and sensitivity.ndim >= 1):
|
try:
|
||||||
sensitivity = np.asarray(sensitivity)[np.newaxis]
|
len(sensitivity)
|
||||||
|
except:
|
||||||
|
raise ValueError("Sensitivity should be given as array-like data type")
|
||||||
|
sensitivity = np.asarray(sensitivity)
|
||||||
|
|
||||||
nchannels = data.shape[1]
|
nchannels = data.shape[1]
|
||||||
if nchannels != sensitivity.shape[0]:
|
if nchannels != sensitivity.shape[0]:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user