PySDKit 0.4.46__tar.gz → 0.4.47__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pysdkit-0.4.46 → pysdkit-0.4.47}/PKG-INFO +2 -2
- {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/PKG-INFO +2 -2
- {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/SOURCES.txt +2 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/README.md +1 -1
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/__init__.py +7 -1
- pysdkit-0.4.47/pysdkit/_vncmd/__init__.py +44 -0
- pysdkit-0.4.47/pysdkit/_vncmd/stnbmd.py +515 -0
- pysdkit-0.4.47/pysdkit/tests/test_stnbmd.py +243 -0
- pysdkit-0.4.46/pysdkit/_vncmd/__init__.py +0 -10
- {pysdkit-0.4.46 → pysdkit-0.4.47}/LICENSE +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/dependency_links.txt +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/requires.txt +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/top_level.txt +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pyproject.toml +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/_find_extrema.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/_prepare_points.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/_splines.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/ceemdan.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/eemd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/efd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/emd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/esmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/hht/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/hht/frequency.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/hht/hht.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/memd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/remd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/semd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/tvf_emd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd2d/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd2d/bemd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd2d/bmemd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd2d/emd2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_ewt/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_ewt/ewt.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_ewt/ewt2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/extrema.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/faemd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/faemd2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/faemd3d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/filter.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_fmd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_fmd/fmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_fmd/nfmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/agncmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/gdmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/ivgnmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/vgnmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/apmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/data/gearbox_fault_snippet.npy +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/data/input_sig.npy +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/imd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_jmd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_jmd/jmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_jmd/mjmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_jmd/sjmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_lmd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_lmd/lmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_lmd/rlmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_osd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_osd/components.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_osd/osd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_osd/swd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/data/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/data/dual_signal_noise.npy +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/data/map2.npy +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/data/single_nsignal.npy +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/vtfmtd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/alif.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/data/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/data/prefixed_double_filter.npy +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/hvd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/itd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/iterative_filtering.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/ssa.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/acmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/avmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/ba_acmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/base.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/mvmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/ovmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/stvmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/svmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/vmd_c.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/vmd_f.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/vme.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd2d/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd2d/cvmd2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd2d/vmd2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vncmd/avncmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vncmd/incmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vncmd/vncmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_add_noise.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_cube.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_generator.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_image.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_models.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_test_univariate.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_time_series.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/fmd_demo.npy +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/texture.txt +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/vmd_example.npy +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/entropy/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/entropy/_permutation_entropy.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/entropy/_sample_entropy.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/_base.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/_kmeans.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/_knn.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/_pca.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_fourier_spectra.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_functions.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_plot_images.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_plot_imfs.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_plot_signal.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_cube.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_image.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_signal.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/models/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/models/test_knn.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/models/test_pca.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/run_all.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_agncmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_alif.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_apmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_avncmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_bmemd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ceemdan.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_cvmd2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_eemd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_emd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_esmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ewt.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ewt2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_faemd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_faemd2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_faemd3d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_fmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_gdmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_hvd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_imd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_itd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ivgnmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_jmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_lmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_moving_decomp.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_mstl.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_osd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ovmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_rlmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_semd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_sjmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ssa.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_stl.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_stvmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_svmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_swd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vgnmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vmd2d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vncmd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vtfmtd.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/_dtw.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/_moving_decomp.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/_mstl.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/_stl.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/__init__.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_correlation.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_diagnalization.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_differ.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_fft.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_function.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_hilbert.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_instantaneous.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_kernel_matrix.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_mirror.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_process.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_smooth1d.py +0 -0
- {pysdkit-0.4.46 → pysdkit-0.4.47}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PySDKit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.47
|
|
4
4
|
Summary: A Python library for signal decomposition algorithms with a unified interface.
|
|
5
5
|
Author: Whenxuan Wang, RuiZhe Wang, Rongkun Zhu, Kai Wu, Lei Wang, josefinez, Deeksha Manjunath, Yuan Feng, WenTong Zhao, JacktheFowler
|
|
6
6
|
Maintainer-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>
|
|
@@ -199,7 +199,7 @@ plot_IMFs(signal, IMFs) # per-channel panels
|
|
|
199
199
|
| [`MJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/mjmd.py) (Multivariate Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169393-multivariate-jump-plus-am-fm-mode-decomposition-mjmd?s_tid=prof_contriblnk) | ✔️ |
|
|
200
200
|
| [`SJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/sjmd.py) / `SMJMD` (Successive Jump and Mode Decomposition) | [[paper]](https://arxiv.org/abs/2504.08453) | [[code]]() | ✔️ |
|
|
201
201
|
| [`ESMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/esmd.py) (Extreme-Point Symmetric Mode Decomposition) | [[paper]](https://arxiv.org/abs/1303.6540) | [[code]](https://github.com/WuShichao/esmd) | ✔️ |
|
|
202
|
-
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) |
|
|
202
|
+
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) | ✔️ |
|
|
203
203
|
| [`VTFMTD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_tfa/vftmtd.py) (Variational TF Mode Tracking Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168426001179) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/183389-variational-time-frequency-mode-tracking-decomposition) | ✔️ |
|
|
204
204
|
| [`SWD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_osd/swd.py) (Swarm Decomposition) | [[paper]](https://doi.org/10.1016/j.sigpro.2016.09.004) | [[code]](https://github.com/gkaposto/Swarm-Decomposition) | ✔️ |
|
|
205
205
|
| [`OSD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_osd/osd.py) (Optimization-based Signal Decomposition) | [[paper]](https://web.stanford.edu/~boyd/papers/sig_decomp_mprox.html) | [[code]](https://github.com/cvxgrp/signal-decomposition) | ✔️ |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PySDKit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.47
|
|
4
4
|
Summary: A Python library for signal decomposition algorithms with a unified interface.
|
|
5
5
|
Author: Whenxuan Wang, RuiZhe Wang, Rongkun Zhu, Kai Wu, Lei Wang, josefinez, Deeksha Manjunath, Yuan Feng, WenTong Zhao, JacktheFowler
|
|
6
6
|
Maintainer-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>
|
|
@@ -199,7 +199,7 @@ plot_IMFs(signal, IMFs) # per-channel panels
|
|
|
199
199
|
| [`MJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/mjmd.py) (Multivariate Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169393-multivariate-jump-plus-am-fm-mode-decomposition-mjmd?s_tid=prof_contriblnk) | ✔️ |
|
|
200
200
|
| [`SJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/sjmd.py) / `SMJMD` (Successive Jump and Mode Decomposition) | [[paper]](https://arxiv.org/abs/2504.08453) | [[code]]() | ✔️ |
|
|
201
201
|
| [`ESMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/esmd.py) (Extreme-Point Symmetric Mode Decomposition) | [[paper]](https://arxiv.org/abs/1303.6540) | [[code]](https://github.com/WuShichao/esmd) | ✔️ |
|
|
202
|
-
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) |
|
|
202
|
+
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) | ✔️ |
|
|
203
203
|
| [`VTFMTD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_tfa/vftmtd.py) (Variational TF Mode Tracking Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168426001179) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/183389-variational-time-frequency-mode-tracking-decomposition) | ✔️ |
|
|
204
204
|
| [`SWD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_osd/swd.py) (Swarm Decomposition) | [[paper]](https://doi.org/10.1016/j.sigpro.2016.09.004) | [[code]](https://github.com/gkaposto/Swarm-Decomposition) | ✔️ |
|
|
205
205
|
| [`OSD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_osd/osd.py) (Optimization-based Signal Decomposition) | [[paper]](https://web.stanford.edu/~boyd/papers/sig_decomp_mprox.html) | [[code]](https://github.com/cvxgrp/signal-decomposition) | ✔️ |
|
|
@@ -92,6 +92,7 @@ pysdkit/_vmd2d/vmd2d.py
|
|
|
92
92
|
pysdkit/_vncmd/__init__.py
|
|
93
93
|
pysdkit/_vncmd/avncmd.py
|
|
94
94
|
pysdkit/_vncmd/incmd.py
|
|
95
|
+
pysdkit/_vncmd/stnbmd.py
|
|
95
96
|
pysdkit/_vncmd/vncmd.py
|
|
96
97
|
pysdkit/data/__init__.py
|
|
97
98
|
pysdkit/data/_add_noise.py
|
|
@@ -153,6 +154,7 @@ pysdkit/tests/test_semd.py
|
|
|
153
154
|
pysdkit/tests/test_sjmd.py
|
|
154
155
|
pysdkit/tests/test_ssa.py
|
|
155
156
|
pysdkit/tests/test_stl.py
|
|
157
|
+
pysdkit/tests/test_stnbmd.py
|
|
156
158
|
pysdkit/tests/test_stvmd.py
|
|
157
159
|
pysdkit/tests/test_svmd.py
|
|
158
160
|
pysdkit/tests/test_swd.py
|
|
@@ -146,7 +146,7 @@ plot_IMFs(signal, IMFs) # per-channel panels
|
|
|
146
146
|
| [`MJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/mjmd.py) (Multivariate Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169393-multivariate-jump-plus-am-fm-mode-decomposition-mjmd?s_tid=prof_contriblnk) | ✔️ |
|
|
147
147
|
| [`SJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/sjmd.py) / `SMJMD` (Successive Jump and Mode Decomposition) | [[paper]](https://arxiv.org/abs/2504.08453) | [[code]]() | ✔️ |
|
|
148
148
|
| [`ESMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_emd/esmd.py) (Extreme-Point Symmetric Mode Decomposition) | [[paper]](https://arxiv.org/abs/1303.6540) | [[code]](https://github.com/WuShichao/esmd) | ✔️ |
|
|
149
|
-
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) |
|
|
149
|
+
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) | ✔️ |
|
|
150
150
|
| [`VTFMTD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_tfa/vftmtd.py) (Variational TF Mode Tracking Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168426001179) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/183389-variational-time-frequency-mode-tracking-decomposition) | ✔️ |
|
|
151
151
|
| [`SWD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_osd/swd.py) (Swarm Decomposition) | [[paper]](https://doi.org/10.1016/j.sigpro.2016.09.004) | [[code]](https://github.com/gkaposto/Swarm-Decomposition) | ✔️ |
|
|
152
152
|
| [`OSD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_osd/osd.py) (Optimization-based Signal Decomposition) | [[paper]](https://web.stanford.edu/~boyd/papers/sig_decomp_mprox.html) | [[code]](https://github.com/cvxgrp/signal-decomposition) | ✔️ |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
A Python library for signal decomposition algorithms.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
__version__ = "0.4.
|
|
5
|
+
__version__ = "0.4.47"
|
|
6
6
|
|
|
7
7
|
# Empirical Mode Decomposition
|
|
8
8
|
from ._emd import EMD
|
|
@@ -109,6 +109,9 @@ from ._vncmd import INCMD
|
|
|
109
109
|
# Adaptive Variational Nonlinear Chirp Mode Decomposition
|
|
110
110
|
from ._vncmd import AVNCMD
|
|
111
111
|
|
|
112
|
+
# Short-Time Narrow-Banded Mode Decomposition
|
|
113
|
+
from ._vncmd import STNBMD, stnbmd
|
|
114
|
+
|
|
112
115
|
# Adaptive Local Iterative Filtering (time iterative decomposition)
|
|
113
116
|
from ._tid import ALIF
|
|
114
117
|
|
|
@@ -208,6 +211,7 @@ Compact VMD for 2D Image | CVMD2D
|
|
|
208
211
|
Variational Nonlinear Chirp Mode Decomposition | VNCMD
|
|
209
212
|
Iterative Nonlinear Chirp Mode Decomposition | INCMD
|
|
210
213
|
Adaptive Variational Nonlinear Chirp Mode Dec. | AVNCMD
|
|
214
|
+
Short-Time Narrow-Banded Mode Decomposition | STNBMD
|
|
211
215
|
Adaptive Local Iterative Filtering | ALIF
|
|
212
216
|
Variational TF Mode Tracking Decomposition | VTFMTD
|
|
213
217
|
Adaptive Polymorphic Mode Decomposition | APMD
|
|
@@ -276,6 +280,8 @@ __all__ = [
|
|
|
276
280
|
"VNCMD",
|
|
277
281
|
"INCMD",
|
|
278
282
|
"AVNCMD",
|
|
283
|
+
"STNBMD",
|
|
284
|
+
"stnbmd",
|
|
279
285
|
"ALIF",
|
|
280
286
|
"VTFMTD",
|
|
281
287
|
"vtfmtd",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Variational / iterative nonlinear chirp mode decomposition.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .vncmd import VNCMD
|
|
7
|
+
from .incmd import INCMD
|
|
8
|
+
from .avncmd import AVNCMD
|
|
9
|
+
from .stnbmd import (
|
|
10
|
+
STNBMD,
|
|
11
|
+
stnbmd,
|
|
12
|
+
stnbm_decomp_ig,
|
|
13
|
+
ps90,
|
|
14
|
+
ps90f,
|
|
15
|
+
analytic_signal,
|
|
16
|
+
first_difference_matrix,
|
|
17
|
+
second_difference_matrix,
|
|
18
|
+
build_smoothing_filters,
|
|
19
|
+
schedule_index,
|
|
20
|
+
fft_two_to_one,
|
|
21
|
+
instantaneous_frequency,
|
|
22
|
+
make_order_tracking_demo,
|
|
23
|
+
constant_frequency_init,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"VNCMD",
|
|
28
|
+
"INCMD",
|
|
29
|
+
"AVNCMD",
|
|
30
|
+
"STNBMD",
|
|
31
|
+
"stnbmd",
|
|
32
|
+
"stnbm_decomp_ig",
|
|
33
|
+
"ps90",
|
|
34
|
+
"ps90f",
|
|
35
|
+
"analytic_signal",
|
|
36
|
+
"first_difference_matrix",
|
|
37
|
+
"second_difference_matrix",
|
|
38
|
+
"build_smoothing_filters",
|
|
39
|
+
"schedule_index",
|
|
40
|
+
"fft_two_to_one",
|
|
41
|
+
"instantaneous_frequency",
|
|
42
|
+
"make_order_tracking_demo",
|
|
43
|
+
"constant_frequency_init",
|
|
44
|
+
]
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Short-Time Narrow-Banded Mode Decomposition (STNBMD).
|
|
4
|
+
|
|
5
|
+
McNeill, S.I. Decomposing a signal into short-time narrow-banded modes.
|
|
6
|
+
Journal of Sound and Vibration, 373:325–339, 2016.
|
|
7
|
+
https://doi.org/10.1016/j.jsv.2016.03.015
|
|
8
|
+
|
|
9
|
+
Faithful Python port of the MATLAB toolbox routines:
|
|
10
|
+
``ps90.m``, ``ps90f.m``, ``stnbm_decomp_ig.m``, ``fft_two2one.m``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Dict, List, Optional, Sequence, Tuple, Union
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
from scipy import sparse
|
|
19
|
+
from scipy.linalg import inv as dense_inv
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
ArrayLike = Union[np.ndarray, Sequence[float]]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Hilbert / 90-degree phase-shift helpers (MATLAB ps90.m / ps90f.m)
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def ps90f(x: np.ndarray, n: Optional[int] = None) -> np.ndarray:
|
|
31
|
+
"""
|
|
32
|
+
Shift phase of each column by 90 degrees in the frequency domain
|
|
33
|
+
(MATLAB ``ps90f``).
|
|
34
|
+
|
|
35
|
+
:param x: data matrix ``(nt, nch)`` (or 1-D vector)
|
|
36
|
+
:param n: FFT length; default ``nt``. Truncate / zero-pad as in MATLAB.
|
|
37
|
+
:return: phase-shifted real array with ``min(nt, n)`` rows
|
|
38
|
+
"""
|
|
39
|
+
x = np.asarray(x)
|
|
40
|
+
if x.ndim == 1:
|
|
41
|
+
x = x.reshape(-1, 1)
|
|
42
|
+
squeeze = True
|
|
43
|
+
else:
|
|
44
|
+
squeeze = False
|
|
45
|
+
|
|
46
|
+
nt, nch = x.shape
|
|
47
|
+
if n is None:
|
|
48
|
+
n = nt
|
|
49
|
+
n = int(n)
|
|
50
|
+
|
|
51
|
+
if n % 2 == 1: # odd
|
|
52
|
+
nlast = (n + 1) // 2
|
|
53
|
+
i2 = np.arange(nlast, 1, -1) # nlast:-1:2 (1-based) -> 0-based nlast-1 .. 1
|
|
54
|
+
else:
|
|
55
|
+
nlast = n // 2 + 1
|
|
56
|
+
i2 = np.arange(nlast - 1, 1, -1) # nlast-1:-1:2
|
|
57
|
+
|
|
58
|
+
tmp = np.fft.fft(x, n=n, axis=0)
|
|
59
|
+
tmp = tmp[:nlast, :].copy()
|
|
60
|
+
tmp2 = np.zeros((n, nch), dtype=np.complex128)
|
|
61
|
+
tmp2[:nlast, :] = -1j * tmp
|
|
62
|
+
# append conjugate spectrum for negative frequencies
|
|
63
|
+
# MATLAB: tmp2(i1,:)=conj(tmp2(i2,:)) with i1 = nlast+1:n
|
|
64
|
+
i1 = np.arange(nlast, n) # 0-based rows nlast .. n-1
|
|
65
|
+
# i2 is 1-based indices into tmp2 after assigning first nlast rows
|
|
66
|
+
i2_0 = i2 - 1 # convert MATLAB 1-based to 0-based
|
|
67
|
+
tmp2[i1, :] = np.conj(tmp2[i2_0, :])
|
|
68
|
+
|
|
69
|
+
y = np.real(np.fft.ifft(tmp2, n=n, axis=0))
|
|
70
|
+
y = y[: min(n, nt), :]
|
|
71
|
+
if squeeze:
|
|
72
|
+
return y[:, 0]
|
|
73
|
+
return y
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def ps90(x: np.ndarray) -> np.ndarray:
|
|
77
|
+
"""
|
|
78
|
+
90-degree phase shift via mirroring + ``ps90f`` (MATLAB ``ps90``).
|
|
79
|
+
|
|
80
|
+
Constructs ``xm = [x; -flipud(x)]``, applies ``ps90f``, then returns the
|
|
81
|
+
first ``nt`` samples.
|
|
82
|
+
"""
|
|
83
|
+
x = np.asarray(x, dtype=float)
|
|
84
|
+
squeeze = False
|
|
85
|
+
if x.ndim == 1:
|
|
86
|
+
x = x.reshape(-1, 1)
|
|
87
|
+
squeeze = True
|
|
88
|
+
nt = x.shape[0]
|
|
89
|
+
tmp = -np.flipud(x)
|
|
90
|
+
xm = np.vstack([x, tmp])
|
|
91
|
+
xm90 = ps90f(xm)
|
|
92
|
+
y = xm90[:nt, :]
|
|
93
|
+
if squeeze:
|
|
94
|
+
return y[:, 0]
|
|
95
|
+
return y
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def analytic_signal(x: np.ndarray) -> np.ndarray:
|
|
99
|
+
"""Form the analytic signal ``x + 1j * ps90(x)`` as in ``stnbm_decomp_ig``."""
|
|
100
|
+
x = np.asarray(x, dtype=float).ravel()
|
|
101
|
+
return x + 1j * ps90(x)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ---------------------------------------------------------------------------
|
|
105
|
+
# Difference operators and smoothing filters
|
|
106
|
+
# ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def first_difference_matrix(nt: int, fs: float) -> sparse.csc_matrix:
|
|
110
|
+
"""
|
|
111
|
+
MATLAB ``D1 = fs * spdiags([-tmp, tmp], [0, 1], nt-1, nt)``.
|
|
112
|
+
|
|
113
|
+
Shape ``(nt-1, nt)``; ``(D1 x)_i = fs * (x_{i+1} - x_i)``.
|
|
114
|
+
"""
|
|
115
|
+
nt = int(nt)
|
|
116
|
+
fs = float(fs)
|
|
117
|
+
# diagonals 0 and 1 of an (nt-1) x nt matrix
|
|
118
|
+
d0 = -fs * np.ones(nt - 1)
|
|
119
|
+
d1 = fs * np.ones(nt - 1)
|
|
120
|
+
return sparse.diags([d0, d1], [0, 1], shape=(nt - 1, nt), format="csc")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def second_difference_matrix(nt: int, fs: float) -> sparse.csc_matrix:
|
|
124
|
+
"""
|
|
125
|
+
MATLAB ``D2 = fs^2 * spdiags([tmp, -2*tmp, tmp], [0, 1, 2], nt-2, nt)``.
|
|
126
|
+
|
|
127
|
+
Shape ``(nt-2, nt)``; second forward difference scaled by ``fs^2``.
|
|
128
|
+
"""
|
|
129
|
+
nt = int(nt)
|
|
130
|
+
fs2 = float(fs) ** 2
|
|
131
|
+
d0 = fs2 * np.ones(nt - 2)
|
|
132
|
+
d1 = -2.0 * fs2 * np.ones(nt - 2)
|
|
133
|
+
d2 = fs2 * np.ones(nt - 2)
|
|
134
|
+
return sparse.diags([d0, d1, d2], [0, 1, 2], shape=(nt - 2, nt), format="csc")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def build_smoothing_filters(
|
|
138
|
+
nt: int,
|
|
139
|
+
fs: float,
|
|
140
|
+
alpha: np.ndarray,
|
|
141
|
+
beta: np.ndarray,
|
|
142
|
+
) -> Tuple[List[np.ndarray], List[np.ndarray]]:
|
|
143
|
+
"""
|
|
144
|
+
Precompute dense filters
|
|
145
|
+
``F1{ii} = inv(alpha(ii)*Q1 + I)``, ``F2{ii} = inv(beta(ii)*Q2 + I)``
|
|
146
|
+
exactly as in MATLAB ``stnbm_decomp_ig``.
|
|
147
|
+
"""
|
|
148
|
+
nt = int(nt)
|
|
149
|
+
alpha = np.asarray(alpha, dtype=float).ravel()
|
|
150
|
+
beta = np.asarray(beta, dtype=float).ravel()
|
|
151
|
+
if alpha.size != beta.size:
|
|
152
|
+
raise ValueError("alpha and beta must have the same length")
|
|
153
|
+
|
|
154
|
+
d1 = first_difference_matrix(nt, fs)
|
|
155
|
+
q1 = (d1.T @ d1).toarray()
|
|
156
|
+
d2 = second_difference_matrix(nt, fs)
|
|
157
|
+
q2 = (d2.T @ d2).toarray()
|
|
158
|
+
eye = np.eye(nt)
|
|
159
|
+
|
|
160
|
+
f1_list: List[np.ndarray] = []
|
|
161
|
+
f2_list: List[np.ndarray] = []
|
|
162
|
+
for a, b in zip(alpha, beta):
|
|
163
|
+
f1_list.append(dense_inv(a * q1 + eye))
|
|
164
|
+
f2_list.append(dense_inv(b * q2 + eye))
|
|
165
|
+
return f1_list, f2_list
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def schedule_index(iteration: int, abitr: np.ndarray) -> int:
|
|
169
|
+
"""
|
|
170
|
+
MATLAB ``ip = min(find(itr - abitr <= 0))`` (1-based) → 0-based index.
|
|
171
|
+
|
|
172
|
+
Selects which ``(alpha, beta)`` pair is active at the current iteration.
|
|
173
|
+
"""
|
|
174
|
+
abitr = np.asarray(abitr, dtype=float).ravel()
|
|
175
|
+
tmp = float(iteration) - abitr
|
|
176
|
+
hits = np.where(tmp <= 0)[0]
|
|
177
|
+
if hits.size == 0:
|
|
178
|
+
return int(abitr.size - 1)
|
|
179
|
+
return int(hits[0])
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# ---------------------------------------------------------------------------
|
|
183
|
+
# Spectral helper (MATLAB fft_two2one.m)
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def fft_two_to_one(
|
|
188
|
+
xf2: np.ndarray,
|
|
189
|
+
fs: float,
|
|
190
|
+
nt: Optional[int] = None,
|
|
191
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
192
|
+
"""
|
|
193
|
+
Convert two-sided unscaled ``fft`` output to one-sided scaled spectrum
|
|
194
|
+
(MATLAB ``fft_two2one``).
|
|
195
|
+
|
|
196
|
+
:param xf2: ``(nf2, nch)`` two-sided FFT
|
|
197
|
+
:param fs: sampling rate [Hz]
|
|
198
|
+
:param nt: original time-series length (default ``nf2``)
|
|
199
|
+
:return: ``(xf1, f1)`` one-sided spectrum and frequency axis
|
|
200
|
+
"""
|
|
201
|
+
xf2 = np.asarray(xf2)
|
|
202
|
+
squeeze = False
|
|
203
|
+
if xf2.ndim == 1:
|
|
204
|
+
xf2 = xf2.reshape(-1, 1)
|
|
205
|
+
squeeze = True
|
|
206
|
+
nf2, _nch = xf2.shape
|
|
207
|
+
if nt is None:
|
|
208
|
+
nt = nf2
|
|
209
|
+
nt = int(nt)
|
|
210
|
+
fs = float(fs)
|
|
211
|
+
|
|
212
|
+
if nf2 % 2 == 1:
|
|
213
|
+
nlast = (nf2 + 1) // 2
|
|
214
|
+
xf1 = (2.0 / nf2) * np.sqrt(nf2 / nt) * xf2[:nlast, :].copy()
|
|
215
|
+
xf1[0, :] = xf2[0, :] / nf2 * np.sqrt(nf2 / nt)
|
|
216
|
+
else:
|
|
217
|
+
nlast = nf2 // 2 + 1
|
|
218
|
+
xf1 = (2.0 / nf2) * np.sqrt(nf2 / nt) * xf2[:nlast, :].copy()
|
|
219
|
+
scale0 = 1.0 / nf2 * np.sqrt(nf2 / nt)
|
|
220
|
+
xf1[0, :] = xf2[0, :] * scale0
|
|
221
|
+
xf1[nlast - 1, :] = xf2[nlast - 1, :] * scale0
|
|
222
|
+
|
|
223
|
+
f1 = np.arange(nlast, dtype=float) * (fs / nf2)
|
|
224
|
+
if squeeze:
|
|
225
|
+
return xf1[:, 0], f1
|
|
226
|
+
return xf1, f1
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def instantaneous_frequency(
|
|
230
|
+
phz: np.ndarray, fs: float
|
|
231
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
232
|
+
"""
|
|
233
|
+
Estimate IF from unwrapped phase as in the MATLAB demo:
|
|
234
|
+
``ifrq = fs/2/pi * diff(phz)``.
|
|
235
|
+
|
|
236
|
+
:return: ``(tfrq_midpoints_unitless_diff_index, ifrq_hz)`` — caller usually
|
|
237
|
+
builds time as ``(t[:-1] + t[1:]) / 2``.
|
|
238
|
+
"""
|
|
239
|
+
phz = np.asarray(phz, dtype=float)
|
|
240
|
+
if phz.ndim == 1:
|
|
241
|
+
phz = phz.reshape(-1, 1)
|
|
242
|
+
squeeze = True
|
|
243
|
+
else:
|
|
244
|
+
squeeze = False
|
|
245
|
+
ifrq = (float(fs) / (2.0 * np.pi)) * np.diff(phz, axis=0)
|
|
246
|
+
if squeeze:
|
|
247
|
+
return ifrq[:, 0]
|
|
248
|
+
return ifrq
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
# Demo signal factory (MATLAB example/test_ord_trk_stnbmd.m)
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def make_order_tracking_demo(
|
|
257
|
+
fs: float = 100.0,
|
|
258
|
+
nt: int = 1000,
|
|
259
|
+
) -> Dict[str, np.ndarray]:
|
|
260
|
+
"""
|
|
261
|
+
Reproduce the synthetic crossing-order demo from
|
|
262
|
+
``example/test_ord_trk_stnbmd.m`` (first three upsweep harmonics).
|
|
263
|
+
"""
|
|
264
|
+
fs = float(fs)
|
|
265
|
+
nt = int(nt)
|
|
266
|
+
t = np.arange(nt, dtype=float) / fs
|
|
267
|
+
|
|
268
|
+
f0, f1 = 1.5, 10.0 / 2.15
|
|
269
|
+
opm = np.log2(f1 / f0) * 60.0 / t[-1]
|
|
270
|
+
fup = f0 * 2.0 ** (opm / 60.0 * t)
|
|
271
|
+
|
|
272
|
+
f = 0.75 / t[-1]
|
|
273
|
+
am1 = 0.25 * np.sin(2 * np.pi * f * t - np.pi / 4) + 0.75
|
|
274
|
+
am2 = 0.25 * np.sin(2 * np.pi * f * t - np.pi / 1.7) + 0.75
|
|
275
|
+
am3 = 0.25 * np.sin(2 * np.pi * f * t - np.pi / 8) + 0.75
|
|
276
|
+
|
|
277
|
+
x1 = am1 * np.sin(1 * 2 * np.pi * np.cumsum(fup / fs))
|
|
278
|
+
x2 = am2 * np.sin(2 * 2 * np.pi * np.cumsum(fup / fs))
|
|
279
|
+
x3 = am3 * np.sin(3 * 2 * np.pi * np.cumsum(fup / fs))
|
|
280
|
+
x1 = x1 / np.std(x1)
|
|
281
|
+
x2 = x2 / np.std(x2)
|
|
282
|
+
x3 = x3 / np.std(x3)
|
|
283
|
+
|
|
284
|
+
fp = np.column_stack([fup, 2 * fup, 3 * fup])
|
|
285
|
+
xp = np.column_stack([x1, x2, x3])
|
|
286
|
+
xe = np.sum(xp, axis=1)
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
"t": t,
|
|
290
|
+
"fs": np.asarray(fs),
|
|
291
|
+
"signal": xe,
|
|
292
|
+
"modes": xp,
|
|
293
|
+
"true_if": fp,
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def constant_frequency_init(
|
|
298
|
+
nt: int,
|
|
299
|
+
fs: float,
|
|
300
|
+
frequencies: ArrayLike,
|
|
301
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
302
|
+
"""
|
|
303
|
+
Unit-amplitude constant-frequency initial guess used in the MATLAB demo:
|
|
304
|
+
``ampg = 1``, ``phzg(:,ii) = 2*pi*fg(ii)*t``.
|
|
305
|
+
"""
|
|
306
|
+
nt = int(nt)
|
|
307
|
+
fs = float(fs)
|
|
308
|
+
freqs = np.asarray(frequencies, dtype=float).ravel()
|
|
309
|
+
t = np.arange(nt, dtype=float) / fs
|
|
310
|
+
nnb = freqs.size
|
|
311
|
+
ampg = np.ones((nt, nnb), dtype=np.complex128)
|
|
312
|
+
phzg = np.zeros((nt, nnb), dtype=float)
|
|
313
|
+
for i, f0 in enumerate(freqs):
|
|
314
|
+
phzg[:, i] = 2.0 * np.pi * f0 * t
|
|
315
|
+
return ampg, phzg
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
# ---------------------------------------------------------------------------
|
|
319
|
+
# Core algorithm (MATLAB stnbm_decomp_ig.m)
|
|
320
|
+
# ---------------------------------------------------------------------------
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def stnbm_decomp_ig(
|
|
324
|
+
x: np.ndarray,
|
|
325
|
+
fs: float,
|
|
326
|
+
ampg: np.ndarray,
|
|
327
|
+
phzg: np.ndarray,
|
|
328
|
+
alpha: ArrayLike,
|
|
329
|
+
beta: ArrayLike,
|
|
330
|
+
abitr: ArrayLike,
|
|
331
|
+
tol: float,
|
|
332
|
+
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
333
|
+
"""
|
|
334
|
+
Decompose a real signal into short-time narrow-banded analytic modes
|
|
335
|
+
(MATLAB ``stnbm_decomp_ig``).
|
|
336
|
+
|
|
337
|
+
:param x: real input vector ``(nt,)``
|
|
338
|
+
:param fs: sampling rate [Hz]
|
|
339
|
+
:param ampg: initial complex amplitudes ``(nt, nnb)``
|
|
340
|
+
:param phzg: initial real phases [rad] ``(nt, nnb)``
|
|
341
|
+
:param alpha: amplitude-smoothness weights (schedule)
|
|
342
|
+
:param beta: frequency-smoothness weights (schedule)
|
|
343
|
+
:param abitr: iteration breakpoints; ``abitr[-1]`` is ``maxitr``
|
|
344
|
+
:param tol: relative change tolerance
|
|
345
|
+
:return: ``(xnb, err, amp, phz)``
|
|
346
|
+
"""
|
|
347
|
+
x = np.asarray(x, dtype=float).ravel()
|
|
348
|
+
nt = x.size
|
|
349
|
+
ampg = np.asarray(ampg, dtype=np.complex128)
|
|
350
|
+
phzg = np.asarray(phzg, dtype=float)
|
|
351
|
+
if ampg.ndim != 2 or ampg.shape[0] != nt:
|
|
352
|
+
raise ValueError("ampg must have shape (len(x), nnb)")
|
|
353
|
+
if phzg.shape != ampg.shape:
|
|
354
|
+
raise ValueError("phzg must have the same shape as ampg")
|
|
355
|
+
|
|
356
|
+
alpha = np.asarray(alpha, dtype=float).ravel()
|
|
357
|
+
beta = np.asarray(beta, dtype=float).ravel()
|
|
358
|
+
abitr = np.asarray(abitr, dtype=float).ravel()
|
|
359
|
+
if not (alpha.shape == beta.shape == abitr.shape):
|
|
360
|
+
raise ValueError("alpha, beta and abitr must have the same shape")
|
|
361
|
+
if alpha.size < 1:
|
|
362
|
+
raise ValueError("alpha/beta/abitr must be non-empty")
|
|
363
|
+
|
|
364
|
+
# analytic signal
|
|
365
|
+
x_an = analytic_signal(x)
|
|
366
|
+
|
|
367
|
+
phz = phzg.astype(float, copy=True)
|
|
368
|
+
amp = ampg.astype(np.complex128, copy=True)
|
|
369
|
+
xnb = amp * np.exp(1j * phz)
|
|
370
|
+
nnb = ampg.shape[1]
|
|
371
|
+
|
|
372
|
+
f1_list, f2_list = build_smoothing_filters(nt, fs, alpha, beta)
|
|
373
|
+
|
|
374
|
+
maxitr = int(abitr[-1])
|
|
375
|
+
err = np.zeros(maxitr, dtype=float)
|
|
376
|
+
tol = float(tol)
|
|
377
|
+
|
|
378
|
+
for itr in range(1, maxitr + 1):
|
|
379
|
+
ip = schedule_index(itr, abitr)
|
|
380
|
+
f1 = f1_list[ip]
|
|
381
|
+
f2 = f2_list[ip]
|
|
382
|
+
|
|
383
|
+
for kk in range(nnb):
|
|
384
|
+
xnbk_old = xnb[:, kk].copy()
|
|
385
|
+
nonk = [i for i in range(nnb) if i != kk]
|
|
386
|
+
|
|
387
|
+
# update amplitude
|
|
388
|
+
if nonk:
|
|
389
|
+
xnbk_hat = x_an - np.sum(xnb[:, nonk], axis=1)
|
|
390
|
+
else:
|
|
391
|
+
xnbk_hat = x_an.copy()
|
|
392
|
+
amp[:, kk] = f1 @ (xnbk_hat * np.exp(-1j * phz[:, kk]))
|
|
393
|
+
|
|
394
|
+
# update phase
|
|
395
|
+
xnbk_hat = amp[:, kk] * np.exp(1j * phz[:, kk])
|
|
396
|
+
phz[:, kk] = f2 @ np.unwrap(np.angle(xnbk_hat))
|
|
397
|
+
xnb[:, kk] = amp[:, kk] * np.exp(1j * phz[:, kk])
|
|
398
|
+
|
|
399
|
+
chngk = xnb[:, kk] - xnbk_old
|
|
400
|
+
denom = np.vdot(xnbk_old, xnbk_old)
|
|
401
|
+
if abs(denom) < np.finfo(float).eps:
|
|
402
|
+
continue
|
|
403
|
+
err[itr - 1] += float(np.real(np.vdot(chngk, chngk) / denom))
|
|
404
|
+
|
|
405
|
+
if itr == maxitr or err[itr - 1] < tol:
|
|
406
|
+
err = err[:itr]
|
|
407
|
+
break
|
|
408
|
+
|
|
409
|
+
return xnb, err, amp, phz
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def stnbmd(
|
|
413
|
+
signal: np.ndarray,
|
|
414
|
+
fs: float,
|
|
415
|
+
frequencies: Optional[ArrayLike] = None,
|
|
416
|
+
ampg: Optional[np.ndarray] = None,
|
|
417
|
+
phzg: Optional[np.ndarray] = None,
|
|
418
|
+
alpha: ArrayLike = (1e-1, 1e-2, 1e-2),
|
|
419
|
+
beta: ArrayLike = (1.0, 1e-1, 1e-3),
|
|
420
|
+
abitr: ArrayLike = (20, 50, 200),
|
|
421
|
+
tol: float = 1e-6,
|
|
422
|
+
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
423
|
+
"""
|
|
424
|
+
Functional STNBMD interface.
|
|
425
|
+
|
|
426
|
+
Provide either constant-frequency centres ``frequencies`` (demo-style
|
|
427
|
+
initialisation) or explicit ``ampg`` / ``phzg`` guesses.
|
|
428
|
+
"""
|
|
429
|
+
x = np.asarray(signal, dtype=float).ravel()
|
|
430
|
+
if ampg is None or phzg is None:
|
|
431
|
+
if frequencies is None:
|
|
432
|
+
raise ValueError("Provide frequencies=... or both ampg and phzg")
|
|
433
|
+
ampg, phzg = constant_frequency_init(x.size, fs, frequencies)
|
|
434
|
+
return stnbm_decomp_ig(
|
|
435
|
+
x, fs, ampg, phzg, alpha=alpha, beta=beta, abitr=abitr, tol=tol
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
class STNBMD(object):
|
|
440
|
+
"""
|
|
441
|
+
Short-Time Narrow-Banded Mode Decomposition.
|
|
442
|
+
|
|
443
|
+
McNeill, J. Sound Vib. 373:325–339, 2016.
|
|
444
|
+
"""
|
|
445
|
+
|
|
446
|
+
def __init__(
|
|
447
|
+
self,
|
|
448
|
+
fs: float = 100.0,
|
|
449
|
+
alpha: ArrayLike = (1e-1, 1e-2, 1e-2),
|
|
450
|
+
beta: ArrayLike = (1.0, 1e-1, 1e-3),
|
|
451
|
+
abitr: ArrayLike = (20, 50, 200),
|
|
452
|
+
tol: float = 1e-6,
|
|
453
|
+
) -> None:
|
|
454
|
+
self.fs = float(fs)
|
|
455
|
+
self.alpha = np.asarray(alpha, dtype=float).ravel()
|
|
456
|
+
self.beta = np.asarray(beta, dtype=float).ravel()
|
|
457
|
+
self.abitr = np.asarray(abitr, dtype=float).ravel()
|
|
458
|
+
self.tol = float(tol)
|
|
459
|
+
|
|
460
|
+
self.signal: Optional[np.ndarray] = None
|
|
461
|
+
self.xnb: Optional[np.ndarray] = None
|
|
462
|
+
self.err: Optional[np.ndarray] = None
|
|
463
|
+
self.amp: Optional[np.ndarray] = None
|
|
464
|
+
self.phz: Optional[np.ndarray] = None
|
|
465
|
+
|
|
466
|
+
def __str__(self) -> str:
|
|
467
|
+
return "Short-Time Narrow-Banded Mode Decomposition (STNBMD)"
|
|
468
|
+
|
|
469
|
+
def __call__(self, signal: np.ndarray, **kwargs):
|
|
470
|
+
return self.fit_transform(signal, **kwargs)
|
|
471
|
+
|
|
472
|
+
def fit_transform(
|
|
473
|
+
self,
|
|
474
|
+
signal: np.ndarray,
|
|
475
|
+
frequencies: Optional[ArrayLike] = None,
|
|
476
|
+
ampg: Optional[np.ndarray] = None,
|
|
477
|
+
phzg: Optional[np.ndarray] = None,
|
|
478
|
+
return_all: bool = False,
|
|
479
|
+
):
|
|
480
|
+
"""
|
|
481
|
+
Run STNBMD.
|
|
482
|
+
|
|
483
|
+
:param signal: real 1-D input
|
|
484
|
+
:param frequencies: optional constant IF centres for initialisation
|
|
485
|
+
:param ampg / phzg: optional explicit initial amplitude / phase
|
|
486
|
+
:param return_all: if True, return ``(modes_real, xnb, err, amp, phz)``
|
|
487
|
+
:return: real modes ``(K, nt)`` by default (rows), matching PySDKit style
|
|
488
|
+
"""
|
|
489
|
+
xnb, err, amp, phz = stnbmd(
|
|
490
|
+
signal=signal,
|
|
491
|
+
fs=self.fs,
|
|
492
|
+
frequencies=frequencies,
|
|
493
|
+
ampg=ampg,
|
|
494
|
+
phzg=phzg,
|
|
495
|
+
alpha=self.alpha,
|
|
496
|
+
beta=self.beta,
|
|
497
|
+
abitr=self.abitr,
|
|
498
|
+
tol=self.tol,
|
|
499
|
+
)
|
|
500
|
+
self.signal = np.asarray(signal, dtype=float).ravel()
|
|
501
|
+
self.xnb = xnb
|
|
502
|
+
self.err = err
|
|
503
|
+
self.amp = amp
|
|
504
|
+
self.phz = phz
|
|
505
|
+
|
|
506
|
+
modes = np.real(xnb).T # (K, nt)
|
|
507
|
+
if return_all:
|
|
508
|
+
return modes, xnb, err, amp, phz
|
|
509
|
+
return modes
|
|
510
|
+
|
|
511
|
+
def instantaneous_frequency_hz(self) -> np.ndarray:
|
|
512
|
+
"""IF in Hz with shape ``(nt-1, K)`` from stored phase."""
|
|
513
|
+
if self.phz is None:
|
|
514
|
+
raise ValueError("Call fit_transform before requesting IF.")
|
|
515
|
+
return instantaneous_frequency(self.phz, self.fs)
|