ezmsg-sigproc 2.5.0__tar.gz → 2.7.0__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.
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/.github/workflows/docs.yml +5 -4
- ezmsg_sigproc-2.7.0/.github/workflows/python-publish.yml +29 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/.pre-commit-config.yaml +1 -2
- ezmsg_sigproc-2.7.0/PKG-INFO +60 -0
- ezmsg_sigproc-2.7.0/README.md +42 -0
- ezmsg_sigproc-2.7.0/docs/source/api/index.rst +11 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/conf.py +1 -0
- ezmsg_sigproc-2.7.0/docs/source/guides/how-tos/signalprocessing/composite.rst +6 -0
- ezmsg_sigproc-2.7.0/docs/source/guides/how-tos/signalprocessing/processor.rst +8 -0
- ezmsg_sigproc-2.7.0/docs/source/guides/how-tos/signalprocessing/stateful.rst +8 -0
- ezmsg_sigproc-2.7.0/docs/source/guides/how-tos/signalprocessing/unit.rst +41 -0
- ezmsg_sigproc-2.7.0/docs/source/guides/sigproc/architecture.rst +41 -0
- ezmsg_sigproc-2.7.0/docs/source/guides/sigproc/base.rst +19 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/sigproc/content-sigproc.rst +1 -0
- ezmsg_sigproc-2.7.0/docs/source/index.md +20 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/pyproject.toml +20 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/__version__.py +2 -2
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/activation.py +5 -11
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/adaptive_lattice_notch.py +11 -30
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/affinetransform.py +16 -42
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/aggregate.py +17 -34
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/bandpower.py +12 -20
- ezmsg_sigproc-2.7.0/src/ezmsg/sigproc/base.py +149 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/butterworthfilter.py +8 -16
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/butterworthzerophase.py +7 -16
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/cheby.py +4 -10
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/combfilter.py +5 -8
- ezmsg_sigproc-2.7.0/src/ezmsg/sigproc/coordinatespaces.py +142 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/decimate.py +3 -7
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/denormalize.py +6 -11
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/detrend.py +3 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/diff.py +8 -17
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/downsample.py +11 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/ewma.py +11 -28
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/extract_axis.py +3 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/fbcca.py +34 -59
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/filter.py +19 -45
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/filterbank.py +37 -74
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/filterbankdesign.py +7 -14
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/fir_hilbert.py +13 -30
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/fir_pmc.py +5 -10
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/firfilter.py +12 -14
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/gaussiansmoothing.py +5 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/kaiser.py +11 -15
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/math/abs.py +4 -3
- ezmsg_sigproc-2.7.0/src/ezmsg/sigproc/math/add.py +121 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/math/clip.py +4 -1
- ezmsg_sigproc-2.7.0/src/ezmsg/sigproc/math/difference.py +137 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/math/invert.py +3 -3
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/math/log.py +5 -6
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/math/scale.py +2 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/messages.py +1 -2
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/quantize.py +3 -6
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/resample.py +17 -38
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/rollingscaler.py +12 -37
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/sampler.py +19 -37
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/scaler.py +11 -22
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/signalinjector.py +7 -18
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/slicer.py +14 -34
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/spectrogram.py +12 -19
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/spectrum.py +17 -38
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/transpose.py +12 -24
- ezmsg_sigproc-2.7.0/src/ezmsg/sigproc/util/asio.py +25 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/util/axisarray_buffer.py +12 -26
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/util/buffer.py +22 -43
- ezmsg_sigproc-2.7.0/src/ezmsg/sigproc/util/message.py +17 -0
- ezmsg_sigproc-2.7.0/src/ezmsg/sigproc/util/profile.py +23 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/util/sparse.py +7 -15
- ezmsg_sigproc-2.7.0/src/ezmsg/sigproc/util/typeresolution.py +17 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/wavelets.py +10 -19
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/window.py +29 -83
- ezmsg_sigproc-2.7.0/tests/helpers/synth.py +281 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/helpers/util.py +9 -18
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/bytewax/test_spectrum_bytewax.py +4 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/bytewax/test_window_bytewax.py +3 -3
- ezmsg_sigproc-2.7.0/tests/integration/ezmsg/test_add_system.py +148 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_butterworth_system.py +10 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_butterworthzerophase_system.py +1 -1
- ezmsg_sigproc-2.7.0/tests/integration/ezmsg/test_coordinatespaces_system.py +226 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_decimate_system.py +5 -8
- ezmsg_sigproc-2.7.0/tests/integration/ezmsg/test_difference_system.py +203 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_downsample_system.py +10 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_filter_system.py +9 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_fir_hilbert_system.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_fir_pmc_system.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_rollingscaler_system.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_sampler_system.py +7 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_scaler_system.py +8 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_spectrum_system.py +9 -10
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/integration/ezmsg/test_window_system.py +10 -17
- ezmsg_sigproc-2.7.0/tests/test_profile.py +39 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/buffer/test_axisarray_buffer.py +6 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/buffer/test_buffer.py +2 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/buffer/test_buffer_overflow.py +10 -21
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_activation.py +4 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_adaptive_lattice_notch.py +5 -17
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_affine_transform.py +2 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_aggregate.py +20 -56
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_bandpower.py +2 -2
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_base.py +52 -133
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_butter.py +8 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_butterworthzerophase.py +7 -21
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_combfilter.py +9 -24
- ezmsg_sigproc-2.7.0/tests/unit/test_coordinatespaces.py +272 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_denormalize.py +7 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_diff.py +3 -3
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_downsample.py +5 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_ewma.py +4 -8
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_extract_axis.py +3 -5
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_fbcca.py +27 -85
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_filter.py +2 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_filterbank.py +5 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_filterbankdesign.py +22 -63
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_fir_hilbert.py +4 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_firfilter.py +2 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_gaussian_smoothing_filter.py +6 -14
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_kaiser.py +4 -10
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_math.py +1 -4
- ezmsg_sigproc-2.7.0/tests/unit/test_math_add.py +247 -0
- ezmsg_sigproc-2.7.0/tests/unit/test_math_difference.py +278 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_quantize.py +4 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_resample.py +8 -22
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_rollingscaler.py +5 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_sampler.py +9 -25
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_scaler.py +4 -7
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_slicer.py +5 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_spectrogram.py +6 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_spectrum.py +14 -29
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_transpose.py +2 -5
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_util.py +17 -51
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_wavelets.py +4 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_window.py +11 -30
- ezmsg_sigproc-2.5.0/.github/workflows/python-publish-ezmsg-sigproc.yml +0 -30
- ezmsg_sigproc-2.5.0/PKG-INFO +0 -72
- ezmsg_sigproc-2.5.0/README.md +0 -55
- ezmsg_sigproc-2.5.0/docs/source/api/index.rst +0 -157
- ezmsg_sigproc-2.5.0/docs/source/guides/ProcessorsBase.md +0 -173
- ezmsg_sigproc-2.5.0/docs/source/guides/how-tos/signalprocessing/composite.rst +0 -4
- ezmsg_sigproc-2.5.0/docs/source/guides/how-tos/signalprocessing/processor.rst +0 -4
- ezmsg_sigproc-2.5.0/docs/source/guides/how-tos/signalprocessing/stateful.rst +0 -4
- ezmsg_sigproc-2.5.0/docs/source/guides/how-tos/signalprocessing/unit.rst +0 -11
- ezmsg_sigproc-2.5.0/docs/source/guides/sigproc/base.rst +0 -65
- ezmsg_sigproc-2.5.0/docs/source/index.rst +0 -82
- ezmsg_sigproc-2.5.0/src/ezmsg/sigproc/base.py +0 -1284
- ezmsg_sigproc-2.5.0/src/ezmsg/sigproc/math/difference.py +0 -73
- ezmsg_sigproc-2.5.0/src/ezmsg/sigproc/synth.py +0 -774
- ezmsg_sigproc-2.5.0/src/ezmsg/sigproc/util/asio.py +0 -156
- ezmsg_sigproc-2.5.0/src/ezmsg/sigproc/util/message.py +0 -31
- ezmsg_sigproc-2.5.0/src/ezmsg/sigproc/util/profile.py +0 -174
- ezmsg_sigproc-2.5.0/src/ezmsg/sigproc/util/typeresolution.py +0 -83
- ezmsg_sigproc-2.5.0/tests/integration/ezmsg/test_synth_system.py +0 -243
- ezmsg_sigproc-2.5.0/tests/test_profile.py +0 -191
- ezmsg_sigproc-2.5.0/tests/unit/test_synth.py +0 -150
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/.github/workflows/python-tests.yml +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/.gitignore +0 -0
- ezmsg_sigproc-2.5.0/LICENSE.txt → ezmsg_sigproc-2.7.0/LICENSE +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/Makefile +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/make.bat +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/_templates/autosummary/module.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/HybridBuffer.md +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/explanations/sigproc.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/how-tos/signalprocessing/adaptive.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/how-tos/signalprocessing/checkpoint.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/how-tos/signalprocessing/content-signalprocessing.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/how-tos/signalprocessing/standalone.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/img/HybridBufferBasic.svg +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/img/HybridBufferOverflow.svg +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/sigproc/processors.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/sigproc/units.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/docs/source/guides/tutorials/signalprocessing.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/ewmfilter.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/math/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/spectral.py +3 -3
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/src/ezmsg/sigproc/util/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/conftest.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/helpers/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/resources/xform.csv +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.7.0}/tests/unit/test_fir_pmc.py +0 -0
|
@@ -4,11 +4,12 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches:
|
|
6
6
|
- main
|
|
7
|
-
tags:
|
|
8
|
-
- 'v*'
|
|
9
7
|
pull_request:
|
|
10
8
|
branches:
|
|
9
|
+
- main
|
|
11
10
|
- dev
|
|
11
|
+
release:
|
|
12
|
+
types: [published]
|
|
12
13
|
workflow_dispatch:
|
|
13
14
|
|
|
14
15
|
permissions:
|
|
@@ -52,8 +53,8 @@ jobs:
|
|
|
52
53
|
path: 'docs/build/html'
|
|
53
54
|
|
|
54
55
|
deploy:
|
|
55
|
-
# Only deploy
|
|
56
|
-
if: github.event_name == '
|
|
56
|
+
# Only deploy when a release is published
|
|
57
|
+
if: github.event_name == 'release'
|
|
57
58
|
environment:
|
|
58
59
|
name: github-pages
|
|
59
60
|
url: ${{ steps.deployment.outputs.page_url }}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Upload Python Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: build and upload release to PyPI
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment: "release"
|
|
16
|
+
permissions:
|
|
17
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@v6
|
|
24
|
+
|
|
25
|
+
- name: Build Package
|
|
26
|
+
run: uv build
|
|
27
|
+
|
|
28
|
+
- name: Publish package distributions to PyPI
|
|
29
|
+
run: uv publish
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ezmsg-sigproc
|
|
3
|
+
Version: 2.7.0
|
|
4
|
+
Summary: Timeseries signal processing implementations in ezmsg
|
|
5
|
+
Author-email: Griffin Milsap <griffin.milsap@gmail.com>, Preston Peranich <pperanich@gmail.com>, Chadwick Boulay <chadwick.boulay@gmail.com>, Kyle McGraw <kmcgraw@blackrockneuro.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.10.15
|
|
9
|
+
Requires-Dist: array-api-compat>=1.11.1
|
|
10
|
+
Requires-Dist: ezmsg-baseproc>=1.1.0
|
|
11
|
+
Requires-Dist: ezmsg>=3.6.0
|
|
12
|
+
Requires-Dist: numba>=0.61.0
|
|
13
|
+
Requires-Dist: numpy>=1.26.0
|
|
14
|
+
Requires-Dist: pywavelets>=1.6.0
|
|
15
|
+
Requires-Dist: scipy>=1.13.1
|
|
16
|
+
Requires-Dist: sparse>=0.15.4
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# ezmsg-sigproc
|
|
20
|
+
|
|
21
|
+
Signal processing primitives for the [ezmsg](https://www.ezmsg.org) message-passing framework.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
* **Filtering** - Chebyshev, comb filters, and more
|
|
26
|
+
* **Spectral analysis** - Spectrogram, spectrum, and wavelet transforms
|
|
27
|
+
* **Resampling** - Downsample, decimate, and resample operations
|
|
28
|
+
* **Windowing** - Sliding windows and buffering utilities
|
|
29
|
+
* **Math operations** - Arithmetic, log, abs, difference, and more
|
|
30
|
+
* **Signal generation** - Synthetic signal generators
|
|
31
|
+
|
|
32
|
+
All modules use `AxisArray` as the primary data structure for passing signals between components.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Install from PyPI:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install ezmsg-sigproc
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or install from GitHub for the latest development version:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install git+https://github.com/ezmsg-org/ezmsg-sigproc.git@dev
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Documentation
|
|
49
|
+
|
|
50
|
+
Full documentation is available at [ezmsg.org](https://www.ezmsg.org).
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
We use [`uv`](https://docs.astral.sh/uv/) for development.
|
|
55
|
+
|
|
56
|
+
1. Fork and clone the repository
|
|
57
|
+
2. `uv sync` to create a virtual environment and install dependencies
|
|
58
|
+
3. `uv run pre-commit install` to set up linting and formatting hooks
|
|
59
|
+
4. `uv run pytest tests` to run the test suite
|
|
60
|
+
5. Submit a PR against the `dev` branch
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# ezmsg-sigproc
|
|
2
|
+
|
|
3
|
+
Signal processing primitives for the [ezmsg](https://www.ezmsg.org) message-passing framework.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
* **Filtering** - Chebyshev, comb filters, and more
|
|
8
|
+
* **Spectral analysis** - Spectrogram, spectrum, and wavelet transforms
|
|
9
|
+
* **Resampling** - Downsample, decimate, and resample operations
|
|
10
|
+
* **Windowing** - Sliding windows and buffering utilities
|
|
11
|
+
* **Math operations** - Arithmetic, log, abs, difference, and more
|
|
12
|
+
* **Signal generation** - Synthetic signal generators
|
|
13
|
+
|
|
14
|
+
All modules use `AxisArray` as the primary data structure for passing signals between components.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Install from PyPI:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install ezmsg-sigproc
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or install from GitHub for the latest development version:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install git+https://github.com/ezmsg-org/ezmsg-sigproc.git@dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
Full documentation is available at [ezmsg.org](https://www.ezmsg.org).
|
|
33
|
+
|
|
34
|
+
## Development
|
|
35
|
+
|
|
36
|
+
We use [`uv`](https://docs.astral.sh/uv/) for development.
|
|
37
|
+
|
|
38
|
+
1. Fork and clone the repository
|
|
39
|
+
2. `uv sync` to create a virtual environment and install dependencies
|
|
40
|
+
3. `uv run pre-commit install` to set up linting and formatting hooks
|
|
41
|
+
4. `uv run pytest tests` to run the test suite
|
|
42
|
+
5. Submit a PR against the `dev` branch
|
|
@@ -72,6 +72,7 @@ intersphinx_mapping = {
|
|
|
72
72
|
"numpy": ("https://numpy.org/doc/stable/", None),
|
|
73
73
|
"scipy": ("https://scipy.org/doc/scipy/", None),
|
|
74
74
|
"ezmsg": ("https://www.ezmsg.org/ezmsg/", None),
|
|
75
|
+
"ezmsg.baseproc": ("https://www.ezmsg.org/ezmsg-baseproc/", None),
|
|
75
76
|
"ezmsg.learn": ("https://www.ezmsg.org/ezmsg-learn/", None),
|
|
76
77
|
"ezmsg.event": ("https://www.ezmsg.org/ezmsg-event/", None),
|
|
77
78
|
"ezmsg.lsl": ("https://www.ezmsg.org/ezmsg-lsl/", None),
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
How to efficiently chain multiple signal processors in ezmsg?
|
|
2
|
+
#############################################################
|
|
3
|
+
|
|
4
|
+
For general composite processor implementation guidance, see the `ezmsg-baseproc documentation <https://www.ezmsg.org/ezmsg-baseproc/guides/ProcessorsBase.html>`_.
|
|
5
|
+
|
|
6
|
+
(under construction)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
How to write a signal processor in ezmsg?
|
|
2
|
+
#########################################
|
|
3
|
+
|
|
4
|
+
For general processor implementation guidance, see the `ezmsg-baseproc documentation <https://www.ezmsg.org/ezmsg-baseproc/guides/ProcessorsBase.html>`_.
|
|
5
|
+
|
|
6
|
+
For signal processing specific patterns, see the existing processors in ``ezmsg.sigproc`` as examples.
|
|
7
|
+
|
|
8
|
+
(under construction)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
How to implement a stateful signal processor in ezmsg?
|
|
2
|
+
######################################################
|
|
3
|
+
|
|
4
|
+
For general stateful processor implementation guidance, see the `ezmsg-baseproc documentation <https://www.ezmsg.org/ezmsg-baseproc/guides/ProcessorsBase.html>`_.
|
|
5
|
+
|
|
6
|
+
For signal processing specific patterns using stateful processors, see implementations like ``ChebyshevFilterTransformer`` and ``WindowTransformer`` as examples.
|
|
7
|
+
|
|
8
|
+
(under construction)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
How to turn a signal processor into an ``ezmsg`` Unit?
|
|
2
|
+
######################################################
|
|
3
|
+
|
|
4
|
+
For general guidance on converting processors to ezmsg Units, see the `ezmsg-baseproc documentation <https://www.ezmsg.org/ezmsg-baseproc/guides/ProcessorsBase.html#implementing-a-custom-ezmsg-unit>`_.
|
|
5
|
+
|
|
6
|
+
Example with Signal Processing
|
|
7
|
+
------------------------------
|
|
8
|
+
|
|
9
|
+
To convert a signal processor to an ``ezmsg`` Unit:
|
|
10
|
+
|
|
11
|
+
1. **Define the Processor**: Create a class that inherits from the appropriate base class (e.g., ``BaseTransformer``, ``BaseStatefulTransformer``).
|
|
12
|
+
2. **Implement the Processing Logic**: Override the ``_process`` method to implement the signal processing logic.
|
|
13
|
+
3. **Create the Unit**: Inherit from the appropriate Unit base class (e.g., ``BaseTransformerUnit``).
|
|
14
|
+
|
|
15
|
+
.. code-block:: python
|
|
16
|
+
|
|
17
|
+
import ezmsg.core as ez
|
|
18
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
19
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MyProcessorSettings(ez.Settings):
|
|
23
|
+
# Your settings here
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class MyProcessor(BaseTransformer[MyProcessorSettings, AxisArray, AxisArray]):
|
|
28
|
+
def _process(self, message: AxisArray) -> AxisArray:
|
|
29
|
+
# Your signal processing logic here
|
|
30
|
+
return message
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class MyUnit(BaseTransformerUnit[
|
|
34
|
+
MyProcessorSettings,
|
|
35
|
+
AxisArray,
|
|
36
|
+
AxisArray,
|
|
37
|
+
MyProcessor,
|
|
38
|
+
]):
|
|
39
|
+
SETTINGS = MyProcessorSettings
|
|
40
|
+
|
|
41
|
+
(under construction)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Architecture & Usage Patterns
|
|
2
|
+
=============================
|
|
3
|
+
|
|
4
|
+
This page describes the architecture of ezmsg-sigproc and different ways to use its components.
|
|
5
|
+
|
|
6
|
+
Operating Styles: Standalone vs. Pipelines
|
|
7
|
+
------------------------------------------
|
|
8
|
+
|
|
9
|
+
While each processor is designed to be assembled into an ezmsg pipeline, the components are also well-suited for offline, ad-hoc analysis. You can instantiate processors directly in scripts or notebooks for quick prototyping or to validate results from other code. The companion Unit wrappers, however, are meant for assembling processors into a full ezmsg pipeline.
|
|
10
|
+
|
|
11
|
+
A fully defined ezmsg pipeline shines in online streaming scenarios where message routing, scheduling, and latency handling are crucial. Nevertheless, you can run the same pipeline offline—say, within a Jupyter notebook—if your analysis benefits from ezmsg's structured execution model.
|
|
12
|
+
|
|
13
|
+
Deciding between a standalone processor and a full pipeline comes down to the trade-off between simplicity and the operational overhead of the pipeline:
|
|
14
|
+
|
|
15
|
+
* **Standalone processors**: Low overhead, ideal for one-off or exploratory offline tasks.
|
|
16
|
+
* **Pipeline + Unit wrappers**: Additional setup cost but bring concurrency, standardized interfaces, and automatic message flow—useful when your offline experiment mirrors a live system or when you require fine-grained pipeline behavior.
|
|
17
|
+
|
|
18
|
+
Source Layout
|
|
19
|
+
-------------
|
|
20
|
+
|
|
21
|
+
All source resides under ``src/ezmsg/sigproc``, which contains:
|
|
22
|
+
|
|
23
|
+
* A suite of processors (for example, ``filter.py``, ``spectrogram.py``, ``spectrum.py``, ``sampler.py``)
|
|
24
|
+
* ``math/`` and ``util/`` subpackages for mathematical operations and utilities
|
|
25
|
+
|
|
26
|
+
Key Modules
|
|
27
|
+
^^^^^^^^^^^
|
|
28
|
+
|
|
29
|
+
* **base.py** (via ``ezmsg.baseproc``): Defines standard protocols—Processor, Producer, Consumer, and Transformer—that enable both stateless and stateful processing chains.
|
|
30
|
+
* **filter.py**: Provides settings dataclasses and a stateful transformer that applies supplied coefficients to incoming data.
|
|
31
|
+
* **spectrogram.py**: Implements spectral analysis using a composite transformer chaining windowing, spectrum computation, and axis adjustments.
|
|
32
|
+
|
|
33
|
+
Where to Learn Next
|
|
34
|
+
-------------------
|
|
35
|
+
|
|
36
|
+
* Study the :doc:`base` page to master the processor architecture.
|
|
37
|
+
* Explore unit tests in the repository for hands-on examples of composing processors and Units.
|
|
38
|
+
* Review the `ezmsg framework <https://www.ezmsg.org>`_ to understand the surrounding ecosystem.
|
|
39
|
+
* Experiment with the code—try running processors standalone and then integrate them into a small pipeline to observe the trade-offs firsthand.
|
|
40
|
+
|
|
41
|
+
This approach equips newcomers to choose the right level of abstraction—raw processor, Unit wrapper, or full pipeline—based on the demands of their analysis or streaming application.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Base Processors
|
|
2
|
+
===============
|
|
3
|
+
|
|
4
|
+
The base processor classes are provided by the ``ezmsg-baseproc`` package and re-exported from ``ezmsg.sigproc.base`` for backwards compatibility.
|
|
5
|
+
|
|
6
|
+
For detailed documentation on the base processor architecture, see the `ezmsg-baseproc documentation <https://www.ezmsg.org/ezmsg-baseproc/>`_.
|
|
7
|
+
|
|
8
|
+
.. note::
|
|
9
|
+
New code should import directly from ``ezmsg.baseproc`` instead of ``ezmsg.sigproc.base``.
|
|
10
|
+
|
|
11
|
+
API Reference
|
|
12
|
+
-------------
|
|
13
|
+
|
|
14
|
+
The following classes are re-exported from ``ezmsg.baseproc``:
|
|
15
|
+
|
|
16
|
+
.. automodule:: ezmsg.sigproc.base
|
|
17
|
+
:members:
|
|
18
|
+
:show-inheritance:
|
|
19
|
+
:imported-members:
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
```{include} ../../README.md
|
|
2
|
+
```
|
|
3
|
+
|
|
4
|
+
## Documentation
|
|
5
|
+
|
|
6
|
+
```{toctree}
|
|
7
|
+
:maxdepth: 2
|
|
8
|
+
:caption: Contents:
|
|
9
|
+
|
|
10
|
+
guides/sigproc/content-sigproc
|
|
11
|
+
guides/HybridBuffer
|
|
12
|
+
guides/how-tos/signalprocessing/content-signalprocessing
|
|
13
|
+
guides/tutorials/signalprocessing
|
|
14
|
+
api/index
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Indices and tables
|
|
18
|
+
|
|
19
|
+
- {ref}`genindex`
|
|
20
|
+
- {ref}`modindex`
|
|
@@ -13,6 +13,7 @@ requires-python = ">=3.10.15"
|
|
|
13
13
|
dynamic = ["version"]
|
|
14
14
|
dependencies = [
|
|
15
15
|
"array-api-compat>=1.11.1",
|
|
16
|
+
"ezmsg-baseproc>=1.1.0",
|
|
16
17
|
"ezmsg>=3.6.0",
|
|
17
18
|
"numba>=0.61.0",
|
|
18
19
|
"numpy>=1.26.0",
|
|
@@ -64,5 +65,24 @@ version-file = "src/ezmsg/sigproc/__version__.py"
|
|
|
64
65
|
packages = ["src/ezmsg"]
|
|
65
66
|
|
|
66
67
|
[tool.pytest.ini_options]
|
|
68
|
+
asyncio_mode = "auto"
|
|
69
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
67
70
|
norecursedirs = "tests/helpers"
|
|
68
71
|
addopts = "-p no:warnings"
|
|
72
|
+
|
|
73
|
+
[tool.ruff]
|
|
74
|
+
line-length = 120
|
|
75
|
+
target-version = "py310"
|
|
76
|
+
# Exclude auto-generated files
|
|
77
|
+
exclude = ["*/__version__.py"]
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint]
|
|
80
|
+
select = ["E", "F", "I", "W"]
|
|
81
|
+
|
|
82
|
+
[tool.ruff.lint.isort]
|
|
83
|
+
known-first-party = ["ezmsg.sigproc"]
|
|
84
|
+
known-third-party = ["ezmsg", "ezmsg.baseproc"]
|
|
85
|
+
|
|
86
|
+
[tool.uv.sources]
|
|
87
|
+
# Uncomment to use development version of ezmsg from git
|
|
88
|
+
#ezmsg = { git = "https://github.com/ezmsg-org/ezmsg.git", branch = "dev" }
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.
|
|
32
|
-
__version_tuple__ = version_tuple = (2,
|
|
31
|
+
__version__ = version = '2.7.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 7, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import scipy.special
|
|
2
1
|
import ezmsg.core as ez
|
|
2
|
+
import scipy.special
|
|
3
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
3
4
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
4
5
|
from ezmsg.util.messages.util import replace
|
|
5
6
|
|
|
6
7
|
from .spectral import OptionsEnum
|
|
7
|
-
from .base import BaseTransformer, BaseTransformerUnit
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class ActivationFunction(OptionsEnum):
|
|
@@ -50,20 +50,14 @@ class ActivationTransformer(BaseTransformer[ActivationSettings, AxisArray, AxisA
|
|
|
50
50
|
# str type handling
|
|
51
51
|
function = self.settings.function.lower()
|
|
52
52
|
if function not in ActivationFunction.options():
|
|
53
|
-
raise ValueError(
|
|
54
|
-
|
|
55
|
-
)
|
|
56
|
-
function = list(ACTIVATIONS.keys())[
|
|
57
|
-
ActivationFunction.options().index(function)
|
|
58
|
-
]
|
|
53
|
+
raise ValueError(f"Unrecognized activation function {function}. Must be one of {ACTIVATIONS.keys()}")
|
|
54
|
+
function = list(ACTIVATIONS.keys())[ActivationFunction.options().index(function)]
|
|
59
55
|
func = ACTIVATIONS[function]
|
|
60
56
|
|
|
61
57
|
return replace(message, data=func(message.data))
|
|
62
58
|
|
|
63
59
|
|
|
64
|
-
class Activation(
|
|
65
|
-
BaseTransformerUnit[ActivationSettings, AxisArray, AxisArray, ActivationTransformer]
|
|
66
|
-
):
|
|
60
|
+
class Activation(BaseTransformerUnit[ActivationSettings, AxisArray, AxisArray, ActivationTransformer]):
|
|
67
61
|
SETTINGS = ActivationSettings
|
|
68
62
|
|
|
69
63
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import ezmsg.core as ez
|
|
1
2
|
import numpy as np
|
|
2
3
|
import numpy.typing as npt
|
|
3
4
|
import scipy.signal
|
|
4
|
-
|
|
5
|
+
from ezmsg.baseproc import BaseStatefulTransformer, processor_state
|
|
5
6
|
from ezmsg.util.messages.axisarray import AxisArray, CoordinateAxis
|
|
6
7
|
from ezmsg.util.messages.util import replace
|
|
7
8
|
|
|
8
|
-
from .base import processor_state, BaseStatefulTransformer
|
|
9
|
-
|
|
10
9
|
|
|
11
10
|
class AdaptiveLatticeNotchFilterSettings(ez.Settings):
|
|
12
11
|
"""Settings for the Adaptive Lattice Notch Filter."""
|
|
@@ -76,9 +75,7 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
76
75
|
|
|
77
76
|
fs = 1 / message.axes[self.settings.axis].gain
|
|
78
77
|
init_f = (
|
|
79
|
-
self.settings.init_notch_freq
|
|
80
|
-
if self.settings.init_notch_freq is not None
|
|
81
|
-
else 0.07178314656435313 * fs
|
|
78
|
+
self.settings.init_notch_freq if self.settings.init_notch_freq is not None else 0.07178314656435313 * fs
|
|
82
79
|
)
|
|
83
80
|
init_omega = init_f * (2 * np.pi) / fs
|
|
84
81
|
init_k1 = -np.cos(init_omega)
|
|
@@ -91,9 +88,7 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
91
88
|
self._state.k1 = init_k1 + np.zeros(sample_shape, dtype=float)
|
|
92
89
|
self._state.freq_template = CoordinateAxis(
|
|
93
90
|
data=np.zeros((0,) + sample_shape, dtype=float),
|
|
94
|
-
dims=[self.settings.axis]
|
|
95
|
-
+ message.dims[:ax_idx]
|
|
96
|
-
+ message.dims[ax_idx + 1 :],
|
|
91
|
+
dims=[self.settings.axis] + message.dims[:ax_idx] + message.dims[ax_idx + 1 :],
|
|
97
92
|
unit="Hz",
|
|
98
93
|
)
|
|
99
94
|
|
|
@@ -147,9 +142,7 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
147
142
|
for ix, k in enumerate(self._state.k1.flatten()):
|
|
148
143
|
# Filter to get s_n (notch filter state)
|
|
149
144
|
a_s = [1, k * gamma_plus_1, gamma]
|
|
150
|
-
s_n[:, ix], self._state.zi[:, ix] = scipy.signal.lfilter(
|
|
151
|
-
[1], a_s, _x[:, ix], zi=self._state.zi[:, ix]
|
|
152
|
-
)
|
|
145
|
+
s_n[:, ix], self._state.zi[:, ix] = scipy.signal.lfilter([1], a_s, _x[:, ix], zi=self._state.zi[:, ix])
|
|
153
146
|
|
|
154
147
|
# Apply output filter to get y_out
|
|
155
148
|
b_y = [1, 2 * k, 1]
|
|
@@ -159,17 +152,11 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
159
152
|
s_n_reshaped = s_n.reshape((s_n.shape[0],) + x_data.shape[1:])
|
|
160
153
|
s_final = s_n_reshaped[-1] # Current s_n
|
|
161
154
|
s_final_1 = s_n_reshaped[-2] # s_n_1
|
|
162
|
-
s_final_2 = (
|
|
163
|
-
s_n_reshaped[-3] if len(s_n_reshaped) > 2 else self._state.s_history[0]
|
|
164
|
-
) # s_n_2
|
|
155
|
+
s_final_2 = s_n_reshaped[-3] if len(s_n_reshaped) > 2 else self._state.s_history[0] # s_n_2
|
|
165
156
|
|
|
166
157
|
# Update p and q using final values
|
|
167
|
-
self._state.p = eta * self._state.p + one_minus_eta * (
|
|
168
|
-
|
|
169
|
-
)
|
|
170
|
-
self._state.q = eta * self._state.q + one_minus_eta * (
|
|
171
|
-
2 * (s_final_1 * s_final_1)
|
|
172
|
-
)
|
|
158
|
+
self._state.p = eta * self._state.p + one_minus_eta * (s_final_1 * (s_final + s_final_2))
|
|
159
|
+
self._state.q = eta * self._state.q + one_minus_eta * (2 * (s_final_1 * s_final_1))
|
|
173
160
|
|
|
174
161
|
# Update reflection coefficient
|
|
175
162
|
new_k1 = -self._state.p / (self._state.q + 1e-8) # Avoid division by zero
|
|
@@ -199,17 +186,11 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
199
186
|
y_out[sample_ix] = s_n + 2 * self._state.k1 * s_n_1 + s_n_2
|
|
200
187
|
|
|
201
188
|
# Update filter parameters
|
|
202
|
-
self._state.p = eta * self._state.p + one_minus_eta * (
|
|
203
|
-
|
|
204
|
-
)
|
|
205
|
-
self._state.q = eta * self._state.q + one_minus_eta * (
|
|
206
|
-
2 * (s_n_1 * s_n_1)
|
|
207
|
-
)
|
|
189
|
+
self._state.p = eta * self._state.p + one_minus_eta * (s_n_1 * (s_n + s_n_2))
|
|
190
|
+
self._state.q = eta * self._state.q + one_minus_eta * (2 * (s_n_1 * s_n_1))
|
|
208
191
|
|
|
209
192
|
# Update reflection coefficient
|
|
210
|
-
new_k1 = -self._state.p / (
|
|
211
|
-
self._state.q + 1e-8
|
|
212
|
-
) # Avoid division by zero
|
|
193
|
+
new_k1 = -self._state.p / (self._state.q + 1e-8) # Avoid division by zero
|
|
213
194
|
new_k1 = np.clip(new_k1, -1, 1) # Clip to prevent instability
|
|
214
195
|
self._state.k1 = mu * self._state.k1 + one_minus_mu * new_k1 # Smoothed
|
|
215
196
|
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
|
+
import ezmsg.core as ez
|
|
4
5
|
import numpy as np
|
|
5
6
|
import numpy.typing as npt
|
|
6
|
-
|
|
7
|
-
from ezmsg.util.messages.axisarray import AxisArray, AxisBase
|
|
8
|
-
from ezmsg.util.messages.util import replace
|
|
9
|
-
|
|
10
|
-
from .base import (
|
|
7
|
+
from ezmsg.baseproc import (
|
|
11
8
|
BaseStatefulTransformer,
|
|
12
|
-
BaseTransformerUnit,
|
|
13
9
|
BaseTransformer,
|
|
10
|
+
BaseTransformerUnit,
|
|
14
11
|
processor_state,
|
|
15
12
|
)
|
|
13
|
+
from ezmsg.util.messages.axisarray import AxisArray, AxisBase
|
|
14
|
+
from ezmsg.util.messages.util import replace
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
class AffineTransformSettings(ez.Settings):
|
|
@@ -38,15 +37,12 @@ class AffineTransformState:
|
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
class AffineTransformTransformer(
|
|
41
|
-
BaseStatefulTransformer[
|
|
42
|
-
AffineTransformSettings, AxisArray, AxisArray, AffineTransformState
|
|
43
|
-
]
|
|
40
|
+
BaseStatefulTransformer[AffineTransformSettings, AxisArray, AxisArray, AffineTransformState]
|
|
44
41
|
):
|
|
45
42
|
def __call__(self, message: AxisArray) -> AxisArray:
|
|
46
43
|
# Override __call__ so we can shortcut if weights are None.
|
|
47
44
|
if self.settings.weights is None or (
|
|
48
|
-
isinstance(self.settings.weights, str)
|
|
49
|
-
and self.settings.weights == "passthrough"
|
|
45
|
+
isinstance(self.settings.weights, str) and self.settings.weights == "passthrough"
|
|
50
46
|
):
|
|
51
47
|
return message
|
|
52
48
|
return super().__call__(message)
|
|
@@ -68,18 +64,12 @@ class AffineTransformTransformer(
|
|
|
68
64
|
self._state.weights = weights
|
|
69
65
|
|
|
70
66
|
axis = self.settings.axis or message.dims[-1]
|
|
71
|
-
if (
|
|
72
|
-
axis in message.axes
|
|
73
|
-
and hasattr(message.axes[axis], "data")
|
|
74
|
-
and weights.shape[0] != weights.shape[1]
|
|
75
|
-
):
|
|
67
|
+
if axis in message.axes and hasattr(message.axes[axis], "data") and weights.shape[0] != weights.shape[1]:
|
|
76
68
|
in_labels = message.axes[axis].data
|
|
77
69
|
new_labels = []
|
|
78
70
|
n_in, n_out = weights.shape
|
|
79
71
|
if len(in_labels) != n_in:
|
|
80
|
-
ez.logger.warning(
|
|
81
|
-
f"Received {len(in_labels)} for {n_in} inputs. Check upstream labels."
|
|
82
|
-
)
|
|
72
|
+
ez.logger.warning(f"Received {len(in_labels)} for {n_in} inputs. Check upstream labels.")
|
|
83
73
|
else:
|
|
84
74
|
b_filled_outputs = np.any(weights, axis=0)
|
|
85
75
|
b_used_inputs = np.any(weights, axis=1)
|
|
@@ -97,9 +87,7 @@ class AffineTransformTransformer(
|
|
|
97
87
|
elif np.all(b_filled_outputs):
|
|
98
88
|
new_labels = np.array(in_labels)[b_used_inputs]
|
|
99
89
|
|
|
100
|
-
self._state.new_axis = replace(
|
|
101
|
-
message.axes[axis], data=np.array(new_labels)
|
|
102
|
-
)
|
|
90
|
+
self._state.new_axis = replace(message.axes[axis], data=np.array(new_labels))
|
|
103
91
|
|
|
104
92
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
105
93
|
axis = self.settings.axis or message.dims[-1]
|
|
@@ -110,9 +98,7 @@ class AffineTransformTransformer(
|
|
|
110
98
|
# The weights are stacked A|B where A is the transform and B is a single row
|
|
111
99
|
# in the equation y = Ax + B. This supports NeuroKey's weights matrices.
|
|
112
100
|
sample_shape = data.shape[:axis_idx] + (1,) + data.shape[axis_idx + 1 :]
|
|
113
|
-
data = np.concatenate(
|
|
114
|
-
(data, np.ones(sample_shape).astype(data.dtype)), axis=axis_idx
|
|
115
|
-
)
|
|
101
|
+
data = np.concatenate((data, np.ones(sample_shape).astype(data.dtype)), axis=axis_idx)
|
|
116
102
|
|
|
117
103
|
if axis_idx in [-1, len(message.dims) - 1]:
|
|
118
104
|
data = np.matmul(data, self._state.weights)
|
|
@@ -128,11 +114,7 @@ class AffineTransformTransformer(
|
|
|
128
114
|
return replace(message, **replace_kwargs)
|
|
129
115
|
|
|
130
116
|
|
|
131
|
-
class AffineTransform(
|
|
132
|
-
BaseTransformerUnit[
|
|
133
|
-
AffineTransformSettings, AxisArray, AxisArray, AffineTransformTransformer
|
|
134
|
-
]
|
|
135
|
-
):
|
|
117
|
+
class AffineTransform(BaseTransformerUnit[AffineTransformSettings, AxisArray, AxisArray, AffineTransformTransformer]):
|
|
136
118
|
SETTINGS = AffineTransformSettings
|
|
137
119
|
|
|
138
120
|
|
|
@@ -153,9 +135,7 @@ def affine_transform(
|
|
|
153
135
|
:obj:`AffineTransformTransformer`.
|
|
154
136
|
"""
|
|
155
137
|
return AffineTransformTransformer(
|
|
156
|
-
AffineTransformSettings(
|
|
157
|
-
weights=weights, axis=axis, right_multiply=right_multiply
|
|
158
|
-
)
|
|
138
|
+
AffineTransformSettings(weights=weights, axis=axis, right_multiply=right_multiply)
|
|
159
139
|
)
|
|
160
140
|
|
|
161
141
|
|
|
@@ -178,9 +158,7 @@ class CommonRereferenceSettings(ez.Settings):
|
|
|
178
158
|
"""Set False to exclude each channel from participating in the calculation of its reference."""
|
|
179
159
|
|
|
180
160
|
|
|
181
|
-
class CommonRereferenceTransformer(
|
|
182
|
-
BaseTransformer[CommonRereferenceSettings, AxisArray, AxisArray]
|
|
183
|
-
):
|
|
161
|
+
class CommonRereferenceTransformer(BaseTransformer[CommonRereferenceSettings, AxisArray, AxisArray]):
|
|
184
162
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
185
163
|
if self.settings.mode == "passthrough":
|
|
186
164
|
return message
|
|
@@ -188,9 +166,7 @@ class CommonRereferenceTransformer(
|
|
|
188
166
|
axis = self.settings.axis or message.dims[-1]
|
|
189
167
|
axis_idx = message.get_axis_idx(axis)
|
|
190
168
|
|
|
191
|
-
func = {"mean": np.mean, "median": np.median, "passthrough": zeros_for_noop}[
|
|
192
|
-
self.settings.mode
|
|
193
|
-
]
|
|
169
|
+
func = {"mean": np.mean, "median": np.median, "passthrough": zeros_for_noop}[self.settings.mode]
|
|
194
170
|
|
|
195
171
|
ref_data = func(message.data, axis=axis_idx, keepdims=True)
|
|
196
172
|
|
|
@@ -213,9 +189,7 @@ class CommonRereferenceTransformer(
|
|
|
213
189
|
|
|
214
190
|
|
|
215
191
|
class CommonRereference(
|
|
216
|
-
BaseTransformerUnit[
|
|
217
|
-
CommonRereferenceSettings, AxisArray, AxisArray, CommonRereferenceTransformer
|
|
218
|
-
]
|
|
192
|
+
BaseTransformerUnit[CommonRereferenceSettings, AxisArray, AxisArray, CommonRereferenceTransformer]
|
|
219
193
|
):
|
|
220
194
|
SETTINGS = CommonRereferenceSettings
|
|
221
195
|
|