PySDKit 0.4.45__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.45 → pysdkit-0.4.47}/PKG-INFO +3 -2
- {pysdkit-0.4.45 → pysdkit-0.4.47}/PySDKit.egg-info/PKG-INFO +3 -2
- {pysdkit-0.4.45 → pysdkit-0.4.47}/PySDKit.egg-info/SOURCES.txt +9 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/README.md +2 -1
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pyproject.toml +2 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/__init__.py +14 -2
- pysdkit-0.4.47/pysdkit/_tfa/__init__.py +41 -0
- pysdkit-0.4.47/pysdkit/_tfa/data/__init__.py +2 -0
- pysdkit-0.4.47/pysdkit/_tfa/data/dual_signal_noise.npy +0 -0
- pysdkit-0.4.47/pysdkit/_tfa/data/map2.npy +0 -0
- pysdkit-0.4.47/pysdkit/_tfa/data/single_nsignal.npy +0 -0
- pysdkit-0.4.47/pysdkit/_tfa/vtfmtd.py +439 -0
- 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.47/pysdkit/tests/test_vtfmtd.py +273 -0
- pysdkit-0.4.45/pysdkit/_vncmd/__init__.py +0 -10
- {pysdkit-0.4.45 → pysdkit-0.4.47}/LICENSE +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/PySDKit.egg-info/dependency_links.txt +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/PySDKit.egg-info/requires.txt +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/PySDKit.egg-info/top_level.txt +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/_find_extrema.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/_prepare_points.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/_splines.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/ceemdan.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/eemd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/efd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/emd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/esmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/hht/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/hht/frequency.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/hht/hht.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/memd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/remd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/semd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd/tvf_emd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd2d/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd2d/bemd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd2d/bmemd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_emd2d/emd2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_ewt/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_ewt/ewt.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_ewt/ewt2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_faemd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_faemd/extrema.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_faemd/faemd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_faemd/faemd2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_faemd/faemd3d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_faemd/filter.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_fmd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_fmd/fmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_fmd/nfmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_gdmd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_gdmd/agncmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_gdmd/gdmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_gdmd/ivgnmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_gdmd/vgnmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_imd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_imd/apmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_imd/data/gearbox_fault_snippet.npy +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_imd/data/input_sig.npy +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_imd/imd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_jmd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_jmd/jmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_jmd/mjmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_jmd/sjmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_lmd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_lmd/lmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_lmd/rlmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_osd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_osd/components.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_osd/osd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_osd/swd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_tid/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_tid/alif.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_tid/data/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_tid/data/prefixed_double_filter.npy +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_tid/hvd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_tid/itd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_tid/iterative_filtering.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_tid/ssa.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/acmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/avmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/ba_acmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/base.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/mvmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/ovmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/stvmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/svmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/vmd_c.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/vmd_f.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd/vme.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd2d/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd2d/cvmd2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vmd2d/vmd2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vncmd/avncmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vncmd/incmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/_vncmd/vncmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/_add_noise.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/_cube.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/_generator.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/_image.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/_models.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/_test_univariate.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/_time_series.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/fmd_demo.npy +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/texture.txt +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/data/vmd_example.npy +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/entropy/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/entropy/_permutation_entropy.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/entropy/_sample_entropy.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/models/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/models/_base.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/models/_kmeans.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/models/_knn.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/models/_pca.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/plot/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/plot/_fourier_spectra.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/plot/_functions.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/plot/_plot_images.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/plot/_plot_imfs.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/plot/_plot_signal.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/data/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_cube.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_image.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_signal.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/models/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/models/test_knn.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/models/test_pca.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/run_all.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_agncmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_alif.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_apmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_avncmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_bmemd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_ceemdan.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_cvmd2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_eemd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_emd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_esmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_ewt.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_ewt2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_faemd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_faemd2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_faemd3d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_fmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_gdmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_hvd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_imd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_itd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_ivgnmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_jmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_lmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_moving_decomp.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_mstl.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_osd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_ovmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_rlmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_semd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_sjmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_ssa.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_stl.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_stvmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_svmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_swd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_vgnmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_vmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_vmd2d.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tests/test_vncmd.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tsa/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tsa/_dtw.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tsa/_moving_decomp.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tsa/_mstl.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/tsa/_stl.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/__init__.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_correlation.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_diagnalization.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_differ.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_fft.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_function.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_hilbert.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_instantaneous.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_kernel_matrix.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_mirror.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_process.py +0 -0
- {pysdkit-0.4.45 → pysdkit-0.4.47}/pysdkit/utils/_smooth1d.py +0 -0
- {pysdkit-0.4.45 → 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,8 @@ 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
|
+
| [`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) | ✔️ |
|
|
203
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) | ✔️ |
|
|
204
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) | ✔️ |
|
|
205
206
|
| [`STL`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/tsa/_stl.py) (Seasonal-Trend decomposition using LOESS) | [[paper]](https://www.nniiem.ru/file/news/2016/stl-statistical-model.pdf) | [[code]](https://www.statsmodels.org/stable/examples/notebooks/generated/stl_decomposition.html) | ✔️ |
|
|
@@ -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,8 @@ 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
|
+
| [`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) | ✔️ |
|
|
203
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) | ✔️ |
|
|
204
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) | ✔️ |
|
|
205
206
|
| [`STL`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/tsa/_stl.py) (Seasonal-Trend decomposition using LOESS) | [[paper]](https://www.nniiem.ru/file/news/2016/stl-statistical-model.pdf) | [[code]](https://www.statsmodels.org/stable/examples/notebooks/generated/stl_decomposition.html) | ✔️ |
|
|
@@ -60,6 +60,12 @@ pysdkit/_osd/__init__.py
|
|
|
60
60
|
pysdkit/_osd/components.py
|
|
61
61
|
pysdkit/_osd/osd.py
|
|
62
62
|
pysdkit/_osd/swd.py
|
|
63
|
+
pysdkit/_tfa/__init__.py
|
|
64
|
+
pysdkit/_tfa/vtfmtd.py
|
|
65
|
+
pysdkit/_tfa/data/__init__.py
|
|
66
|
+
pysdkit/_tfa/data/dual_signal_noise.npy
|
|
67
|
+
pysdkit/_tfa/data/map2.npy
|
|
68
|
+
pysdkit/_tfa/data/single_nsignal.npy
|
|
63
69
|
pysdkit/_tid/__init__.py
|
|
64
70
|
pysdkit/_tid/alif.py
|
|
65
71
|
pysdkit/_tid/hvd.py
|
|
@@ -86,6 +92,7 @@ pysdkit/_vmd2d/vmd2d.py
|
|
|
86
92
|
pysdkit/_vncmd/__init__.py
|
|
87
93
|
pysdkit/_vncmd/avncmd.py
|
|
88
94
|
pysdkit/_vncmd/incmd.py
|
|
95
|
+
pysdkit/_vncmd/stnbmd.py
|
|
89
96
|
pysdkit/_vncmd/vncmd.py
|
|
90
97
|
pysdkit/data/__init__.py
|
|
91
98
|
pysdkit/data/_add_noise.py
|
|
@@ -147,6 +154,7 @@ pysdkit/tests/test_semd.py
|
|
|
147
154
|
pysdkit/tests/test_sjmd.py
|
|
148
155
|
pysdkit/tests/test_ssa.py
|
|
149
156
|
pysdkit/tests/test_stl.py
|
|
157
|
+
pysdkit/tests/test_stnbmd.py
|
|
150
158
|
pysdkit/tests/test_stvmd.py
|
|
151
159
|
pysdkit/tests/test_svmd.py
|
|
152
160
|
pysdkit/tests/test_swd.py
|
|
@@ -154,6 +162,7 @@ pysdkit/tests/test_vgnmd.py
|
|
|
154
162
|
pysdkit/tests/test_vmd.py
|
|
155
163
|
pysdkit/tests/test_vmd2d.py
|
|
156
164
|
pysdkit/tests/test_vncmd.py
|
|
165
|
+
pysdkit/tests/test_vtfmtd.py
|
|
157
166
|
pysdkit/tests/data/__init__.py
|
|
158
167
|
pysdkit/tests/data/test_generator_cube.py
|
|
159
168
|
pysdkit/tests/data/test_generator_image.py
|
|
@@ -146,7 +146,8 @@ 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
|
+
| [`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) | ✔️ |
|
|
150
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) | ✔️ |
|
|
151
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) | ✔️ |
|
|
152
153
|
| [`STL`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/tsa/_stl.py) (Seasonal-Trend decomposition using LOESS) | [[paper]](https://www.nniiem.ru/file/news/2016/stl-statistical-model.pdf) | [[code]](https://www.statsmodels.org/stable/examples/notebooks/generated/stl_decomposition.html) | ✔️ |
|
|
@@ -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,9 +109,15 @@ 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
|
|
|
118
|
+
# Variational Time-Frequency Mode Tracking Decomposition (TFA)
|
|
119
|
+
from ._tfa import VTFMTD, vtfmtd
|
|
120
|
+
|
|
115
121
|
# Adaptive Polymorphic Mode Decomposition / Impulsive Mode Decomposition
|
|
116
122
|
from ._imd import APMD, IMD, imd
|
|
117
123
|
|
|
@@ -193,7 +199,7 @@ Optimization-based Signal Decomposition | OSD
|
|
|
193
199
|
Generalized Dispersion Mode Decomposition | GDMD
|
|
194
200
|
Variational Generalized Nonlinear Mode Dec. | VGNMD
|
|
195
201
|
Improved VGNMD (crossed chirp / dispersive) | IVGNMD
|
|
196
|
-
Adaptive Generalized Dispersive Mode Dec.
|
|
202
|
+
Adaptive Generalized Dispersive Mode Dec. | AGNCMD / AGDMD
|
|
197
203
|
Variational Mode Decomposition | VMD
|
|
198
204
|
Multivariate Variational Mode Decomposition | MVMD
|
|
199
205
|
Variational Mode Extraction | VME
|
|
@@ -205,7 +211,9 @@ Compact VMD for 2D Image | CVMD2D
|
|
|
205
211
|
Variational Nonlinear Chirp Mode Decomposition | VNCMD
|
|
206
212
|
Iterative Nonlinear Chirp Mode Decomposition | INCMD
|
|
207
213
|
Adaptive Variational Nonlinear Chirp Mode Dec. | AVNCMD
|
|
214
|
+
Short-Time Narrow-Banded Mode Decomposition | STNBMD
|
|
208
215
|
Adaptive Local Iterative Filtering | ALIF
|
|
216
|
+
Variational TF Mode Tracking Decomposition | VTFMTD
|
|
209
217
|
Adaptive Polymorphic Mode Decomposition | APMD
|
|
210
218
|
Impulsive Mode Decomposition | IMD
|
|
211
219
|
Empirical Wavelet Transform | EWT
|
|
@@ -272,7 +280,11 @@ __all__ = [
|
|
|
272
280
|
"VNCMD",
|
|
273
281
|
"INCMD",
|
|
274
282
|
"AVNCMD",
|
|
283
|
+
"STNBMD",
|
|
284
|
+
"stnbmd",
|
|
275
285
|
"ALIF",
|
|
286
|
+
"VTFMTD",
|
|
287
|
+
"vtfmtd",
|
|
276
288
|
"APMD",
|
|
277
289
|
"IMD",
|
|
278
290
|
"imd",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Time-Frequency Analysis (TFA).
|
|
4
|
+
|
|
5
|
+
Methods that operate primarily on time-frequency representations
|
|
6
|
+
(e.g. STFT) for mode separation and instantaneous-frequency tracking.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .vtfmtd import (
|
|
10
|
+
VTFMTD,
|
|
11
|
+
vtfmtd,
|
|
12
|
+
stft,
|
|
13
|
+
frequency_axis,
|
|
14
|
+
bin_index_grid,
|
|
15
|
+
expand_omega_init,
|
|
16
|
+
first_difference_gram,
|
|
17
|
+
estimate_if_centroid,
|
|
18
|
+
smooth_if,
|
|
19
|
+
moving_average_if,
|
|
20
|
+
omega_bins_to_hz,
|
|
21
|
+
load_dual_signal_noise,
|
|
22
|
+
load_single_nsignal,
|
|
23
|
+
load_map2,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"VTFMTD",
|
|
28
|
+
"vtfmtd",
|
|
29
|
+
"stft",
|
|
30
|
+
"frequency_axis",
|
|
31
|
+
"bin_index_grid",
|
|
32
|
+
"expand_omega_init",
|
|
33
|
+
"first_difference_gram",
|
|
34
|
+
"estimate_if_centroid",
|
|
35
|
+
"smooth_if",
|
|
36
|
+
"moving_average_if",
|
|
37
|
+
"omega_bins_to_hz",
|
|
38
|
+
"load_dual_signal_noise",
|
|
39
|
+
"load_single_nsignal",
|
|
40
|
+
"load_map2",
|
|
41
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Variational Time-Frequency Mode Tracking Decomposition (VTFMTD).
|
|
4
|
+
|
|
5
|
+
Dong, H., Shan, T., Yu, G., Shi, Y., Chen, Y.
|
|
6
|
+
Variational time-frequency mode tracking for micro-Doppler signature extraction.
|
|
7
|
+
Signal Processing, 246:110603, 2026.
|
|
8
|
+
https://doi.org/10.1016/j.sigpro.2026.110603
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Dict, Optional, Tuple, Union
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
from scipy import sparse
|
|
18
|
+
from scipy.sparse.linalg import spsolve
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
# Packaged demo data
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
_DATA_DIR = Path(__file__).resolve().parent / "data"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _load_complex_npy(name: str) -> np.ndarray:
|
|
29
|
+
path = _DATA_DIR / name
|
|
30
|
+
if not path.is_file():
|
|
31
|
+
raise FileNotFoundError(
|
|
32
|
+
f"Missing VTFMTD demo data: {path}. "
|
|
33
|
+
"Reinstall PySDKit or restore pysdkit/_tfa/data/"
|
|
34
|
+
)
|
|
35
|
+
return np.asarray(np.load(path))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_dual_signal_noise() -> Dict[str, Union[np.ndarray, float]]:
|
|
39
|
+
"""
|
|
40
|
+
Load the packaged dual-component noisy complex demo
|
|
41
|
+
(MATLAB ``Dual_signal_noise.mat``).
|
|
42
|
+
|
|
43
|
+
Sampling rate is ``fs = 3000`` Hz and length is 3000 samples (1 s),
|
|
44
|
+
matching ``test1.m``.
|
|
45
|
+
"""
|
|
46
|
+
signal = _load_complex_npy("dual_signal_noise.npy").astype(np.complex128).ravel()
|
|
47
|
+
fs = 3000.0
|
|
48
|
+
t = np.arange(1, signal.size + 1, dtype=float) / fs
|
|
49
|
+
return {"signal": signal, "fs": fs, "t": t, "K": 2}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def load_single_nsignal() -> Dict[str, Union[np.ndarray, float]]:
|
|
53
|
+
"""
|
|
54
|
+
Load the packaged single-component noisy micro-Doppler demo
|
|
55
|
+
(MATLAB ``Single_nsignal.mat``).
|
|
56
|
+
|
|
57
|
+
Sampling rate is ``fs = 8011`` Hz and length is 8011 samples (1 s),
|
|
58
|
+
matching ``test2.m``.
|
|
59
|
+
"""
|
|
60
|
+
signal = _load_complex_npy("single_nsignal.npy").astype(np.complex128).ravel()
|
|
61
|
+
fs = 8011.0
|
|
62
|
+
t = np.arange(signal.size, dtype=float) / fs
|
|
63
|
+
return {"signal": signal, "fs": fs, "t": t, "K": 1}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def load_map2() -> np.ndarray:
|
|
67
|
+
"""Load the packaged MATLAB ``map2`` colormap (shape ``(64, 3)``)."""
|
|
68
|
+
return np.asarray(np.load(_DATA_DIR / "map2.npy"), dtype=float)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
# STFT (MATLAB STFT.m)
|
|
73
|
+
# ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def stft(signal: np.ndarray, hlength: Optional[int] = None) -> np.ndarray:
|
|
77
|
+
"""
|
|
78
|
+
Short-time Fourier transform used by VTFMTD (MATLAB ``STFT.m``).
|
|
79
|
+
|
|
80
|
+
Uses a Gaussian analysis window and hop size 1 (one column per sample).
|
|
81
|
+
Only the positive-frequency half of the FFT is retained.
|
|
82
|
+
|
|
83
|
+
:param signal: 1D array (real or complex), length ``N``
|
|
84
|
+
:param hlength: window length; defaults to ``round(N / 8)``. Odd length
|
|
85
|
+
is enforced as in MATLAB (``hlength + 1 - rem(hlength, 2)``).
|
|
86
|
+
:return: STFT matrix of shape ``(F, N)`` with ``F = round(N / 2)``
|
|
87
|
+
"""
|
|
88
|
+
x = np.asarray(signal).ravel()
|
|
89
|
+
n = int(x.size)
|
|
90
|
+
if n < 2:
|
|
91
|
+
raise ValueError("signal length must be >= 2")
|
|
92
|
+
|
|
93
|
+
if hlength is None:
|
|
94
|
+
hlength = int(round(n / 8.0))
|
|
95
|
+
hlength = int(hlength)
|
|
96
|
+
hlength = hlength + 1 - (hlength % 2)
|
|
97
|
+
|
|
98
|
+
ht = np.linspace(-0.5, 0.5, hlength)
|
|
99
|
+
h = np.exp(-np.pi / (0.32**2) * ht**2)
|
|
100
|
+
lh = (h.size - 1) // 2
|
|
101
|
+
|
|
102
|
+
f_bins = int(round(n / 2.0))
|
|
103
|
+
tfr = np.zeros((n, n), dtype=np.complex128)
|
|
104
|
+
|
|
105
|
+
# Vectorized over time for each lag tau (equivalent to MATLAB STFT.m)
|
|
106
|
+
half_n = n // 2 - 1
|
|
107
|
+
for tau_i, w in enumerate(h):
|
|
108
|
+
tau = int(tau_i - lh)
|
|
109
|
+
if abs(tau) > half_n:
|
|
110
|
+
continue
|
|
111
|
+
row = int(np.mod(n + tau, n))
|
|
112
|
+
# columns ti with valid sample index ti + tau
|
|
113
|
+
ti_lo = max(0, -tau)
|
|
114
|
+
ti_hi = min(n - 1, n - 1 - tau)
|
|
115
|
+
if ti_lo > ti_hi:
|
|
116
|
+
continue
|
|
117
|
+
cols = np.arange(ti_lo, ti_hi + 1)
|
|
118
|
+
tfr[row, cols] = x[cols + tau] * np.conj(w)
|
|
119
|
+
|
|
120
|
+
tfr = np.fft.fft(tfr, axis=0)
|
|
121
|
+
tfr = tfr[:f_bins, :]
|
|
122
|
+
return tfr / n * 2.0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def frequency_axis(n: int, fs: float) -> np.ndarray:
|
|
126
|
+
"""
|
|
127
|
+
Frequency axis (Hz) matching MATLAB demos:
|
|
128
|
+
``f = (0 : round(N/2)-1) * fs / N``.
|
|
129
|
+
"""
|
|
130
|
+
n = int(n)
|
|
131
|
+
f_bins = int(round(n / 2.0))
|
|
132
|
+
return np.arange(f_bins, dtype=float) * (float(fs) / n)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def bin_index_grid(f_bins: int, n_time: int) -> np.ndarray:
|
|
136
|
+
"""
|
|
137
|
+
MATLAB ``omega = repmat((1:F)', 1, T)`` — 1-based STFT bin indices
|
|
138
|
+
broadcast to shape ``(F, T)``.
|
|
139
|
+
"""
|
|
140
|
+
return np.tile(np.arange(1, f_bins + 1, dtype=float)[:, None], (1, n_time))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def expand_omega_init(
|
|
144
|
+
omega_init: np.ndarray,
|
|
145
|
+
f_bins: int,
|
|
146
|
+
n_time: int,
|
|
147
|
+
k_modes: int,
|
|
148
|
+
) -> np.ndarray:
|
|
149
|
+
"""
|
|
150
|
+
Broadcast initial IFs to shape ``(F, T, K)``.
|
|
151
|
+
|
|
152
|
+
Accepted forms:
|
|
153
|
+
- ``(F, T, K)`` — used as-is
|
|
154
|
+
- ``(K,)`` or ``(K, 1)`` — constant IF (bin index) per mode
|
|
155
|
+
- ``(T, K)`` or ``(K, T)`` — time-varying IF per mode (no frequency axis)
|
|
156
|
+
"""
|
|
157
|
+
w = np.asarray(omega_init, dtype=float)
|
|
158
|
+
if w.shape == (f_bins, n_time, k_modes):
|
|
159
|
+
return w.copy()
|
|
160
|
+
|
|
161
|
+
out = np.zeros((f_bins, n_time, k_modes), dtype=float)
|
|
162
|
+
if w.ndim == 1 and w.size == k_modes:
|
|
163
|
+
for k in range(k_modes):
|
|
164
|
+
out[:, :, k] = w[k]
|
|
165
|
+
return out
|
|
166
|
+
if w.shape == (k_modes, 1):
|
|
167
|
+
for k in range(k_modes):
|
|
168
|
+
out[:, :, k] = float(w[k, 0])
|
|
169
|
+
return out
|
|
170
|
+
if w.shape == (n_time, k_modes):
|
|
171
|
+
for k in range(k_modes):
|
|
172
|
+
out[:, :, k] = w[:, k][None, :]
|
|
173
|
+
return out
|
|
174
|
+
if w.shape == (k_modes, n_time):
|
|
175
|
+
for k in range(k_modes):
|
|
176
|
+
out[:, :, k] = w[k, :][None, :]
|
|
177
|
+
return out
|
|
178
|
+
raise ValueError(
|
|
179
|
+
"omega_init must have shape (F,T,K), (K,), (T,K) or (K,T); "
|
|
180
|
+
f"got {w.shape} for F={f_bins}, T={n_time}, K={k_modes}"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def first_difference_gram(n_time: int) -> sparse.csc_matrix:
|
|
185
|
+
"""
|
|
186
|
+
Build ``D.T @ D`` for the first-order difference operator in MATLAB
|
|
187
|
+
``VTFMTD.m`` (``spdiags`` with ``D(1,1)=1``).
|
|
188
|
+
"""
|
|
189
|
+
t = int(n_time)
|
|
190
|
+
# D: diagonal 1, sub-diagonal -1
|
|
191
|
+
d0 = np.ones(t)
|
|
192
|
+
d_m1 = -np.ones(t - 1)
|
|
193
|
+
d = sparse.diags([d_m1, d0], [-1, 0], shape=(t, t), format="csc")
|
|
194
|
+
return (d.T @ d).tocsc()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def estimate_if_centroid(
|
|
198
|
+
mode_stft: np.ndarray,
|
|
199
|
+
bin_grid: np.ndarray,
|
|
200
|
+
eps: float = np.finfo(float).eps,
|
|
201
|
+
) -> np.ndarray:
|
|
202
|
+
"""
|
|
203
|
+
Centroid IF estimate (MATLAB ``omega_est`` update).
|
|
204
|
+
|
|
205
|
+
:param mode_stft: complex STFT of one mode, shape ``(F, T)``
|
|
206
|
+
:param bin_grid: frequency-bin indices, shape ``(F, T)``
|
|
207
|
+
:return: IF field shape ``(F, T)`` (constant along frequency)
|
|
208
|
+
"""
|
|
209
|
+
power = np.abs(mode_stft) ** 2
|
|
210
|
+
numerator = np.sum(bin_grid * power, axis=0)
|
|
211
|
+
denominator = np.sum(power, axis=0)
|
|
212
|
+
if_traj = np.zeros(denominator.shape, dtype=float)
|
|
213
|
+
ok = denominator >= eps
|
|
214
|
+
if_traj[ok] = numerator[ok] / denominator[ok]
|
|
215
|
+
return np.tile(if_traj[None, :], (mode_stft.shape[0], 1))
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def smooth_if(
|
|
219
|
+
omega_est: np.ndarray,
|
|
220
|
+
gram: sparse.csc_matrix,
|
|
221
|
+
beta: float,
|
|
222
|
+
) -> np.ndarray:
|
|
223
|
+
"""
|
|
224
|
+
Smooth IF trajectories (MATLAB ``omega_smooth`` update).
|
|
225
|
+
|
|
226
|
+
Solves ``(2/beta * D'D + I) x = omega_est`` once per mode row pattern.
|
|
227
|
+
Because centroids are constant along frequency, only one solve per
|
|
228
|
+
time-series is required, then broadcast.
|
|
229
|
+
"""
|
|
230
|
+
f_bins, n_time = omega_est.shape
|
|
231
|
+
beta = float(beta)
|
|
232
|
+
eye = sparse.eye(n_time, format="csc")
|
|
233
|
+
system = ((2.0 / beta) * gram + eye).tocsc()
|
|
234
|
+
# all frequency rows share the same target trajectory
|
|
235
|
+
target = omega_est[0, :].astype(float)
|
|
236
|
+
smoothed = spsolve(system, target)
|
|
237
|
+
return np.tile(np.asarray(smoothed, dtype=float)[None, :], (f_bins, 1))
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def moving_average_if(if_traj: np.ndarray, win: int = 30) -> np.ndarray:
|
|
241
|
+
"""
|
|
242
|
+
Post-smoothing used in MATLAB demos (``Lth`` moving average on IF).
|
|
243
|
+
|
|
244
|
+
:param if_traj: 1D IF trajectory
|
|
245
|
+
:param win: odd/even window length (``Lth`` in the demos)
|
|
246
|
+
"""
|
|
247
|
+
x = np.asarray(if_traj, dtype=float).ravel()
|
|
248
|
+
m = x.size
|
|
249
|
+
half = int(round(win / 2.0))
|
|
250
|
+
out = np.empty(m, dtype=float)
|
|
251
|
+
for i in range(m):
|
|
252
|
+
lo = max(0, i - half)
|
|
253
|
+
hi = min(m, i + half + 1)
|
|
254
|
+
out[i] = np.mean(x[lo:hi])
|
|
255
|
+
return out
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def omega_bins_to_hz(omega_bins: np.ndarray, fs: float, n: int) -> np.ndarray:
|
|
259
|
+
"""
|
|
260
|
+
Convert MATLAB-style 1-based bin IF values to Hertz.
|
|
261
|
+
|
|
262
|
+
STFT row ``j`` (1-based) corresponds to frequency ``(j - 1) * fs / n``.
|
|
263
|
+
"""
|
|
264
|
+
return (np.asarray(omega_bins, dtype=float) - 1.0) * (float(fs) / float(n))
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# ---------------------------------------------------------------------------
|
|
268
|
+
# Core algorithm
|
|
269
|
+
# ---------------------------------------------------------------------------
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def vtfmtd(
|
|
273
|
+
signal: np.ndarray,
|
|
274
|
+
hlength: int,
|
|
275
|
+
K: int,
|
|
276
|
+
omega_init: np.ndarray,
|
|
277
|
+
alpha: float = 1e-5,
|
|
278
|
+
sigma: float = 1e-2,
|
|
279
|
+
beta: float = 1.0,
|
|
280
|
+
max_iter: int = 100,
|
|
281
|
+
epsilon: float = 1e-3,
|
|
282
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
283
|
+
"""
|
|
284
|
+
Functional VTFMTD interface (MATLAB ``VTFMTD``).
|
|
285
|
+
|
|
286
|
+
:param signal: 1D real/complex signal
|
|
287
|
+
:param hlength: STFT window length
|
|
288
|
+
:param K: number of modes
|
|
289
|
+
:param omega_init: initial IF field / centers (see ``expand_omega_init``)
|
|
290
|
+
:param alpha: bandwidth penalty (larger -> narrower TF support)
|
|
291
|
+
:param sigma: dual-ascent step size
|
|
292
|
+
:param beta: IF smoothness penalty (smaller -> smoother IF)
|
|
293
|
+
:param max_iter: maximum ADMM iterations
|
|
294
|
+
:param epsilon: relative convergence tolerance on mode STFTs
|
|
295
|
+
:return: ``(Gk, omega_smooth)`` with shapes ``(F, T, K)`` and ``(F, T, K)``
|
|
296
|
+
"""
|
|
297
|
+
x = np.asarray(signal).ravel()
|
|
298
|
+
g = stft(x, hlength=hlength)
|
|
299
|
+
f_bins, n_time = g.shape
|
|
300
|
+
k_modes = int(K)
|
|
301
|
+
|
|
302
|
+
omega_smooth = expand_omega_init(omega_init, f_bins, n_time, k_modes)
|
|
303
|
+
gk = np.zeros((f_bins, n_time, k_modes), dtype=np.complex128)
|
|
304
|
+
lam = np.zeros((f_bins, n_time), dtype=np.complex128)
|
|
305
|
+
bin_grid = bin_index_grid(f_bins, n_time)
|
|
306
|
+
gram = first_difference_gram(n_time)
|
|
307
|
+
|
|
308
|
+
alpha = float(alpha)
|
|
309
|
+
sigma = float(sigma)
|
|
310
|
+
beta = float(beta)
|
|
311
|
+
epsilon = float(epsilon)
|
|
312
|
+
|
|
313
|
+
for it in range(int(max_iter)):
|
|
314
|
+
gk_prev = gk.copy()
|
|
315
|
+
|
|
316
|
+
# ----- update mode STFTs (TF Wiener filtering) -----
|
|
317
|
+
for k in range(k_modes):
|
|
318
|
+
sum_others = np.sum(gk, axis=2) - gk[:, :, k]
|
|
319
|
+
denom = 1.0 + alpha * (bin_grid - omega_smooth[:, :, k]) ** 2
|
|
320
|
+
gk[:, :, k] = (g - sum_others - lam / 2.0) / denom
|
|
321
|
+
|
|
322
|
+
# ----- centroid IF + smoothing -----
|
|
323
|
+
for k in range(k_modes):
|
|
324
|
+
omega_est = estimate_if_centroid(gk[:, :, k], bin_grid)
|
|
325
|
+
omega_smooth[:, :, k] = smooth_if(omega_est, gram, beta=beta)
|
|
326
|
+
|
|
327
|
+
# ----- dual ascent -----
|
|
328
|
+
lam = lam + sigma * (g - np.sum(gk, axis=2))
|
|
329
|
+
|
|
330
|
+
if it > 0:
|
|
331
|
+
diff = np.sum(np.abs(gk - gk_prev))
|
|
332
|
+
norm = np.sum(np.abs(gk_prev)) + np.finfo(float).eps
|
|
333
|
+
if diff < epsilon * norm:
|
|
334
|
+
break
|
|
335
|
+
|
|
336
|
+
return gk, omega_smooth
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class VTFMTD(object):
|
|
340
|
+
"""
|
|
341
|
+
Variational Time-Frequency Mode Tracking Decomposition.
|
|
342
|
+
|
|
343
|
+
Decomposes the STFT of a (possibly complex) 1D signal into ``K`` mode
|
|
344
|
+
STFTs while tracking instantaneous-frequency ridges via ADMM, following
|
|
345
|
+
Dong et al., Signal Processing 246:110603, 2026.
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
def __init__(
|
|
349
|
+
self,
|
|
350
|
+
hlength: int = 30,
|
|
351
|
+
K: int = 2,
|
|
352
|
+
alpha: float = 1e-5,
|
|
353
|
+
sigma: float = 1e-2,
|
|
354
|
+
beta: float = 1.0,
|
|
355
|
+
max_iter: int = 100,
|
|
356
|
+
epsilon: float = 1e-3,
|
|
357
|
+
) -> None:
|
|
358
|
+
"""
|
|
359
|
+
:param hlength: STFT window length
|
|
360
|
+
:param K: number of TF modes
|
|
361
|
+
:param alpha: TF bandwidth penalty
|
|
362
|
+
:param sigma: Lagrange dual step
|
|
363
|
+
:param beta: IF smoothness penalty
|
|
364
|
+
:param max_iter: maximum iterations
|
|
365
|
+
:param epsilon: convergence tolerance
|
|
366
|
+
"""
|
|
367
|
+
self.hlength = int(hlength)
|
|
368
|
+
self.K = int(K)
|
|
369
|
+
self.alpha = float(alpha)
|
|
370
|
+
self.sigma = float(sigma)
|
|
371
|
+
self.beta = float(beta)
|
|
372
|
+
self.max_iter = int(max_iter)
|
|
373
|
+
self.epsilon = float(epsilon)
|
|
374
|
+
|
|
375
|
+
self.signal: Optional[np.ndarray] = None
|
|
376
|
+
self.stft_signal: Optional[np.ndarray] = None
|
|
377
|
+
self.Gk: Optional[np.ndarray] = None
|
|
378
|
+
self.omega: Optional[np.ndarray] = None
|
|
379
|
+
|
|
380
|
+
def __str__(self) -> str:
|
|
381
|
+
return "Variational Time-Frequency Mode Tracking Decomposition (VTFMTD)"
|
|
382
|
+
|
|
383
|
+
def __call__(
|
|
384
|
+
self,
|
|
385
|
+
signal: np.ndarray,
|
|
386
|
+
omega_init: np.ndarray,
|
|
387
|
+
return_all: bool = False,
|
|
388
|
+
):
|
|
389
|
+
return self.fit_transform(
|
|
390
|
+
signal=signal, omega_init=omega_init, return_all=return_all
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
def fit_transform(
|
|
394
|
+
self,
|
|
395
|
+
signal: np.ndarray,
|
|
396
|
+
omega_init: np.ndarray,
|
|
397
|
+
return_all: bool = False,
|
|
398
|
+
) -> Union[
|
|
399
|
+
Tuple[np.ndarray, np.ndarray], Tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
400
|
+
]:
|
|
401
|
+
"""
|
|
402
|
+
Run VTFMTD.
|
|
403
|
+
|
|
404
|
+
:param signal: 1D real/complex input
|
|
405
|
+
:param omega_init: initial IFs (bin units), see ``expand_omega_init``
|
|
406
|
+
:param return_all: if True, also return the composite STFT
|
|
407
|
+
:return: ``(Gk, omega)`` or ``(Gk, omega, G)``
|
|
408
|
+
"""
|
|
409
|
+
x = np.asarray(signal).ravel()
|
|
410
|
+
gk, omega = vtfmtd(
|
|
411
|
+
signal=x,
|
|
412
|
+
hlength=self.hlength,
|
|
413
|
+
K=self.K,
|
|
414
|
+
omega_init=omega_init,
|
|
415
|
+
alpha=self.alpha,
|
|
416
|
+
sigma=self.sigma,
|
|
417
|
+
beta=self.beta,
|
|
418
|
+
max_iter=self.max_iter,
|
|
419
|
+
epsilon=self.epsilon,
|
|
420
|
+
)
|
|
421
|
+
g = stft(x, hlength=self.hlength)
|
|
422
|
+
|
|
423
|
+
self.signal = x
|
|
424
|
+
self.stft_signal = g
|
|
425
|
+
self.Gk = gk
|
|
426
|
+
self.omega = omega
|
|
427
|
+
|
|
428
|
+
if return_all:
|
|
429
|
+
return gk, omega, g
|
|
430
|
+
return gk, omega
|
|
431
|
+
|
|
432
|
+
def if_trajectories(self) -> np.ndarray:
|
|
433
|
+
"""
|
|
434
|
+
Return IF trajectories with shape ``(K, T)`` (bin units),
|
|
435
|
+
taken from the first frequency row of ``omega`` (MATLAB convention).
|
|
436
|
+
"""
|
|
437
|
+
if self.omega is None:
|
|
438
|
+
raise ValueError("Call fit_transform before requesting IF trajectories.")
|
|
439
|
+
return np.asarray(self.omega[0, :, :], dtype=float).T.copy()
|