ezmsg-sigproc 2.5.0__tar.gz → 2.6.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.6.0}/.github/workflows/docs.yml +5 -4
- ezmsg_sigproc-2.6.0/.github/workflows/python-publish.yml +29 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/.pre-commit-config.yaml +1 -2
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/PKG-INFO +3 -2
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/conf.py +1 -0
- ezmsg_sigproc-2.6.0/docs/source/guides/ProcessorsBase.md +58 -0
- ezmsg_sigproc-2.6.0/docs/source/guides/how-tos/signalprocessing/composite.rst +6 -0
- ezmsg_sigproc-2.6.0/docs/source/guides/how-tos/signalprocessing/processor.rst +8 -0
- ezmsg_sigproc-2.6.0/docs/source/guides/how-tos/signalprocessing/stateful.rst +8 -0
- ezmsg_sigproc-2.6.0/docs/source/guides/how-tos/signalprocessing/unit.rst +41 -0
- ezmsg_sigproc-2.6.0/docs/source/guides/sigproc/base.rst +19 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/pyproject.toml +20 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/__version__.py +2 -2
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/activation.py +5 -11
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/adaptive_lattice_notch.py +11 -29
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/affinetransform.py +13 -38
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/aggregate.py +13 -30
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/bandpower.py +7 -15
- ezmsg_sigproc-2.6.0/src/ezmsg/sigproc/base.py +149 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/butterworthfilter.py +8 -16
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/butterworthzerophase.py +7 -16
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/cheby.py +4 -10
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/combfilter.py +5 -8
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/decimate.py +2 -6
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/denormalize.py +6 -11
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/detrend.py +3 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/diff.py +8 -17
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/downsample.py +6 -14
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/ewma.py +11 -27
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/extract_axis.py +3 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/fbcca.py +31 -56
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/filter.py +19 -45
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/filterbank.py +33 -70
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/filterbankdesign.py +5 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/fir_hilbert.py +13 -30
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/fir_pmc.py +5 -10
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/firfilter.py +12 -14
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/gaussiansmoothing.py +5 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/kaiser.py +11 -15
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/math/abs.py +1 -3
- ezmsg_sigproc-2.6.0/src/ezmsg/sigproc/math/add.py +121 -0
- ezmsg_sigproc-2.6.0/src/ezmsg/sigproc/math/difference.py +135 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/math/invert.py +1 -3
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/math/log.py +2 -6
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/messages.py +1 -2
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/quantize.py +2 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/resample.py +13 -34
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/rollingscaler.py +12 -37
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/sampler.py +17 -35
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/scaler.py +8 -18
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/signalinjector.py +6 -16
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/slicer.py +9 -28
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/spectrogram.py +12 -19
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/spectrum.py +12 -32
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/transpose.py +7 -18
- ezmsg_sigproc-2.6.0/src/ezmsg/sigproc/util/asio.py +25 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/util/axisarray_buffer.py +10 -26
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/util/buffer.py +18 -43
- ezmsg_sigproc-2.6.0/src/ezmsg/sigproc/util/message.py +17 -0
- ezmsg_sigproc-2.6.0/src/ezmsg/sigproc/util/profile.py +23 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/util/sparse.py +5 -15
- ezmsg_sigproc-2.6.0/src/ezmsg/sigproc/util/typeresolution.py +17 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/wavelets.py +6 -15
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/window.py +24 -78
- ezmsg_sigproc-2.6.0/tests/helpers/synth.py +281 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/helpers/util.py +9 -18
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/bytewax/test_spectrum_bytewax.py +4 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/bytewax/test_window_bytewax.py +3 -3
- ezmsg_sigproc-2.6.0/tests/integration/ezmsg/test_add_system.py +148 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_butterworth_system.py +10 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_butterworthzerophase_system.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_decimate_system.py +5 -8
- ezmsg_sigproc-2.6.0/tests/integration/ezmsg/test_difference_system.py +203 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_downsample_system.py +10 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_filter_system.py +9 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_fir_hilbert_system.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_fir_pmc_system.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_rollingscaler_system.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_sampler_system.py +7 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_scaler_system.py +8 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_spectrum_system.py +9 -10
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/integration/ezmsg/test_window_system.py +10 -17
- ezmsg_sigproc-2.6.0/tests/test_profile.py +39 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/buffer/test_axisarray_buffer.py +6 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/buffer/test_buffer.py +2 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/buffer/test_buffer_overflow.py +10 -21
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_activation.py +4 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_adaptive_lattice_notch.py +5 -17
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_affine_transform.py +2 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_aggregate.py +20 -56
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_bandpower.py +2 -2
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_base.py +52 -133
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_butter.py +8 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_butterworthzerophase.py +7 -21
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_combfilter.py +9 -24
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_denormalize.py +7 -20
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_diff.py +3 -3
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_downsample.py +5 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_ewma.py +4 -8
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_extract_axis.py +3 -5
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_fbcca.py +27 -85
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_filter.py +2 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_filterbank.py +5 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_filterbankdesign.py +22 -63
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_fir_hilbert.py +4 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_firfilter.py +2 -4
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_gaussian_smoothing_filter.py +6 -14
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_kaiser.py +4 -10
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_math.py +1 -4
- ezmsg_sigproc-2.6.0/tests/unit/test_math_add.py +247 -0
- ezmsg_sigproc-2.6.0/tests/unit/test_math_difference.py +278 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_quantize.py +4 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_resample.py +8 -22
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_rollingscaler.py +5 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_sampler.py +9 -25
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_scaler.py +4 -7
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_slicer.py +5 -12
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_spectrogram.py +6 -13
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_spectrum.py +14 -29
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_transpose.py +2 -5
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_util.py +17 -51
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/unit/test_wavelets.py +4 -9
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.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/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/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.6.0}/.github/workflows/python-tests.yml +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/.gitignore +0 -0
- ezmsg_sigproc-2.5.0/LICENSE.txt → ezmsg_sigproc-2.6.0/LICENSE +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/README.md +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/Makefile +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/make.bat +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/_templates/autosummary/module.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/api/index.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/HybridBuffer.md +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/explanations/sigproc.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/how-tos/signalprocessing/adaptive.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/how-tos/signalprocessing/checkpoint.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/how-tos/signalprocessing/content-signalprocessing.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/how-tos/signalprocessing/standalone.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/img/HybridBufferBasic.svg +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/img/HybridBufferOverflow.svg +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/sigproc/content-sigproc.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/sigproc/processors.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/sigproc/units.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/guides/tutorials/signalprocessing.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/docs/source/index.rst +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/ewmfilter.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/math/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/math/clip.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/math/scale.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/spectral.py +3 -3
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/src/ezmsg/sigproc/util/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/conftest.py +1 -1
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/helpers/__init__.py +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.0}/tests/resources/xform.csv +0 -0
- {ezmsg_sigproc-2.5.0 → ezmsg_sigproc-2.6.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
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ezmsg-sigproc
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Timeseries signal processing implementations in ezmsg
|
|
5
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
6
|
License-Expression: MIT
|
|
7
|
-
License-File: LICENSE
|
|
7
|
+
License-File: LICENSE
|
|
8
8
|
Requires-Python: >=3.10.15
|
|
9
9
|
Requires-Dist: array-api-compat>=1.11.1
|
|
10
|
+
Requires-Dist: ezmsg-baseproc>=1.0.3
|
|
10
11
|
Requires-Dist: ezmsg>=3.6.0
|
|
11
12
|
Requires-Dist: numba>=0.61.0
|
|
12
13
|
Requires-Dist: numpy>=1.26.0
|
|
@@ -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,58 @@
|
|
|
1
|
+
## Signal Processor Base Classes
|
|
2
|
+
|
|
3
|
+
The signal processors in `ezmsg-sigproc` are built on top of the base processor classes from `ezmsg-baseproc`. For comprehensive documentation on the processor architecture, including protocols, base classes, and implementation patterns, see the [ezmsg-baseproc documentation](https://www.ezmsg.org/ezmsg-baseproc/guides/ProcessorsBase.html).
|
|
4
|
+
|
|
5
|
+
### Quick Reference
|
|
6
|
+
|
|
7
|
+
The base classes provide a consistent pattern for building message processors:
|
|
8
|
+
|
|
9
|
+
* **Processors** - Transform input messages to output messages
|
|
10
|
+
* **Producers** - Generate output messages without requiring input
|
|
11
|
+
* **Consumers** - Accept input messages without producing output
|
|
12
|
+
* **Transformers** - A specific type of processor with typed input/output
|
|
13
|
+
* **Stateful variants** - Processors that maintain state across invocations
|
|
14
|
+
* **Adaptive transformers** - Transformers that can be trained via `partial_fit`
|
|
15
|
+
* **Composite processors** - Chain multiple processors together efficiently
|
|
16
|
+
|
|
17
|
+
### Signal Processing Example
|
|
18
|
+
|
|
19
|
+
Here's an example of a signal processing transformer using `AxisArray`:
|
|
20
|
+
|
|
21
|
+
```Python
|
|
22
|
+
import ezmsg.core as ez
|
|
23
|
+
from ezmsg.util.messages.axisarray import AxisArray, replace
|
|
24
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ScaleTransformerSettings(ez.Settings):
|
|
28
|
+
scale: float = 1.0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ScaleTransformer(BaseTransformer[ScaleTransformerSettings, AxisArray, AxisArray]):
|
|
32
|
+
def _process(self, message: AxisArray) -> AxisArray:
|
|
33
|
+
return replace(message, data=message.data * self.settings.scale)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ScaleUnit(BaseTransformerUnit[
|
|
37
|
+
ScaleTransformerSettings,
|
|
38
|
+
AxisArray,
|
|
39
|
+
AxisArray,
|
|
40
|
+
ScaleTransformer,
|
|
41
|
+
]):
|
|
42
|
+
SETTINGS = ScaleTransformerSettings
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Existing Signal Processors
|
|
46
|
+
|
|
47
|
+
For examples of signal processing implementations, see the processors in `ezmsg.sigproc`:
|
|
48
|
+
|
|
49
|
+
* **Filtering** - `ChebyshevFilterTransformer`, `CombFilterTransformer`, etc.
|
|
50
|
+
* **Spectral** - `SpectrogramTransformer`, `SpectrumTransformer`, `WaveletTransformer`
|
|
51
|
+
* **Resampling** - `DownsampleTransformer`, `DecimateTransformer`, `ResampleTransformer`
|
|
52
|
+
* **Windowing** - `WindowTransformer`, `AggregateTransformer`
|
|
53
|
+
* **Math** - `ScalerTransformer`, `LogTransformer`, `AbsTransformer`, `DifferenceTransformer`
|
|
54
|
+
|
|
55
|
+
### Learn More
|
|
56
|
+
|
|
57
|
+
* [Processor Base Classes (ezmsg-baseproc)](https://www.ezmsg.org/ezmsg-baseproc/guides/ProcessorsBase.html) - Full documentation on the processor architecture
|
|
58
|
+
* [API Reference](../api/index) - Complete API documentation for ezmsg-sigproc
|
|
@@ -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,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:
|
|
@@ -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.0.3",
|
|
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.6.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 6, 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
3
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
4
4
|
from ezmsg.util.messages.util import replace
|
|
5
5
|
|
|
6
|
-
from .spectral import OptionsEnum
|
|
7
6
|
from .base import BaseTransformer, BaseTransformerUnit
|
|
7
|
+
from .spectral import OptionsEnum
|
|
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,11 +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
|
-
import ezmsg.core as ez
|
|
5
5
|
from ezmsg.util.messages.axisarray import AxisArray, CoordinateAxis
|
|
6
6
|
from ezmsg.util.messages.util import replace
|
|
7
7
|
|
|
8
|
-
from .base import
|
|
8
|
+
from .base import BaseStatefulTransformer, processor_state
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class AdaptiveLatticeNotchFilterSettings(ez.Settings):
|
|
@@ -76,9 +76,7 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
76
76
|
|
|
77
77
|
fs = 1 / message.axes[self.settings.axis].gain
|
|
78
78
|
init_f = (
|
|
79
|
-
self.settings.init_notch_freq
|
|
80
|
-
if self.settings.init_notch_freq is not None
|
|
81
|
-
else 0.07178314656435313 * fs
|
|
79
|
+
self.settings.init_notch_freq if self.settings.init_notch_freq is not None else 0.07178314656435313 * fs
|
|
82
80
|
)
|
|
83
81
|
init_omega = init_f * (2 * np.pi) / fs
|
|
84
82
|
init_k1 = -np.cos(init_omega)
|
|
@@ -91,9 +89,7 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
91
89
|
self._state.k1 = init_k1 + np.zeros(sample_shape, dtype=float)
|
|
92
90
|
self._state.freq_template = CoordinateAxis(
|
|
93
91
|
data=np.zeros((0,) + sample_shape, dtype=float),
|
|
94
|
-
dims=[self.settings.axis]
|
|
95
|
-
+ message.dims[:ax_idx]
|
|
96
|
-
+ message.dims[ax_idx + 1 :],
|
|
92
|
+
dims=[self.settings.axis] + message.dims[:ax_idx] + message.dims[ax_idx + 1 :],
|
|
97
93
|
unit="Hz",
|
|
98
94
|
)
|
|
99
95
|
|
|
@@ -147,9 +143,7 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
147
143
|
for ix, k in enumerate(self._state.k1.flatten()):
|
|
148
144
|
# Filter to get s_n (notch filter state)
|
|
149
145
|
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
|
-
)
|
|
146
|
+
s_n[:, ix], self._state.zi[:, ix] = scipy.signal.lfilter([1], a_s, _x[:, ix], zi=self._state.zi[:, ix])
|
|
153
147
|
|
|
154
148
|
# Apply output filter to get y_out
|
|
155
149
|
b_y = [1, 2 * k, 1]
|
|
@@ -159,17 +153,11 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
159
153
|
s_n_reshaped = s_n.reshape((s_n.shape[0],) + x_data.shape[1:])
|
|
160
154
|
s_final = s_n_reshaped[-1] # Current s_n
|
|
161
155
|
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
|
|
156
|
+
s_final_2 = s_n_reshaped[-3] if len(s_n_reshaped) > 2 else self._state.s_history[0] # s_n_2
|
|
165
157
|
|
|
166
158
|
# 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
|
-
)
|
|
159
|
+
self._state.p = eta * self._state.p + one_minus_eta * (s_final_1 * (s_final + s_final_2))
|
|
160
|
+
self._state.q = eta * self._state.q + one_minus_eta * (2 * (s_final_1 * s_final_1))
|
|
173
161
|
|
|
174
162
|
# Update reflection coefficient
|
|
175
163
|
new_k1 = -self._state.p / (self._state.q + 1e-8) # Avoid division by zero
|
|
@@ -199,17 +187,11 @@ class AdaptiveLatticeNotchFilterTransformer(
|
|
|
199
187
|
y_out[sample_ix] = s_n + 2 * self._state.k1 * s_n_1 + s_n_2
|
|
200
188
|
|
|
201
189
|
# 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
|
-
)
|
|
190
|
+
self._state.p = eta * self._state.p + one_minus_eta * (s_n_1 * (s_n + s_n_2))
|
|
191
|
+
self._state.q = eta * self._state.q + one_minus_eta * (2 * (s_n_1 * s_n_1))
|
|
208
192
|
|
|
209
193
|
# Update reflection coefficient
|
|
210
|
-
new_k1 = -self._state.p / (
|
|
211
|
-
self._state.q + 1e-8
|
|
212
|
-
) # Avoid division by zero
|
|
194
|
+
new_k1 = -self._state.p / (self._state.q + 1e-8) # Avoid division by zero
|
|
213
195
|
new_k1 = np.clip(new_k1, -1, 1) # Clip to prevent instability
|
|
214
196
|
self._state.k1 = mu * self._state.k1 + one_minus_mu * new_k1 # Smoothed
|
|
215
197
|
|
|
@@ -1,16 +1,16 @@
|
|
|
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
|
-
import ezmsg.core as ez
|
|
7
7
|
from ezmsg.util.messages.axisarray import AxisArray, AxisBase
|
|
8
8
|
from ezmsg.util.messages.util import replace
|
|
9
9
|
|
|
10
10
|
from .base import (
|
|
11
11
|
BaseStatefulTransformer,
|
|
12
|
-
BaseTransformerUnit,
|
|
13
12
|
BaseTransformer,
|
|
13
|
+
BaseTransformerUnit,
|
|
14
14
|
processor_state,
|
|
15
15
|
)
|
|
16
16
|
|
|
@@ -38,15 +38,12 @@ class AffineTransformState:
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
class AffineTransformTransformer(
|
|
41
|
-
BaseStatefulTransformer[
|
|
42
|
-
AffineTransformSettings, AxisArray, AxisArray, AffineTransformState
|
|
43
|
-
]
|
|
41
|
+
BaseStatefulTransformer[AffineTransformSettings, AxisArray, AxisArray, AffineTransformState]
|
|
44
42
|
):
|
|
45
43
|
def __call__(self, message: AxisArray) -> AxisArray:
|
|
46
44
|
# Override __call__ so we can shortcut if weights are None.
|
|
47
45
|
if self.settings.weights is None or (
|
|
48
|
-
isinstance(self.settings.weights, str)
|
|
49
|
-
and self.settings.weights == "passthrough"
|
|
46
|
+
isinstance(self.settings.weights, str) and self.settings.weights == "passthrough"
|
|
50
47
|
):
|
|
51
48
|
return message
|
|
52
49
|
return super().__call__(message)
|
|
@@ -68,18 +65,12 @@ class AffineTransformTransformer(
|
|
|
68
65
|
self._state.weights = weights
|
|
69
66
|
|
|
70
67
|
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
|
-
):
|
|
68
|
+
if axis in message.axes and hasattr(message.axes[axis], "data") and weights.shape[0] != weights.shape[1]:
|
|
76
69
|
in_labels = message.axes[axis].data
|
|
77
70
|
new_labels = []
|
|
78
71
|
n_in, n_out = weights.shape
|
|
79
72
|
if len(in_labels) != n_in:
|
|
80
|
-
ez.logger.warning(
|
|
81
|
-
f"Received {len(in_labels)} for {n_in} inputs. Check upstream labels."
|
|
82
|
-
)
|
|
73
|
+
ez.logger.warning(f"Received {len(in_labels)} for {n_in} inputs. Check upstream labels.")
|
|
83
74
|
else:
|
|
84
75
|
b_filled_outputs = np.any(weights, axis=0)
|
|
85
76
|
b_used_inputs = np.any(weights, axis=1)
|
|
@@ -97,9 +88,7 @@ class AffineTransformTransformer(
|
|
|
97
88
|
elif np.all(b_filled_outputs):
|
|
98
89
|
new_labels = np.array(in_labels)[b_used_inputs]
|
|
99
90
|
|
|
100
|
-
self._state.new_axis = replace(
|
|
101
|
-
message.axes[axis], data=np.array(new_labels)
|
|
102
|
-
)
|
|
91
|
+
self._state.new_axis = replace(message.axes[axis], data=np.array(new_labels))
|
|
103
92
|
|
|
104
93
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
105
94
|
axis = self.settings.axis or message.dims[-1]
|
|
@@ -110,9 +99,7 @@ class AffineTransformTransformer(
|
|
|
110
99
|
# The weights are stacked A|B where A is the transform and B is a single row
|
|
111
100
|
# in the equation y = Ax + B. This supports NeuroKey's weights matrices.
|
|
112
101
|
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
|
-
)
|
|
102
|
+
data = np.concatenate((data, np.ones(sample_shape).astype(data.dtype)), axis=axis_idx)
|
|
116
103
|
|
|
117
104
|
if axis_idx in [-1, len(message.dims) - 1]:
|
|
118
105
|
data = np.matmul(data, self._state.weights)
|
|
@@ -128,11 +115,7 @@ class AffineTransformTransformer(
|
|
|
128
115
|
return replace(message, **replace_kwargs)
|
|
129
116
|
|
|
130
117
|
|
|
131
|
-
class AffineTransform(
|
|
132
|
-
BaseTransformerUnit[
|
|
133
|
-
AffineTransformSettings, AxisArray, AxisArray, AffineTransformTransformer
|
|
134
|
-
]
|
|
135
|
-
):
|
|
118
|
+
class AffineTransform(BaseTransformerUnit[AffineTransformSettings, AxisArray, AxisArray, AffineTransformTransformer]):
|
|
136
119
|
SETTINGS = AffineTransformSettings
|
|
137
120
|
|
|
138
121
|
|
|
@@ -153,9 +136,7 @@ def affine_transform(
|
|
|
153
136
|
:obj:`AffineTransformTransformer`.
|
|
154
137
|
"""
|
|
155
138
|
return AffineTransformTransformer(
|
|
156
|
-
AffineTransformSettings(
|
|
157
|
-
weights=weights, axis=axis, right_multiply=right_multiply
|
|
158
|
-
)
|
|
139
|
+
AffineTransformSettings(weights=weights, axis=axis, right_multiply=right_multiply)
|
|
159
140
|
)
|
|
160
141
|
|
|
161
142
|
|
|
@@ -178,9 +159,7 @@ class CommonRereferenceSettings(ez.Settings):
|
|
|
178
159
|
"""Set False to exclude each channel from participating in the calculation of its reference."""
|
|
179
160
|
|
|
180
161
|
|
|
181
|
-
class CommonRereferenceTransformer(
|
|
182
|
-
BaseTransformer[CommonRereferenceSettings, AxisArray, AxisArray]
|
|
183
|
-
):
|
|
162
|
+
class CommonRereferenceTransformer(BaseTransformer[CommonRereferenceSettings, AxisArray, AxisArray]):
|
|
184
163
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
185
164
|
if self.settings.mode == "passthrough":
|
|
186
165
|
return message
|
|
@@ -188,9 +167,7 @@ class CommonRereferenceTransformer(
|
|
|
188
167
|
axis = self.settings.axis or message.dims[-1]
|
|
189
168
|
axis_idx = message.get_axis_idx(axis)
|
|
190
169
|
|
|
191
|
-
func = {"mean": np.mean, "median": np.median, "passthrough": zeros_for_noop}[
|
|
192
|
-
self.settings.mode
|
|
193
|
-
]
|
|
170
|
+
func = {"mean": np.mean, "median": np.median, "passthrough": zeros_for_noop}[self.settings.mode]
|
|
194
171
|
|
|
195
172
|
ref_data = func(message.data, axis=axis_idx, keepdims=True)
|
|
196
173
|
|
|
@@ -213,9 +190,7 @@ class CommonRereferenceTransformer(
|
|
|
213
190
|
|
|
214
191
|
|
|
215
192
|
class CommonRereference(
|
|
216
|
-
BaseTransformerUnit[
|
|
217
|
-
CommonRereferenceSettings, AxisArray, AxisArray, CommonRereferenceTransformer
|
|
218
|
-
]
|
|
193
|
+
BaseTransformerUnit[CommonRereferenceSettings, AxisArray, AxisArray, CommonRereferenceTransformer]
|
|
219
194
|
):
|
|
220
195
|
SETTINGS = CommonRereferenceSettings
|
|
221
196
|
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
from array_api_compat import get_namespace
|
|
2
1
|
import typing
|
|
3
2
|
|
|
3
|
+
import ezmsg.core as ez
|
|
4
4
|
import numpy as np
|
|
5
5
|
import numpy.typing as npt
|
|
6
|
-
|
|
6
|
+
from array_api_compat import get_namespace
|
|
7
7
|
from ezmsg.util.messages.axisarray import (
|
|
8
8
|
AxisArray,
|
|
9
|
-
slice_along_axis,
|
|
10
9
|
AxisBase,
|
|
11
10
|
replace,
|
|
11
|
+
slice_along_axis,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
from .spectral import OptionsEnum
|
|
15
14
|
from .base import (
|
|
16
|
-
BaseTransformer,
|
|
17
15
|
BaseStatefulTransformer,
|
|
16
|
+
BaseTransformer,
|
|
18
17
|
BaseTransformerUnit,
|
|
19
18
|
processor_state,
|
|
20
19
|
)
|
|
20
|
+
from .spectral import OptionsEnum
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class AggregationFunction(OptionsEnum):
|
|
@@ -89,9 +89,7 @@ class RangedAggregateState:
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
class RangedAggregateTransformer(
|
|
92
|
-
BaseStatefulTransformer[
|
|
93
|
-
RangedAggregateSettings, AxisArray, AxisArray, RangedAggregateState
|
|
94
|
-
]
|
|
92
|
+
BaseStatefulTransformer[RangedAggregateSettings, AxisArray, AxisArray, RangedAggregateState]
|
|
95
93
|
):
|
|
96
94
|
def __call__(self, message: AxisArray) -> AxisArray:
|
|
97
95
|
# Override for shortcut passthrough mode.
|
|
@@ -118,16 +116,12 @@ class RangedAggregateTransformer(
|
|
|
118
116
|
if hasattr(target_axis, "data"):
|
|
119
117
|
self._state.ax_vec = target_axis.data
|
|
120
118
|
else:
|
|
121
|
-
self._state.ax_vec = target_axis.value(
|
|
122
|
-
np.arange(message.data.shape[ax_idx])
|
|
123
|
-
)
|
|
119
|
+
self._state.ax_vec = target_axis.value(np.arange(message.data.shape[ax_idx]))
|
|
124
120
|
|
|
125
121
|
ax_dat = []
|
|
126
122
|
slices = []
|
|
127
123
|
for start, stop in self.settings.bands:
|
|
128
|
-
inds = np.where(
|
|
129
|
-
np.logical_and(self._state.ax_vec >= start, self._state.ax_vec <= stop)
|
|
130
|
-
)[0]
|
|
124
|
+
inds = np.where(np.logical_and(self._state.ax_vec >= start, self._state.ax_vec <= stop))[0]
|
|
131
125
|
slices.append(np.s_[inds[0] : inds[-1] + 1])
|
|
132
126
|
if hasattr(target_axis, "data"):
|
|
133
127
|
if self._state.ax_vec.dtype.type is np.str_:
|
|
@@ -164,8 +158,7 @@ class RangedAggregateTransformer(
|
|
|
164
158
|
]
|
|
165
159
|
else:
|
|
166
160
|
out_data = [
|
|
167
|
-
agg_func(slice_along_axis(message.data, sl, axis=ax_idx), axis=ax_idx)
|
|
168
|
-
for sl in self._state.slices
|
|
161
|
+
agg_func(slice_along_axis(message.data, sl, axis=ax_idx), axis=ax_idx) for sl in self._state.slices
|
|
169
162
|
]
|
|
170
163
|
|
|
171
164
|
msg_out = replace(
|
|
@@ -187,11 +180,7 @@ class RangedAggregateTransformer(
|
|
|
187
180
|
return msg_out
|
|
188
181
|
|
|
189
182
|
|
|
190
|
-
class RangedAggregate(
|
|
191
|
-
BaseTransformerUnit[
|
|
192
|
-
RangedAggregateSettings, AxisArray, AxisArray, RangedAggregateTransformer
|
|
193
|
-
]
|
|
194
|
-
):
|
|
183
|
+
class RangedAggregate(BaseTransformerUnit[RangedAggregateSettings, AxisArray, AxisArray, RangedAggregateTransformer]):
|
|
195
184
|
SETTINGS = RangedAggregateSettings
|
|
196
185
|
|
|
197
186
|
|
|
@@ -212,9 +201,7 @@ def ranged_aggregate(
|
|
|
212
201
|
Returns:
|
|
213
202
|
:obj:`RangedAggregateTransformer`
|
|
214
203
|
"""
|
|
215
|
-
return RangedAggregateTransformer(
|
|
216
|
-
RangedAggregateSettings(axis=axis, bands=bands, operation=operation)
|
|
217
|
-
)
|
|
204
|
+
return RangedAggregateTransformer(RangedAggregateSettings(axis=axis, bands=bands, operation=operation))
|
|
218
205
|
|
|
219
206
|
|
|
220
207
|
class AggregateSettings(ez.Settings):
|
|
@@ -242,9 +229,7 @@ class AggregateTransformer(BaseTransformer[AggregateSettings, AxisArray, AxisArr
|
|
|
242
229
|
op = self.settings.operation
|
|
243
230
|
|
|
244
231
|
if op == AggregationFunction.NONE:
|
|
245
|
-
raise ValueError(
|
|
246
|
-
"AggregationFunction.NONE is not supported for full-axis aggregation"
|
|
247
|
-
)
|
|
232
|
+
raise ValueError("AggregationFunction.NONE is not supported for full-axis aggregation")
|
|
248
233
|
|
|
249
234
|
if op == AggregationFunction.TRAPEZOID:
|
|
250
235
|
# Trapezoid integration requires x-coordinates
|
|
@@ -276,9 +261,7 @@ class AggregateTransformer(BaseTransformer[AggregateSettings, AxisArray, AxisArr
|
|
|
276
261
|
)
|
|
277
262
|
|
|
278
263
|
|
|
279
|
-
class AggregateUnit(
|
|
280
|
-
BaseTransformerUnit[AggregateSettings, AxisArray, AxisArray, AggregateTransformer]
|
|
281
|
-
):
|
|
264
|
+
class AggregateUnit(BaseTransformerUnit[AggregateSettings, AxisArray, AxisArray, AggregateTransformer]):
|
|
282
265
|
"""Unit that aggregates an entire axis using a specified operation."""
|
|
283
266
|
|
|
284
267
|
SETTINGS = AggregateSettings
|