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.
Files changed (193) hide show
  1. {pysdkit-0.4.46 → pysdkit-0.4.47}/PKG-INFO +2 -2
  2. {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/PKG-INFO +2 -2
  3. {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/SOURCES.txt +2 -0
  4. {pysdkit-0.4.46 → pysdkit-0.4.47}/README.md +1 -1
  5. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/__init__.py +7 -1
  6. pysdkit-0.4.47/pysdkit/_vncmd/__init__.py +44 -0
  7. pysdkit-0.4.47/pysdkit/_vncmd/stnbmd.py +515 -0
  8. pysdkit-0.4.47/pysdkit/tests/test_stnbmd.py +243 -0
  9. pysdkit-0.4.46/pysdkit/_vncmd/__init__.py +0 -10
  10. {pysdkit-0.4.46 → pysdkit-0.4.47}/LICENSE +0 -0
  11. {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/dependency_links.txt +0 -0
  12. {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/requires.txt +0 -0
  13. {pysdkit-0.4.46 → pysdkit-0.4.47}/PySDKit.egg-info/top_level.txt +0 -0
  14. {pysdkit-0.4.46 → pysdkit-0.4.47}/pyproject.toml +0 -0
  15. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/__init__.py +0 -0
  16. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/_find_extrema.py +0 -0
  17. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/_prepare_points.py +0 -0
  18. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/_splines.py +0 -0
  19. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/ceemdan.py +0 -0
  20. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/eemd.py +0 -0
  21. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/efd.py +0 -0
  22. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/emd.py +0 -0
  23. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/esmd.py +0 -0
  24. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/hht/__init__.py +0 -0
  25. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/hht/frequency.py +0 -0
  26. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/hht/hht.py +0 -0
  27. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/memd.py +0 -0
  28. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/remd.py +0 -0
  29. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/semd.py +0 -0
  30. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd/tvf_emd.py +0 -0
  31. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd2d/__init__.py +0 -0
  32. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd2d/bemd.py +0 -0
  33. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd2d/bmemd.py +0 -0
  34. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_emd2d/emd2d.py +0 -0
  35. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_ewt/__init__.py +0 -0
  36. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_ewt/ewt.py +0 -0
  37. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_ewt/ewt2d.py +0 -0
  38. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/__init__.py +0 -0
  39. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/extrema.py +0 -0
  40. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/faemd.py +0 -0
  41. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/faemd2d.py +0 -0
  42. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/faemd3d.py +0 -0
  43. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_faemd/filter.py +0 -0
  44. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_fmd/__init__.py +0 -0
  45. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_fmd/fmd.py +0 -0
  46. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_fmd/nfmd.py +0 -0
  47. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/__init__.py +0 -0
  48. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/agncmd.py +0 -0
  49. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/gdmd.py +0 -0
  50. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/ivgnmd.py +0 -0
  51. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_gdmd/vgnmd.py +0 -0
  52. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/__init__.py +0 -0
  53. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/apmd.py +0 -0
  54. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/data/gearbox_fault_snippet.npy +0 -0
  55. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/data/input_sig.npy +0 -0
  56. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_imd/imd.py +0 -0
  57. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_jmd/__init__.py +0 -0
  58. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_jmd/jmd.py +0 -0
  59. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_jmd/mjmd.py +0 -0
  60. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_jmd/sjmd.py +0 -0
  61. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_lmd/__init__.py +0 -0
  62. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_lmd/lmd.py +0 -0
  63. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_lmd/rlmd.py +0 -0
  64. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_osd/__init__.py +0 -0
  65. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_osd/components.py +0 -0
  66. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_osd/osd.py +0 -0
  67. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_osd/swd.py +0 -0
  68. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/__init__.py +0 -0
  69. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/data/__init__.py +0 -0
  70. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/data/dual_signal_noise.npy +0 -0
  71. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/data/map2.npy +0 -0
  72. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/data/single_nsignal.npy +0 -0
  73. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tfa/vtfmtd.py +0 -0
  74. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/__init__.py +0 -0
  75. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/alif.py +0 -0
  76. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/data/__init__.py +0 -0
  77. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/data/prefixed_double_filter.npy +0 -0
  78. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/hvd.py +0 -0
  79. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/itd.py +0 -0
  80. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/iterative_filtering.py +0 -0
  81. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_tid/ssa.py +0 -0
  82. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/__init__.py +0 -0
  83. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/acmd.py +0 -0
  84. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/avmd.py +0 -0
  85. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/ba_acmd.py +0 -0
  86. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/base.py +0 -0
  87. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/mvmd.py +0 -0
  88. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/ovmd.py +0 -0
  89. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/stvmd.py +0 -0
  90. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/svmd.py +0 -0
  91. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/vmd_c.py +0 -0
  92. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/vmd_f.py +0 -0
  93. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd/vme.py +0 -0
  94. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd2d/__init__.py +0 -0
  95. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd2d/cvmd2d.py +0 -0
  96. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vmd2d/vmd2d.py +0 -0
  97. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vncmd/avncmd.py +0 -0
  98. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vncmd/incmd.py +0 -0
  99. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/_vncmd/vncmd.py +0 -0
  100. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/__init__.py +0 -0
  101. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_add_noise.py +0 -0
  102. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_cube.py +0 -0
  103. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_generator.py +0 -0
  104. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_image.py +0 -0
  105. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_models.py +0 -0
  106. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_test_univariate.py +0 -0
  107. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/_time_series.py +0 -0
  108. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/fmd_demo.npy +0 -0
  109. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/texture.txt +0 -0
  110. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/data/vmd_example.npy +0 -0
  111. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/entropy/__init__.py +0 -0
  112. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
  113. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/entropy/_permutation_entropy.py +0 -0
  114. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/entropy/_sample_entropy.py +0 -0
  115. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/__init__.py +0 -0
  116. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/_base.py +0 -0
  117. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/_kmeans.py +0 -0
  118. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/_knn.py +0 -0
  119. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/models/_pca.py +0 -0
  120. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/__init__.py +0 -0
  121. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_fourier_spectra.py +0 -0
  122. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_functions.py +0 -0
  123. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_plot_images.py +0 -0
  124. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_plot_imfs.py +0 -0
  125. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/plot/_plot_signal.py +0 -0
  126. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/__init__.py +0 -0
  127. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/__init__.py +0 -0
  128. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_cube.py +0 -0
  129. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_image.py +0 -0
  130. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_signal.py +0 -0
  131. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
  132. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/models/__init__.py +0 -0
  133. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/models/test_knn.py +0 -0
  134. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/models/test_pca.py +0 -0
  135. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/run_all.py +0 -0
  136. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_agncmd.py +0 -0
  137. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_alif.py +0 -0
  138. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_apmd.py +0 -0
  139. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_avncmd.py +0 -0
  140. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_bmemd.py +0 -0
  141. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ceemdan.py +0 -0
  142. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_cvmd2d.py +0 -0
  143. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_eemd.py +0 -0
  144. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_emd.py +0 -0
  145. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_esmd.py +0 -0
  146. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ewt.py +0 -0
  147. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ewt2d.py +0 -0
  148. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_faemd.py +0 -0
  149. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_faemd2d.py +0 -0
  150. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_faemd3d.py +0 -0
  151. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_fmd.py +0 -0
  152. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_gdmd.py +0 -0
  153. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_hvd.py +0 -0
  154. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_imd.py +0 -0
  155. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_itd.py +0 -0
  156. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ivgnmd.py +0 -0
  157. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_jmd.py +0 -0
  158. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_lmd.py +0 -0
  159. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_moving_decomp.py +0 -0
  160. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_mstl.py +0 -0
  161. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_osd.py +0 -0
  162. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ovmd.py +0 -0
  163. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_rlmd.py +0 -0
  164. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_semd.py +0 -0
  165. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_sjmd.py +0 -0
  166. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_ssa.py +0 -0
  167. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_stl.py +0 -0
  168. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_stvmd.py +0 -0
  169. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_svmd.py +0 -0
  170. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_swd.py +0 -0
  171. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vgnmd.py +0 -0
  172. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vmd.py +0 -0
  173. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vmd2d.py +0 -0
  174. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vncmd.py +0 -0
  175. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tests/test_vtfmtd.py +0 -0
  176. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/__init__.py +0 -0
  177. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/_dtw.py +0 -0
  178. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/_moving_decomp.py +0 -0
  179. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/_mstl.py +0 -0
  180. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/tsa/_stl.py +0 -0
  181. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/__init__.py +0 -0
  182. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_correlation.py +0 -0
  183. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_diagnalization.py +0 -0
  184. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_differ.py +0 -0
  185. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_fft.py +0 -0
  186. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_function.py +0 -0
  187. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_hilbert.py +0 -0
  188. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_instantaneous.py +0 -0
  189. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_kernel_matrix.py +0 -0
  190. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_mirror.py +0 -0
  191. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_process.py +0 -0
  192. {pysdkit-0.4.46 → pysdkit-0.4.47}/pysdkit/utils/_smooth1d.py +0 -0
  193. {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.46
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.46
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.46"
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)