ezmsg-sigproc 1.8.1__tar.gz → 1.8.2__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-1.8.1 → ezmsg_sigproc-1.8.2}/PKG-INFO +1 -1
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/__version__.py +2 -2
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/downsample.py +16 -10
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_downsample.py +13 -6
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/.github/workflows/python-publish-ezmsg-sigproc.yml +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/.github/workflows/python-tests.yml +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/.gitignore +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/.pre-commit-config.yaml +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/LICENSE.txt +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/README.md +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/pyproject.toml +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/__init__.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/activation.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/affinetransform.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/aggregate.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/bandpower.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/base.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/butterworthfilter.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/cheby.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/decimate.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/ewmfilter.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/filter.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/filterbank.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/math/__init__.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/math/abs.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/math/clip.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/math/difference.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/math/invert.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/math/log.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/math/scale.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/messages.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/sampler.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/scaler.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/signalinjector.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/slicer.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/spectral.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/spectrogram.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/spectrum.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/synth.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/util/__init__.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/util/profile.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/util/sparse.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/wavelets.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/src/ezmsg/sigproc/window.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/conftest.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/helpers/__init__.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/helpers/util.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/resources/xform.csv +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_activation.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_affine_transform.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_aggregate.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_bandpower.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_butter.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_butterworth.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_decimate.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_filter_system.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_filterbank.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_math.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_sampler.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_scaler.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_slicer.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_spectrogram.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_spectrum.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_synth.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_wavelets.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/tests/test_window.py +0 -0
- {ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ezmsg-sigproc
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.2
|
|
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-Expression: MIT
|
|
@@ -14,7 +14,7 @@ from .base import GenAxisArray
|
|
|
14
14
|
|
|
15
15
|
@consumer
|
|
16
16
|
def downsample(
|
|
17
|
-
axis: str | None = None, target_rate: float | None = None
|
|
17
|
+
axis: str | None = None, target_rate: float | None = None, factor: int | 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.
|
|
@@ -28,6 +28,7 @@ def downsample(
|
|
|
28
28
|
Note: The axis must exist in the message .axes and be of type AxisArray.LinearAxis.
|
|
29
29
|
target_rate: Desired rate after downsampling. The actual rate will be the nearest integer factor of the
|
|
30
30
|
input rate that is the same or higher than the target rate.
|
|
31
|
+
factor: Explicitly specify downsample factor. If specified, target_rate is ignored.
|
|
31
32
|
|
|
32
33
|
Returns:
|
|
33
34
|
A primed generator object ready to receive an :obj:`AxisArray` via `.send(axis_array)`
|
|
@@ -39,7 +40,7 @@ def downsample(
|
|
|
39
40
|
msg_out = AxisArray(np.array([]), dims=[""])
|
|
40
41
|
|
|
41
42
|
# state variables
|
|
42
|
-
|
|
43
|
+
q: int = 0 # The integer downsampling factor. It will be determined based on the target rate.
|
|
43
44
|
s_idx: int = 0 # Index of the next msg's first sample into the virtual rotating ds_factor counter.
|
|
44
45
|
|
|
45
46
|
check_input = {"gain": None, "key": None}
|
|
@@ -61,19 +62,21 @@ def downsample(
|
|
|
61
62
|
check_input["key"] = msg_in.key
|
|
62
63
|
# Reset state variables
|
|
63
64
|
s_idx = 0
|
|
64
|
-
if
|
|
65
|
-
|
|
65
|
+
if factor is not None:
|
|
66
|
+
q = factor
|
|
67
|
+
elif target_rate is None:
|
|
68
|
+
q = 1
|
|
66
69
|
else:
|
|
67
|
-
|
|
68
|
-
if
|
|
70
|
+
q = int(1 / (axis_info.gain * target_rate))
|
|
71
|
+
if q < 1:
|
|
69
72
|
ez.logger.warning(
|
|
70
73
|
f"Target rate {target_rate} cannot be achieved with input rate of {1/axis_info.gain}."
|
|
71
74
|
"Setting factor to 1."
|
|
72
75
|
)
|
|
73
|
-
|
|
76
|
+
q = 1
|
|
74
77
|
|
|
75
78
|
n_samples = msg_in.data.shape[axis_idx]
|
|
76
|
-
samples = np.arange(s_idx, s_idx + n_samples) %
|
|
79
|
+
samples = np.arange(s_idx, s_idx + n_samples) % q
|
|
77
80
|
if n_samples > 0:
|
|
78
81
|
# Update state for next iteration.
|
|
79
82
|
s_idx = samples[-1] + 1
|
|
@@ -92,7 +95,7 @@ def downsample(
|
|
|
92
95
|
**msg_in.axes,
|
|
93
96
|
axis: replace(
|
|
94
97
|
axis_info,
|
|
95
|
-
gain=axis_info.gain *
|
|
98
|
+
gain=axis_info.gain * q,
|
|
96
99
|
offset=axis_info.offset + axis_info.gain * n_step,
|
|
97
100
|
),
|
|
98
101
|
},
|
|
@@ -107,6 +110,7 @@ class DownsampleSettings(ez.Settings):
|
|
|
107
110
|
|
|
108
111
|
axis: str | None = None
|
|
109
112
|
target_rate: float | None = None
|
|
113
|
+
factor: int | None = None
|
|
110
114
|
|
|
111
115
|
|
|
112
116
|
class Downsample(GenAxisArray):
|
|
@@ -116,5 +120,7 @@ class Downsample(GenAxisArray):
|
|
|
116
120
|
|
|
117
121
|
def construct_generator(self):
|
|
118
122
|
self.STATE.gen = downsample(
|
|
119
|
-
axis=self.SETTINGS.axis,
|
|
123
|
+
axis=self.SETTINGS.axis,
|
|
124
|
+
target_rate=self.SETTINGS.target_rate,
|
|
125
|
+
factor=self.SETTINGS.factor,
|
|
120
126
|
)
|
|
@@ -21,7 +21,8 @@ from ezmsg.util.debuglog import DebugLog
|
|
|
21
21
|
|
|
22
22
|
@pytest.mark.parametrize("block_size", [1, 5, 10, 20])
|
|
23
23
|
@pytest.mark.parametrize("target_rate", [19.0, 9.5, 6.3])
|
|
24
|
-
|
|
24
|
+
@pytest.mark.parametrize("factor", [None, 1, 2])
|
|
25
|
+
def test_downsample_core(block_size: int, target_rate: float, factor: int | None):
|
|
25
26
|
in_fs = 19.0
|
|
26
27
|
test_dur = 4.0
|
|
27
28
|
n_channels = 2
|
|
@@ -60,7 +61,7 @@ def test_downsample_core(block_size: int, target_rate: float):
|
|
|
60
61
|
in_msgs = list(msg_generator())
|
|
61
62
|
backup = [copy.deepcopy(msg) for msg in in_msgs]
|
|
62
63
|
|
|
63
|
-
proc = downsample(axis="time", target_rate=target_rate)
|
|
64
|
+
proc = downsample(axis="time", target_rate=target_rate, factor=factor)
|
|
64
65
|
out_msgs = []
|
|
65
66
|
for msg in in_msgs:
|
|
66
67
|
res = proc.send(msg)
|
|
@@ -70,7 +71,7 @@ def test_downsample_core(block_size: int, target_rate: float):
|
|
|
70
71
|
assert_messages_equal(in_msgs, backup)
|
|
71
72
|
|
|
72
73
|
# Assert correctness of gain
|
|
73
|
-
expected_factor: int = int(in_fs // target_rate)
|
|
74
|
+
expected_factor: int = int(in_fs // target_rate) if factor is None else factor
|
|
74
75
|
assert all(msg.axes["time"].gain == expected_factor / in_fs for msg in out_msgs)
|
|
75
76
|
|
|
76
77
|
# Assert messages have the correct timestamps
|
|
@@ -132,7 +133,13 @@ class DownsampleSystem(ez.Collection):
|
|
|
132
133
|
|
|
133
134
|
@pytest.mark.parametrize("block_size", [10])
|
|
134
135
|
@pytest.mark.parametrize("target_rate", [6.3])
|
|
135
|
-
|
|
136
|
+
@pytest.mark.parametrize("factor", [None, 2])
|
|
137
|
+
def test_downsample_system(
|
|
138
|
+
block_size: int,
|
|
139
|
+
target_rate: float,
|
|
140
|
+
factor: int | None,
|
|
141
|
+
test_name: str | None = None,
|
|
142
|
+
):
|
|
136
143
|
in_fs = 19.0
|
|
137
144
|
num_msgs = int(4.0 / (block_size / in_fs)) # Ensure 4 seconds of data
|
|
138
145
|
|
|
@@ -146,7 +153,7 @@ def test_downsample_system(block_size: int, target_rate: float, test_name: str |
|
|
|
146
153
|
fs=in_fs,
|
|
147
154
|
dispatch_rate=20.0,
|
|
148
155
|
),
|
|
149
|
-
down_settings=DownsampleSettings(target_rate=target_rate),
|
|
156
|
+
down_settings=DownsampleSettings(target_rate=target_rate, factor=factor),
|
|
150
157
|
log_settings=MessageLoggerSettings(output=test_filename),
|
|
151
158
|
term_settings=TerminateTestSettings(time=1.0),
|
|
152
159
|
)
|
|
@@ -160,7 +167,7 @@ def test_downsample_system(block_size: int, target_rate: float, test_name: str |
|
|
|
160
167
|
ez.logger.info(f"Analyzing recording of { len( messages ) } messages...")
|
|
161
168
|
|
|
162
169
|
# Check fs
|
|
163
|
-
expected_factor: int = int(in_fs // target_rate)
|
|
170
|
+
expected_factor: int = int(in_fs // target_rate) if factor is None else factor
|
|
164
171
|
out_fs = in_fs / expected_factor
|
|
165
172
|
assert np.allclose(
|
|
166
173
|
np.array([1 / msg.axes["time"].gain for msg in messages]),
|
{ezmsg_sigproc-1.8.1 → ezmsg_sigproc-1.8.2}/.github/workflows/python-publish-ezmsg-sigproc.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|