ezmsg-sigproc 2.6.0__py3-none-any.whl → 2.8.0__py3-none-any.whl

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 (49) hide show
  1. ezmsg/sigproc/__version__.py +2 -2
  2. ezmsg/sigproc/activation.py +1 -1
  3. ezmsg/sigproc/adaptive_lattice_notch.py +1 -2
  4. ezmsg/sigproc/affinetransform.py +26 -5
  5. ezmsg/sigproc/aggregate.py +6 -6
  6. ezmsg/sigproc/bandpower.py +6 -6
  7. ezmsg/sigproc/butterworthzerophase.py +1 -1
  8. ezmsg/sigproc/coordinatespaces.py +142 -0
  9. ezmsg/sigproc/decimate.py +1 -1
  10. ezmsg/sigproc/denormalize.py +3 -4
  11. ezmsg/sigproc/detrend.py +1 -1
  12. ezmsg/sigproc/diff.py +3 -4
  13. ezmsg/sigproc/downsample.py +5 -6
  14. ezmsg/sigproc/ewma.py +45 -9
  15. ezmsg/sigproc/extract_axis.py +1 -2
  16. ezmsg/sigproc/fbcca.py +4 -4
  17. ezmsg/sigproc/filter.py +3 -4
  18. ezmsg/sigproc/filterbank.py +5 -5
  19. ezmsg/sigproc/filterbankdesign.py +4 -4
  20. ezmsg/sigproc/fir_hilbert.py +1 -1
  21. ezmsg/sigproc/linear.py +118 -0
  22. ezmsg/sigproc/math/abs.py +3 -0
  23. ezmsg/sigproc/math/add.py +1 -1
  24. ezmsg/sigproc/math/clip.py +3 -0
  25. ezmsg/sigproc/math/difference.py +2 -0
  26. ezmsg/sigproc/math/invert.py +2 -0
  27. ezmsg/sigproc/math/log.py +3 -0
  28. ezmsg/sigproc/math/scale.py +2 -0
  29. ezmsg/sigproc/quantize.py +1 -2
  30. ezmsg/sigproc/resample.py +4 -4
  31. ezmsg/sigproc/rollingscaler.py +4 -4
  32. ezmsg/sigproc/sampler.py +6 -6
  33. ezmsg/sigproc/scaler.py +56 -8
  34. ezmsg/sigproc/signalinjector.py +3 -4
  35. ezmsg/sigproc/slicer.py +5 -6
  36. ezmsg/sigproc/spectrogram.py +4 -4
  37. ezmsg/sigproc/spectrum.py +5 -6
  38. ezmsg/sigproc/transpose.py +5 -6
  39. ezmsg/sigproc/util/axisarray_buffer.py +2 -0
  40. ezmsg/sigproc/util/buffer.py +4 -0
  41. ezmsg/sigproc/util/sparse.py +2 -0
  42. ezmsg/sigproc/wavelets.py +4 -4
  43. ezmsg/sigproc/window.py +5 -5
  44. ezmsg_sigproc-2.8.0.dist-info/METADATA +60 -0
  45. ezmsg_sigproc-2.8.0.dist-info/RECORD +65 -0
  46. ezmsg_sigproc-2.6.0.dist-info/METADATA +0 -73
  47. ezmsg_sigproc-2.6.0.dist-info/RECORD +0 -63
  48. {ezmsg_sigproc-2.6.0.dist-info → ezmsg_sigproc-2.8.0.dist-info}/WHEEL +0 -0
  49. {ezmsg_sigproc-2.6.0.dist-info → ezmsg_sigproc-2.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,118 @@
1
+ """Apply a linear transformation: output = scale * input + offset.
2
+
3
+ Supports per-element scale and offset along a specified axis.
4
+ Uses Array API for compatibility with numpy, cupy, torch, etc.
5
+
6
+ For full matrix transformations, use :obj:`AffineTransformTransformer` instead.
7
+ """
8
+
9
+ import ezmsg.core as ez
10
+ import numpy as np
11
+ import numpy.typing as npt
12
+ from array_api_compat import get_namespace
13
+ from ezmsg.util.messages.axisarray import AxisArray
14
+ from ezmsg.util.messages.util import replace
15
+
16
+ from .base import BaseStatefulTransformer, BaseTransformerUnit, processor_state
17
+
18
+
19
+ class LinearTransformSettings(ez.Settings):
20
+ scale: float | list[float] | npt.ArrayLike = 1.0
21
+ """Scale factor(s). Can be a scalar (applied to all elements) or an array
22
+ matching the size of the specified axis for per-element scaling."""
23
+
24
+ offset: float | list[float] | npt.ArrayLike = 0.0
25
+ """Offset value(s). Can be a scalar (applied to all elements) or an array
26
+ matching the size of the specified axis for per-element offset."""
27
+
28
+ axis: str | None = None
29
+ """Axis along which to apply per-element scale/offset. If None, scalar
30
+ scale/offset are broadcast to all elements."""
31
+
32
+
33
+ @processor_state
34
+ class LinearTransformState:
35
+ scale: npt.NDArray = None
36
+ """Prepared scale array for broadcasting."""
37
+
38
+ offset: npt.NDArray = None
39
+ """Prepared offset array for broadcasting."""
40
+
41
+
42
+ class LinearTransformTransformer(
43
+ BaseStatefulTransformer[LinearTransformSettings, AxisArray, AxisArray, LinearTransformState]
44
+ ):
45
+ """Apply linear transformation: output = scale * input + offset.
46
+
47
+ This transformer is optimized for element-wise linear operations with
48
+ optional per-channel (or per-axis) coefficients. For full matrix
49
+ transformations, use :obj:`AffineTransformTransformer` instead.
50
+
51
+ Examples:
52
+ # Uniform scaling and offset
53
+ >>> transformer = LinearTransformTransformer(LinearTransformSettings(scale=2.0, offset=1.0))
54
+
55
+ # Per-channel scaling (e.g., for 3-channel data along "ch" axis)
56
+ >>> transformer = LinearTransformTransformer(LinearTransformSettings(
57
+ ... scale=[0.5, 1.0, 2.0],
58
+ ... offset=[0.0, 0.1, 0.2],
59
+ ... axis="ch"
60
+ ... ))
61
+ """
62
+
63
+ def _hash_message(self, message: AxisArray) -> int:
64
+ """Hash based on shape and axis to detect when broadcast shapes need recalculation."""
65
+ axis = self.settings.axis
66
+ if axis is not None:
67
+ axis_idx = message.get_axis_idx(axis)
68
+ return hash((message.data.ndim, axis_idx, message.data.shape[axis_idx]))
69
+ return hash(message.data.ndim)
70
+
71
+ def _reset_state(self, message: AxisArray) -> None:
72
+ """Prepare scale/offset arrays with proper broadcast shapes."""
73
+ xp = get_namespace(message.data)
74
+ ndim = message.data.ndim
75
+
76
+ scale = self.settings.scale
77
+ offset = self.settings.offset
78
+
79
+ # Convert settings to arrays
80
+ if isinstance(scale, (list, np.ndarray)):
81
+ scale = xp.asarray(scale, dtype=xp.float64)
82
+ else:
83
+ # Scalar: create a 0-d array
84
+ scale = xp.asarray(float(scale), dtype=xp.float64)
85
+
86
+ if isinstance(offset, (list, np.ndarray)):
87
+ offset = xp.asarray(offset, dtype=xp.float64)
88
+ else:
89
+ # Scalar: create a 0-d array
90
+ offset = xp.asarray(float(offset), dtype=xp.float64)
91
+
92
+ # If axis is specified and we have 1-d arrays, reshape for proper broadcasting
93
+ if self.settings.axis is not None and ndim > 0:
94
+ axis_idx = message.get_axis_idx(self.settings.axis)
95
+
96
+ if scale.ndim == 1:
97
+ # Create shape for broadcasting: all 1s except at axis_idx
98
+ broadcast_shape = [1] * ndim
99
+ broadcast_shape[axis_idx] = scale.shape[0]
100
+ scale = xp.reshape(scale, broadcast_shape)
101
+
102
+ if offset.ndim == 1:
103
+ broadcast_shape = [1] * ndim
104
+ broadcast_shape[axis_idx] = offset.shape[0]
105
+ offset = xp.reshape(offset, broadcast_shape)
106
+
107
+ self._state.scale = scale
108
+ self._state.offset = offset
109
+
110
+ def _process(self, message: AxisArray) -> AxisArray:
111
+ result = message.data * self._state.scale + self._state.offset
112
+ return replace(message, data=result)
113
+
114
+
115
+ class LinearTransform(BaseTransformerUnit[LinearTransformSettings, AxisArray, AxisArray, LinearTransformTransformer]):
116
+ """Unit wrapper for LinearTransformTransformer."""
117
+
118
+ SETTINGS = LinearTransformSettings
ezmsg/sigproc/math/abs.py CHANGED
@@ -1,3 +1,6 @@
1
+ """Take the absolute value of the data."""
2
+ # TODO: Array API
3
+
1
4
  import numpy as np
2
5
  from ezmsg.util.messages.axisarray import AxisArray
3
6
  from ezmsg.util.messages.util import replace
ezmsg/sigproc/math/add.py CHANGED
@@ -1,4 +1,4 @@
1
- """Signal addition utilities."""
1
+ """Add 2 signals or add a constant to a signal."""
2
2
 
3
3
  import asyncio
4
4
  import typing
@@ -1,3 +1,6 @@
1
+ """Clips the data to be within the specified range."""
2
+ # TODO: Array API
3
+
1
4
  import ezmsg.core as ez
2
5
  import numpy as np
3
6
  from ezmsg.util.messages.axisarray import AxisArray
@@ -1,3 +1,5 @@
1
+ """Take the difference between 2 signals or between a signal and a constant value."""
2
+
1
3
  import asyncio
2
4
  import typing
3
5
  from dataclasses import dataclass, field
@@ -1,3 +1,5 @@
1
+ """1/data transformer."""
2
+
1
3
  from ezmsg.util.messages.axisarray import AxisArray
2
4
  from ezmsg.util.messages.util import replace
3
5
 
ezmsg/sigproc/math/log.py CHANGED
@@ -1,3 +1,6 @@
1
+ """Take the logarithm of the data."""
2
+
3
+ # TODO: Array API
1
4
  import ezmsg.core as ez
2
5
  import numpy as np
3
6
  from ezmsg.util.messages.axisarray import AxisArray
@@ -1,3 +1,5 @@
1
+ """Scale the data by a constant factor."""
2
+
1
3
  import ezmsg.core as ez
2
4
  from ezmsg.util.messages.axisarray import AxisArray
3
5
  from ezmsg.util.messages.util import replace
ezmsg/sigproc/quantize.py CHANGED
@@ -1,9 +1,8 @@
1
1
  import ezmsg.core as ez
2
2
  import numpy as np
3
+ from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
3
4
  from ezmsg.util.messages.axisarray import AxisArray, replace
4
5
 
5
- from .base import BaseTransformer, BaseTransformerUnit
6
-
7
6
 
8
7
  class QuantizeSettings(ez.Settings):
9
8
  """
ezmsg/sigproc/resample.py CHANGED
@@ -5,14 +5,14 @@ import time
5
5
  import ezmsg.core as ez
6
6
  import numpy as np
7
7
  import scipy.interpolate
8
- from ezmsg.util.messages.axisarray import AxisArray, LinearAxis
9
- from ezmsg.util.messages.util import replace
10
-
11
- from .base import (
8
+ from ezmsg.baseproc import (
12
9
  BaseConsumerUnit,
13
10
  BaseStatefulProcessor,
14
11
  processor_state,
15
12
  )
13
+ from ezmsg.util.messages.axisarray import AxisArray, LinearAxis
14
+ from ezmsg.util.messages.util import replace
15
+
16
16
  from .util.axisarray_buffer import HybridAxisArrayBuffer, HybridAxisBuffer
17
17
  from .util.buffer import UpdateStrategy
18
18
 
@@ -3,14 +3,14 @@ from collections import deque
3
3
  import ezmsg.core as ez
4
4
  import numpy as np
5
5
  import numpy.typing as npt
6
- from ezmsg.util.messages.axisarray import AxisArray
7
- from ezmsg.util.messages.util import replace
8
-
9
- from ezmsg.sigproc.base import (
6
+ from ezmsg.baseproc import (
10
7
  BaseAdaptiveTransformer,
11
8
  BaseAdaptiveTransformerUnit,
12
9
  processor_state,
13
10
  )
11
+ from ezmsg.util.messages.axisarray import AxisArray
12
+ from ezmsg.util.messages.util import replace
13
+
14
14
  from ezmsg.sigproc.sampler import SampleMessage
15
15
 
16
16
 
ezmsg/sigproc/sampler.py CHANGED
@@ -6,12 +6,7 @@ from collections import deque
6
6
 
7
7
  import ezmsg.core as ez
8
8
  import numpy as np
9
- from ezmsg.util.messages.axisarray import (
10
- AxisArray,
11
- )
12
- from ezmsg.util.messages.util import replace
13
-
14
- from .base import (
9
+ from ezmsg.baseproc import (
15
10
  BaseConsumerUnit,
16
11
  BaseProducerUnit,
17
12
  BaseStatefulProducer,
@@ -19,6 +14,11 @@ from .base import (
19
14
  BaseTransformerUnit,
20
15
  processor_state,
21
16
  )
17
+ from ezmsg.util.messages.axisarray import (
18
+ AxisArray,
19
+ )
20
+ from ezmsg.util.messages.util import replace
21
+
22
22
  from .util.axisarray_buffer import HybridAxisArrayBuffer
23
23
  from .util.buffer import UpdateStrategy
24
24
  from .util.message import SampleMessage, SampleTriggerMessage
ezmsg/sigproc/scaler.py CHANGED
@@ -1,15 +1,15 @@
1
1
  import typing
2
2
 
3
+ import ezmsg.core as ez
3
4
  import numpy as np
4
- from ezmsg.util.generator import consumer
5
- from ezmsg.util.messages.axisarray import AxisArray
6
- from ezmsg.util.messages.util import replace
7
-
8
- from .base import (
5
+ from ezmsg.baseproc import (
9
6
  BaseStatefulTransformer,
10
7
  BaseTransformerUnit,
11
8
  processor_state,
12
9
  )
10
+ from ezmsg.util.generator import consumer
11
+ from ezmsg.util.messages.axisarray import AxisArray
12
+ from ezmsg.util.messages.util import replace
13
13
 
14
14
  # Imports for backwards compatibility with previous module location
15
15
  from .ewma import EWMA_Deprecated as EWMA_Deprecated
@@ -83,11 +83,38 @@ class AdaptiveStandardScalerTransformer(
83
83
  ]
84
84
  ):
85
85
  def _reset_state(self, message: AxisArray) -> None:
86
- self._state.samps_ewma = EWMATransformer(time_constant=self.settings.time_constant, axis=self.settings.axis)
87
- self._state.vars_sq_ewma = EWMATransformer(time_constant=self.settings.time_constant, axis=self.settings.axis)
86
+ self._state.samps_ewma = EWMATransformer(
87
+ time_constant=self.settings.time_constant,
88
+ axis=self.settings.axis,
89
+ accumulate=self.settings.accumulate,
90
+ )
91
+ self._state.vars_sq_ewma = EWMATransformer(
92
+ time_constant=self.settings.time_constant,
93
+ axis=self.settings.axis,
94
+ accumulate=self.settings.accumulate,
95
+ )
96
+
97
+ @property
98
+ def accumulate(self) -> bool:
99
+ """Whether to accumulate statistics from incoming samples."""
100
+ return self.settings.accumulate
101
+
102
+ @accumulate.setter
103
+ def accumulate(self, value: bool) -> None:
104
+ """
105
+ Set the accumulate mode and propagate to child EWMA transformers.
106
+
107
+ Args:
108
+ value: If True, update statistics with each sample.
109
+ If False, only apply current statistics without updating.
110
+ """
111
+ if self._state.samps_ewma is not None:
112
+ self._state.samps_ewma.settings = replace(self._state.samps_ewma.settings, accumulate=value)
113
+ if self._state.vars_sq_ewma is not None:
114
+ self._state.vars_sq_ewma.settings = replace(self._state.vars_sq_ewma.settings, accumulate=value)
88
115
 
89
116
  def _process(self, message: AxisArray) -> AxisArray:
90
- # Update step
117
+ # Update step (respects accumulate setting via child EWMAs)
91
118
  mean_message = self._state.samps_ewma(message)
92
119
  var_sq_message = self._state.vars_sq_ewma(replace(message, data=message.data**2))
93
120
 
@@ -109,6 +136,27 @@ class AdaptiveStandardScaler(
109
136
  ):
110
137
  SETTINGS = AdaptiveStandardScalerSettings
111
138
 
139
+ @ez.subscriber(BaseTransformerUnit.INPUT_SETTINGS)
140
+ async def on_settings(self, msg: AdaptiveStandardScalerSettings) -> None:
141
+ """
142
+ Handle settings updates with smart reset behavior.
143
+
144
+ Only resets state if `axis` changes (structural change).
145
+ Changes to `time_constant` or `accumulate` are applied without
146
+ resetting accumulated statistics.
147
+ """
148
+ old_axis = self.SETTINGS.axis
149
+ self.apply_settings(msg)
150
+
151
+ if msg.axis != old_axis:
152
+ # Axis changed - need full reset
153
+ self.create_processor()
154
+ else:
155
+ # Update accumulate on processor (propagates to child EWMAs)
156
+ self.processor.accumulate = msg.accumulate
157
+ # Also update own settings reference
158
+ self.processor.settings = msg
159
+
112
160
 
113
161
  # Backwards compatibility...
114
162
  def scaler_np(time_constant: float = 1.0, axis: str | None = None) -> AdaptiveStandardScalerTransformer:
@@ -1,14 +1,13 @@
1
1
  import ezmsg.core as ez
2
2
  import numpy as np
3
3
  import numpy.typing as npt
4
- from ezmsg.util.messages.axisarray import AxisArray
5
- from ezmsg.util.messages.util import replace
6
-
7
- from .base import (
4
+ from ezmsg.baseproc import (
8
5
  BaseAsyncTransformer,
9
6
  BaseTransformerUnit,
10
7
  processor_state,
11
8
  )
9
+ from ezmsg.util.messages.axisarray import AxisArray
10
+ from ezmsg.util.messages.util import replace
12
11
 
13
12
 
14
13
  class SignalInjectorSettings(ez.Settings):
ezmsg/sigproc/slicer.py CHANGED
@@ -1,6 +1,11 @@
1
1
  import ezmsg.core as ez
2
2
  import numpy as np
3
3
  import numpy.typing as npt
4
+ from ezmsg.baseproc import (
5
+ BaseStatefulTransformer,
6
+ BaseTransformerUnit,
7
+ processor_state,
8
+ )
4
9
  from ezmsg.util.messages.axisarray import (
5
10
  AxisArray,
6
11
  AxisBase,
@@ -8,12 +13,6 @@ from ezmsg.util.messages.axisarray import (
8
13
  slice_along_axis,
9
14
  )
10
15
 
11
- from .base import (
12
- BaseStatefulTransformer,
13
- BaseTransformerUnit,
14
- processor_state,
15
- )
16
-
17
16
  """
18
17
  Slicer:Select a subset of data along a particular axis.
19
18
  """
@@ -1,14 +1,14 @@
1
1
  from typing import Generator
2
2
 
3
3
  import ezmsg.core as ez
4
- from ezmsg.util.messages.axisarray import AxisArray
5
- from ezmsg.util.messages.modify import modify_axis
6
-
7
- from .base import (
4
+ from ezmsg.baseproc import (
8
5
  BaseStatefulProcessor,
9
6
  BaseTransformerUnit,
10
7
  CompositeProcessor,
11
8
  )
9
+ from ezmsg.util.messages.axisarray import AxisArray
10
+ from ezmsg.util.messages.modify import modify_axis
11
+
12
12
  from .spectrum import (
13
13
  SpectralOutput,
14
14
  SpectralTransform,
ezmsg/sigproc/spectrum.py CHANGED
@@ -5,18 +5,17 @@ from functools import partial
5
5
  import ezmsg.core as ez
6
6
  import numpy as np
7
7
  import numpy.typing as npt
8
+ from ezmsg.baseproc import (
9
+ BaseStatefulTransformer,
10
+ BaseTransformerUnit,
11
+ processor_state,
12
+ )
8
13
  from ezmsg.util.messages.axisarray import (
9
14
  AxisArray,
10
15
  replace,
11
16
  slice_along_axis,
12
17
  )
13
18
 
14
- from .base import (
15
- BaseStatefulTransformer,
16
- BaseTransformerUnit,
17
- processor_state,
18
- )
19
-
20
19
 
21
20
  class OptionsEnum(enum.Enum):
22
21
  @classmethod
@@ -2,16 +2,15 @@ from types import EllipsisType
2
2
 
3
3
  import ezmsg.core as ez
4
4
  import numpy as np
5
- from ezmsg.util.messages.axisarray import (
6
- AxisArray,
7
- replace,
8
- )
9
-
10
- from .base import (
5
+ from ezmsg.baseproc import (
11
6
  BaseStatefulTransformer,
12
7
  BaseTransformerUnit,
13
8
  processor_state,
14
9
  )
10
+ from ezmsg.util.messages.axisarray import (
11
+ AxisArray,
12
+ replace,
13
+ )
15
14
 
16
15
 
17
16
  class TransposeSettings(ez.Settings):
@@ -1,3 +1,5 @@
1
+ """AxisArray support for .buffer.HybridBuffer."""
2
+
1
3
  import math
2
4
  import typing
3
5
 
@@ -1,3 +1,7 @@
1
+ """A stateful, FIFO buffer that combines a deque for fast appends with a
2
+ contiguous circular buffer for efficient, advancing reads.
3
+ """
4
+
1
5
  import collections
2
6
  import math
3
7
  import typing
@@ -1,3 +1,5 @@
1
+ """Methods for sparse array signal processing operations."""
2
+
1
3
  import numpy as np
2
4
  import sparse
3
5
 
ezmsg/sigproc/wavelets.py CHANGED
@@ -4,14 +4,14 @@ import ezmsg.core as ez
4
4
  import numpy as np
5
5
  import numpy.typing as npt
6
6
  import pywt
7
- from ezmsg.util.messages.axisarray import AxisArray
8
- from ezmsg.util.messages.util import replace
9
-
10
- from .base import (
7
+ from ezmsg.baseproc import (
11
8
  BaseStatefulTransformer,
12
9
  BaseTransformerUnit,
13
10
  processor_state,
14
11
  )
12
+ from ezmsg.util.messages.axisarray import AxisArray
13
+ from ezmsg.util.messages.util import replace
14
+
15
15
  from .filterbank import FilterbankMode, MinPhaseMode, filterbank
16
16
 
17
17
 
ezmsg/sigproc/window.py CHANGED
@@ -6,6 +6,11 @@ import ezmsg.core as ez
6
6
  import numpy.typing as npt
7
7
  import sparse
8
8
  from array_api_compat import get_namespace, is_pydata_sparse_namespace
9
+ from ezmsg.baseproc import (
10
+ BaseStatefulTransformer,
11
+ BaseTransformerUnit,
12
+ processor_state,
13
+ )
9
14
  from ezmsg.util.messages.axisarray import (
10
15
  AxisArray,
11
16
  replace,
@@ -13,11 +18,6 @@ from ezmsg.util.messages.axisarray import (
13
18
  sliding_win_oneaxis,
14
19
  )
15
20
 
16
- from .base import (
17
- BaseStatefulTransformer,
18
- BaseTransformerUnit,
19
- processor_state,
20
- )
21
21
  from .util.profile import profile_subpub
22
22
  from .util.sparse import sliding_win_oneaxis as sparse_sliding_win_oneaxis
23
23
 
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: ezmsg-sigproc
3
+ Version: 2.8.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,65 @@
1
+ ezmsg/sigproc/__init__.py,sha256=8K4IcOA3-pfzadoM6s2Sfg5460KlJUocGgyTJTJl96U,52
2
+ ezmsg/sigproc/__version__.py,sha256=9o_P9mg_lm8dZdRjWfkpikVSLuPB26JLjcQeqWLe_GI,704
3
+ ezmsg/sigproc/activation.py,sha256=83vnTa3ZcC4Q3VSWcGfaqhCEqYRNySUOyVpMHZXfz-c,2755
4
+ ezmsg/sigproc/adaptive_lattice_notch.py,sha256=ThUR48mbSHuThkimtD0j4IXNMrOVcpZgGhE7PCYfXhU,8818
5
+ ezmsg/sigproc/affinetransform.py,sha256=jl7DiSa5Yb0qsmFJbfSiSeGmvK1SGoBgycFC5JU5DVY,9434
6
+ ezmsg/sigproc/aggregate.py,sha256=yc3Hnak0yhucqlTCv9Isg6BKR24s6rMZqbZKpayyBgM,9048
7
+ ezmsg/sigproc/bandpower.py,sha256=dAhH56sUrXNhcRFymTTwjdM_KcU5OxFzrR_sxIPAxyw,2264
8
+ ezmsg/sigproc/base.py,sha256=SJvKEb8gw6mUMwlV5sH0iPG0bXrgS8tvkPwhI-j89MQ,3672
9
+ ezmsg/sigproc/butterworthfilter.py,sha256=NKTGkgjvlmC1Dc9gD2Z6UBzUq12KicfnczrzM5ZTosk,5255
10
+ ezmsg/sigproc/butterworthzerophase.py,sha256=Df3F1QBBE39FBjNi67wvTsb1bSdTRTSTZXbZiKFlxC4,4105
11
+ ezmsg/sigproc/cheby.py,sha256=B8pGt5_pOBpNZCmaibNl_NKkyuasd8ZEJXeTDCTaino,3711
12
+ ezmsg/sigproc/combfilter.py,sha256=MSxr1I-jBePW_9AuCiv3RQ1HUNxIsNhLk0q1Iu8ikAw,4766
13
+ ezmsg/sigproc/coordinatespaces.py,sha256=NWbQVvizmiU4F3AIwHHhiZ30Kg2IeSW0fRaa-yXkn-c,4610
14
+ ezmsg/sigproc/decimate.py,sha256=DCX9p4ZrcGoQ9di-jmPKqiKERTkvTAtSqLg8chQLp84,2336
15
+ ezmsg/sigproc/denormalize.py,sha256=OACzoINCISNUmZcR2pqWfxi3p8uuBLCz1PMw4XNwOCs,3035
16
+ ezmsg/sigproc/detrend.py,sha256=2C-dhVy-ty4b22aE07kIF3VjqCoFitoZqyrX6hAjXYQ,894
17
+ ezmsg/sigproc/diff.py,sha256=m0g1KQXEooSM3V2oLx_MyYEZqdXm04jI9B4V-HjMUa4,2777
18
+ ezmsg/sigproc/downsample.py,sha256=Jqxt1Va1FrQLH1wUQpP0U79iQARTTHEKklKHy7yGL2o,3679
19
+ ezmsg/sigproc/ewma.py,sha256=ecLq1ZlON85NWdW_5cz9chWirxjf4y9zCPMOt3IqQuk,7723
20
+ ezmsg/sigproc/ewmfilter.py,sha256=9AuvVn3TDdf5R4bVolYNM46AtDVHFJa8MLGltY6mYPE,4795
21
+ ezmsg/sigproc/extract_axis.py,sha256=T2e9zW8N0bwOj4_zlB3I0oT0iwaBijpzF6wrFsCfD24,1593
22
+ ezmsg/sigproc/fbcca.py,sha256=JYFWsMDRJEWwUNujr4EsFL5t1ux-cnBGamNVrCRO_RA,12043
23
+ ezmsg/sigproc/filter.py,sha256=IWg3J5IGVdv7kfp4zaKRzfjEuRa5xNzcq6fHtq4tPCM,11604
24
+ ezmsg/sigproc/filterbank.py,sha256=tD7fn4dZzEvsmp_sSn16VVJ4WcJ5sN5lsSuNTLDCQZ8,13056
25
+ ezmsg/sigproc/filterbankdesign.py,sha256=vLXQVJwoFEK4V6umqzcr1PJKcwv6pGO29klSWQXk7y0,4712
26
+ ezmsg/sigproc/fir_hilbert.py,sha256=ByZDsDFjbJx-EgLZF85vZCPQbprQaiMEuG2dyvTPiDY,10855
27
+ ezmsg/sigproc/fir_pmc.py,sha256=Ze2pd9K8XrdDhgJZG2E7x-5C2hfd6kIns4bYjTJsBvc,6997
28
+ ezmsg/sigproc/firfilter.py,sha256=7r_I476nYuixJsuwc_hQ0Fbq8WB0gnYBZUKs3zultOQ,3790
29
+ ezmsg/sigproc/gaussiansmoothing.py,sha256=BfXm9YQoOtieM4ABK2KRgxeQz055rd7mqtTVqmjT3Rk,2672
30
+ ezmsg/sigproc/kaiser.py,sha256=dIwgHbLXUHPtdotsGNLE9VG_clhcMgvVnSoFkMVgF9M,3483
31
+ ezmsg/sigproc/linear.py,sha256=o5HNEQMx2e6FrFOJskJeKCJwZmSN1uO-1LgBxERzmEE,4535
32
+ ezmsg/sigproc/messages.py,sha256=KQczHTeifn4BZycChN8ZcpfZoQW3lC_xuFmN72QT97s,925
33
+ ezmsg/sigproc/quantize.py,sha256=uSM2z2xXwL0dgSltyzLEmlKjaJZ2meA3PDWX8_bM0Hs,2195
34
+ ezmsg/sigproc/resample.py,sha256=3mm9pvxryNVhQuTCIMW3ToUkUfbVOCsIgvXUiurit1Y,11389
35
+ ezmsg/sigproc/rollingscaler.py,sha256=e-smSKDhmDD2nWIf6I77CtRxQp_7sHS268SGPi7aXp8,8499
36
+ ezmsg/sigproc/sampler.py,sha256=iOk2YoUX22u9iTjFKimzP5V074RDBVcmswgfyxvZRZo,10761
37
+ ezmsg/sigproc/scaler.py,sha256=oBZa6uzyftChvk6aqBD5clil6pedx3IF-dptrb74EA0,5888
38
+ ezmsg/sigproc/signalinjector.py,sha256=mB62H2b-ScgPtH1jajEpxgDHqdb-RKekQfgyNncsE8Y,2874
39
+ ezmsg/sigproc/slicer.py,sha256=xLXxWf722V08ytVwvPimYjDKKj0pkC2HjdgCVaoaOvs,5195
40
+ ezmsg/sigproc/spectral.py,sha256=wFzuihS7qJZMQcp0ds_qCG-zCbvh5DyhFRjn2wA9TWQ,322
41
+ ezmsg/sigproc/spectrogram.py,sha256=g8xYWENzle6O5uEF-vfjsF5gOSDnJTwiu3ZudicO470,2893
42
+ ezmsg/sigproc/spectrum.py,sha256=AAxrywIYpAPENRWvaaM2VjcKEaqMt6ra1E3Ao26jdZs,9523
43
+ ezmsg/sigproc/transpose.py,sha256=Yx4losN7tKCacUt2GmFvjOSqJ0b3XhEepXzq_Z_iMCI,4381
44
+ ezmsg/sigproc/wavelets.py,sha256=mN9TrZencyvKBfnuMiGZ_lzrE1O7DhVo05EYgCjXncg,7428
45
+ ezmsg/sigproc/window.py,sha256=ZlawY4TPbLc46qIgcKhP4X7dpMDo4zNlnfzgV1eFaGU,15335
46
+ ezmsg/sigproc/math/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ ezmsg/sigproc/math/abs.py,sha256=_omQP4baHGu8MgKzNDMUsjrmA_yeXWNqKyTU7BiHCSE,741
48
+ ezmsg/sigproc/math/add.py,sha256=lR4lB8-QdUfyARJcdf5sMQhbHtFpRNYt8_1zYzkVEhU,3659
49
+ ezmsg/sigproc/math/clip.py,sha256=vJ8zpzVlTVPlkElsqyPVMOwyKBrgym8RD8BEOvf8g1o,1131
50
+ ezmsg/sigproc/math/difference.py,sha256=TtAURz9TsW_Vk39sMJOT84P0xw0xDrgE3ToRi8MWVv4,4491
51
+ ezmsg/sigproc/math/invert.py,sha256=SYJDNEb7snkVODnItfkkp8rhMrHEdyBmzg6qZkZLX2c,630
52
+ ezmsg/sigproc/math/log.py,sha256=K8bZSm-ubTDY0oqwuc0IOMkdPIwpw1yysjTOG657kkQ,1488
53
+ ezmsg/sigproc/math/scale.py,sha256=Zpzlz1NSLyvmggjs0pPypUKgXX99z2S3VXJ06fHok5M,942
54
+ ezmsg/sigproc/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
+ ezmsg/sigproc/util/asio.py,sha256=aAj0e7OoBvkRy28k05HL2s9YPCTxOddc05xMN-qd4lQ,577
56
+ ezmsg/sigproc/util/axisarray_buffer.py,sha256=TGDeC6CXmmp7OUuiGd6xYQijRGYDE4QGdWxjK5Vs3nE,14057
57
+ ezmsg/sigproc/util/buffer.py,sha256=83Gm0IuowmcMlXgLFB_rz8_ZPhkwG4DNNejyWJDKJl8,19658
58
+ ezmsg/sigproc/util/message.py,sha256=ppN3IYtIAwrxWG9JOvgWFn1wDdIumkEzYFfqpH9VQkY,338
59
+ ezmsg/sigproc/util/profile.py,sha256=eVOo9pXgusrnH1yfRdd2RsM7Dbe2UpyC0LJ9MfGpB08,416
60
+ ezmsg/sigproc/util/sparse.py,sha256=NjbJitCtO0B6CENTlyd9c-lHEJwoCan-T3DIgPyeShw,4834
61
+ ezmsg/sigproc/util/typeresolution.py,sha256=fMFzLi63dqCIclGFLcMdM870OYxJnkeWw6aWKNMk718,362
62
+ ezmsg_sigproc-2.8.0.dist-info/METADATA,sha256=JZfuyxTvO6n3dIztHrFs531fF6agB_uKkPyXRMlW-EU,1908
63
+ ezmsg_sigproc-2.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
64
+ ezmsg_sigproc-2.8.0.dist-info/licenses/LICENSE,sha256=seu0tKhhAMPCUgc1XpXGGaCxY1YaYvFJwqFuQZAl2go,1100
65
+ ezmsg_sigproc-2.8.0.dist-info/RECORD,,