ezmsg-sigproc 1.6.0__py3-none-any.whl → 1.7.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.
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.6.0'
16
- __version_tuple__ = version_tuple = (1, 6, 0)
15
+ __version__ = version = '1.7.0'
16
+ __version_tuple__ = version_tuple = (1, 7, 0)
ezmsg/sigproc/decimate.py CHANGED
@@ -1,8 +1,31 @@
1
+ import typing
2
+
1
3
  import ezmsg.core as ez
2
4
  from ezmsg.util.messages.axisarray import AxisArray
3
5
 
4
6
  from .cheby import ChebyshevFilter, ChebyshevFilterSettings
5
7
  from .downsample import Downsample, DownsampleSettings
8
+ from .filter import FilterCoefsMultiType
9
+
10
+
11
+ class ChebyForDecimate(ChebyshevFilter):
12
+ """
13
+ A :obj:`ChebyshevFilter` node with a design filter method that additionally accepts a target sampling rate,
14
+ and if the target rate cannot be achieved it returns None, else it returns the filter coefficients.
15
+ """
16
+
17
+ def design_filter(
18
+ self,
19
+ ) -> typing.Callable[[float], FilterCoefsMultiType | None]:
20
+ def cheby_opt_design_fun(fs: float) -> FilterCoefsMultiType | None:
21
+ if fs is None:
22
+ return None
23
+ ds_factor = int(fs / (2.5 * self.SETTINGS.Wn))
24
+ if ds_factor < 2:
25
+ return None
26
+ partial_fun = super(ChebyForDecimate, self).design_filter()
27
+ return partial_fun(fs)
28
+ return cheby_opt_design_fun
6
29
 
7
30
 
8
31
  class Decimate(ez.Collection):
@@ -16,17 +39,18 @@ class Decimate(ez.Collection):
16
39
  INPUT_SIGNAL = ez.InputStream(AxisArray)
17
40
  OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
18
41
 
19
- FILTER = ChebyshevFilter()
42
+ FILTER = ChebyForDecimate()
20
43
  DOWNSAMPLE = Downsample()
21
44
 
22
45
  def configure(self) -> None:
46
+
23
47
  cheby_settings = ChebyshevFilterSettings(
24
- order=8 if self.SETTINGS.factor > 1 else 0,
48
+ order=8,
25
49
  ripple_tol=0.05,
26
- Wn=0.8 / self.SETTINGS.factor if self.SETTINGS.factor > 1 else None,
50
+ Wn=0.4 * self.SETTINGS.target_rate,
27
51
  btype="lowpass",
28
52
  axis=self.SETTINGS.axis,
29
- wn_hz=False,
53
+ wn_hz=True,
30
54
  )
31
55
  self.FILTER.apply_settings(cheby_settings)
32
56
  self.DOWNSAMPLE.apply_settings(self.SETTINGS)
@@ -14,7 +14,7 @@ from .base import GenAxisArray
14
14
 
15
15
  @consumer
16
16
  def downsample(
17
- axis: str | None = None, factor: int = 1
17
+ axis: str | None = None, target_rate: float | None = None
18
18
  ) -> typing.Generator[AxisArray, AxisArray, None]:
19
19
  """
20
20
  Construct a generator that yields a downsampled version of the data .send() to it.
@@ -26,7 +26,8 @@ def downsample(
26
26
  Args:
27
27
  axis: The name of the axis along which to downsample.
28
28
  Note: The axis must exist in the message .axes and be of type AxisArray.LinearAxis.
29
- factor: Downsampling factor.
29
+ target_rate: Desired rate after downsampling. The actual rate will be the nearest integer factor of the
30
+ input rate that is the same or higher than the target rate.
30
31
 
31
32
  Returns:
32
33
  A primed generator object ready to receive an :obj:`AxisArray` via `.send(axis_array)`
@@ -37,10 +38,8 @@ def downsample(
37
38
  """
38
39
  msg_out = AxisArray(np.array([]), dims=[""])
39
40
 
40
- if factor < 1:
41
- raise ValueError("Downsample factor must be at least 1 (no downsampling)")
42
-
43
41
  # state variables
42
+ factor: int = 0 # The integer downsampling factor. It will be determined based on the target rate.
44
43
  s_idx: int = 0 # Index of the next msg's first sample into the virtual rotating ds_factor counter.
45
44
 
46
45
  check_input = {"gain": None, "key": None}
@@ -62,6 +61,16 @@ def downsample(
62
61
  check_input["key"] = msg_in.key
63
62
  # Reset state variables
64
63
  s_idx = 0
64
+ if target_rate is None:
65
+ factor = 1
66
+ else:
67
+ factor = int(1 / (axis_info.gain * target_rate))
68
+ if factor < 1:
69
+ ez.logger.warning(
70
+ f"Target rate {target_rate} cannot be achieved with input rate of {1/axis_info.gain}."
71
+ "Setting factor to 1."
72
+ )
73
+ factor = 1
65
74
 
66
75
  n_samples = msg_in.data.shape[axis_idx]
67
76
  samples = np.arange(s_idx, s_idx + n_samples) % factor
@@ -97,7 +106,7 @@ class DownsampleSettings(ez.Settings):
97
106
  """
98
107
 
99
108
  axis: str | None = None
100
- factor: int = 1
109
+ target_rate: float | None = None
101
110
 
102
111
 
103
112
  class Downsample(GenAxisArray):
@@ -107,5 +116,5 @@ class Downsample(GenAxisArray):
107
116
 
108
117
  def construct_generator(self):
109
118
  self.STATE.gen = downsample(
110
- axis=self.SETTINGS.axis, factor=self.SETTINGS.factor
119
+ axis=self.SETTINGS.axis, target_rate=self.SETTINGS.target_rate
111
120
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ezmsg-sigproc
3
- Version: 1.6.0
3
+ Version: 1.7.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>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  ezmsg/sigproc/__init__.py,sha256=8K4IcOA3-pfzadoM6s2Sfg5460KlJUocGgyTJTJl96U,52
2
- ezmsg/sigproc/__version__.py,sha256=Ry70pc5l-IBhT9gahlkNwZPp4g0CzVEWqsat9H-UASY,411
2
+ ezmsg/sigproc/__version__.py,sha256=2fEqxujmrV2dsREie2BmOYFLu66FowyHtZT2AoLuIzU,411
3
3
  ezmsg/sigproc/activation.py,sha256=LM-MtaNvFvmBZ1_EPNu--4K3-wIzsb7CUQ00fvMM69g,2642
4
4
  ezmsg/sigproc/affinetransform.py,sha256=R2P1f5JdNSpr30x8Pxoqv-J-nAasGl_UZR8o7sVqlaQ,8740
5
5
  ezmsg/sigproc/aggregate.py,sha256=6BqAujWe7_zzGPDJz7yh4ofPzJTU5z2lrclX-IuqXDU,6235
@@ -7,8 +7,8 @@ ezmsg/sigproc/bandpower.py,sha256=Mx8iUU-UMWKK5sgCXvxpUSA3522rW8Jg96asrT8MdpA,21
7
7
  ezmsg/sigproc/base.py,sha256=vut0BLjgc0mxYRbs7tDd9XzwRFA2_GcsgXZmIYovR0Y,1248
8
8
  ezmsg/sigproc/butterworthfilter.py,sha256=vZTY37FfLwa24bRZmeZGyO5323824wJosUrrZarb0_o,5402
9
9
  ezmsg/sigproc/cheby.py,sha256=yds5y1fOeBE1ljyH_EreBLxqFX4UetxB_3rwz3omHyc,3394
10
- ezmsg/sigproc/decimate.py,sha256=Cu2weOIKGoo_pUSpA90N47kuzcT6s7OAa1HaF7u3cY0,1286
11
- ezmsg/sigproc/downsample.py,sha256=oJ0VOZ0UrLAhce2bolSom23LD8Ob-8LCOmzqkrZCJ0E,3454
10
+ ezmsg/sigproc/decimate.py,sha256=qxZoGmriviTNIUvTOA--U65CPWD1uTvQ9pij79-u00Q,2044
11
+ ezmsg/sigproc/downsample.py,sha256=PYdrp7p6subP_qbGB2-3C14bd8W4tvC5oMKEmERQZmk,4049
12
12
  ezmsg/sigproc/ewmfilter.py,sha256=EPlocRdKORj575VV1YUzcNsVcq-pYgdEJ7_m9WfpVnY,4795
13
13
  ezmsg/sigproc/filter.py,sha256=n3_gColSPXe4pI-A2VDKfrdFHZgC-k4kqB8H6tnGIws,6969
14
14
  ezmsg/sigproc/filterbank.py,sha256=pySLNGppnG6Dx9r5jQpNSyWhd9qmvj6up2udG7RMNok,12410
@@ -30,7 +30,7 @@ ezmsg/sigproc/math/difference.py,sha256=I96Md2pyfCU5TSw35u1uS6FaPJRv0JpHQVhrTHv1
30
30
  ezmsg/sigproc/math/invert.py,sha256=T6PEhBCquKRJQF6LfUgqjXKSBsS_tvi4BJAqJ_5VCjw,895
31
31
  ezmsg/sigproc/math/log.py,sha256=i-iF2WDH18RWapYwlhAsiDdhzvuJY9mDWrNtjfuiyL8,1572
32
32
  ezmsg/sigproc/math/scale.py,sha256=lQKF02Mv9nTWfgc4OcMpQMCx993p57GSN-AADeO2fjY,1063
33
- ezmsg_sigproc-1.6.0.dist-info/METADATA,sha256=-Y0s91NhAEO_KWsBo1KQKPQE4IDyU-xp51rOa8FuUAo,2343
34
- ezmsg_sigproc-1.6.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
35
- ezmsg_sigproc-1.6.0.dist-info/licenses/LICENSE.txt,sha256=seu0tKhhAMPCUgc1XpXGGaCxY1YaYvFJwqFuQZAl2go,1100
36
- ezmsg_sigproc-1.6.0.dist-info/RECORD,,
33
+ ezmsg_sigproc-1.7.0.dist-info/METADATA,sha256=Nnz-8hSZBErjldQf3kxBfZ7xEtr9Woi9boBBVFtZ3DI,2343
34
+ ezmsg_sigproc-1.7.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
35
+ ezmsg_sigproc-1.7.0.dist-info/licenses/LICENSE.txt,sha256=seu0tKhhAMPCUgc1XpXGGaCxY1YaYvFJwqFuQZAl2go,1100
36
+ ezmsg_sigproc-1.7.0.dist-info/RECORD,,