ezmsg-sigproc 1.7.0__py3-none-any.whl → 2.10.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.
- ezmsg/sigproc/__version__.py +22 -4
- ezmsg/sigproc/activation.py +31 -40
- ezmsg/sigproc/adaptive_lattice_notch.py +212 -0
- ezmsg/sigproc/affinetransform.py +171 -169
- ezmsg/sigproc/aggregate.py +190 -97
- ezmsg/sigproc/bandpower.py +60 -55
- ezmsg/sigproc/base.py +143 -33
- ezmsg/sigproc/butterworthfilter.py +34 -38
- ezmsg/sigproc/butterworthzerophase.py +305 -0
- ezmsg/sigproc/cheby.py +23 -17
- ezmsg/sigproc/combfilter.py +160 -0
- ezmsg/sigproc/coordinatespaces.py +159 -0
- ezmsg/sigproc/decimate.py +15 -10
- ezmsg/sigproc/denormalize.py +78 -0
- ezmsg/sigproc/detrend.py +28 -0
- ezmsg/sigproc/diff.py +82 -0
- ezmsg/sigproc/downsample.py +72 -81
- ezmsg/sigproc/ewma.py +217 -0
- ezmsg/sigproc/ewmfilter.py +1 -1
- ezmsg/sigproc/extract_axis.py +39 -0
- ezmsg/sigproc/fbcca.py +307 -0
- ezmsg/sigproc/filter.py +254 -148
- ezmsg/sigproc/filterbank.py +226 -214
- ezmsg/sigproc/filterbankdesign.py +129 -0
- ezmsg/sigproc/fir_hilbert.py +336 -0
- ezmsg/sigproc/fir_pmc.py +209 -0
- ezmsg/sigproc/firfilter.py +117 -0
- ezmsg/sigproc/gaussiansmoothing.py +89 -0
- ezmsg/sigproc/kaiser.py +106 -0
- ezmsg/sigproc/linear.py +120 -0
- ezmsg/sigproc/math/abs.py +23 -22
- ezmsg/sigproc/math/add.py +120 -0
- ezmsg/sigproc/math/clip.py +33 -25
- ezmsg/sigproc/math/difference.py +117 -43
- ezmsg/sigproc/math/invert.py +18 -25
- ezmsg/sigproc/math/log.py +38 -33
- ezmsg/sigproc/math/scale.py +24 -25
- ezmsg/sigproc/messages.py +1 -2
- ezmsg/sigproc/quantize.py +68 -0
- ezmsg/sigproc/resample.py +278 -0
- ezmsg/sigproc/rollingscaler.py +232 -0
- ezmsg/sigproc/sampler.py +209 -254
- ezmsg/sigproc/scaler.py +93 -218
- ezmsg/sigproc/signalinjector.py +44 -43
- ezmsg/sigproc/slicer.py +74 -102
- ezmsg/sigproc/spectral.py +3 -3
- ezmsg/sigproc/spectrogram.py +70 -70
- ezmsg/sigproc/spectrum.py +187 -173
- ezmsg/sigproc/transpose.py +134 -0
- ezmsg/sigproc/util/__init__.py +0 -0
- ezmsg/sigproc/util/asio.py +25 -0
- ezmsg/sigproc/util/axisarray_buffer.py +365 -0
- ezmsg/sigproc/util/buffer.py +449 -0
- ezmsg/sigproc/util/message.py +17 -0
- ezmsg/sigproc/util/profile.py +23 -0
- ezmsg/sigproc/util/sparse.py +115 -0
- ezmsg/sigproc/util/typeresolution.py +17 -0
- ezmsg/sigproc/wavelets.py +147 -154
- ezmsg/sigproc/window.py +248 -210
- ezmsg_sigproc-2.10.0.dist-info/METADATA +60 -0
- ezmsg_sigproc-2.10.0.dist-info/RECORD +65 -0
- {ezmsg_sigproc-1.7.0.dist-info → ezmsg_sigproc-2.10.0.dist-info}/WHEEL +1 -1
- ezmsg/sigproc/synth.py +0 -621
- ezmsg_sigproc-1.7.0.dist-info/METADATA +0 -58
- ezmsg_sigproc-1.7.0.dist-info/RECORD +0 -36
- /ezmsg_sigproc-1.7.0.dist-info/licenses/LICENSE.txt → /ezmsg_sigproc-2.10.0.dist-info/licenses/LICENSE +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
ezmsg/sigproc/__init__.py,sha256=8K4IcOA3-pfzadoM6s2Sfg5460KlJUocGgyTJTJl96U,52
|
|
2
|
+
ezmsg/sigproc/__version__.py,sha256=2cFLkHY3I9n23ToIWm38PohvvIEYjdsebREPVvOKAiU,706
|
|
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=7Hdz1m-S6Cl9h0oRQHeS_UTGBemhOB4XdFyX6cGcdHo,9362
|
|
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=CU6cXkI6j1LQCEz0sr2IthAPCq_TEtbvSb7h2Nw1w74,11820
|
|
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=bp_0fTS9b27OQqLoFzgE3f9rb287P8y0S1dWWGrS08o,5298
|
|
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=nDf9-XywZKlfeainIeubl_NccmkuAdLjYMcWSPMquss,3179
|
|
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=b3NRzQNBvdU2jqenZT9XXFHax9Mavbj2xFiVxOwl1Ms,4662
|
|
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=uRGieeYRFTO9gAhWzkq49f-Qo49rpDQhn9r3nLuwx4I,4983
|
|
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=Zrt0xhGnhf90Vk00S1PYkbeCOQshqkvh1HLj26N4AL0,979
|
|
48
|
+
ezmsg/sigproc/math/add.py,sha256=UikpS_vhaY58ssf46C1r4_tnRUFMl86xCmd5Y8GFZ1Q,3666
|
|
49
|
+
ezmsg/sigproc/math/clip.py,sha256=1D6mUlOzBB7L35G_KKYZmfg7nYlbuDdITV4EH0R-yUo,1529
|
|
50
|
+
ezmsg/sigproc/math/difference.py,sha256=uUYZgbLe-GrFSN6EOFjs9fQZllp827IluxL6m8TJuH8,4791
|
|
51
|
+
ezmsg/sigproc/math/invert.py,sha256=nz8jbfvDoez6s9NmAprBtTAI5oSDj0wNUPk8j13XiVk,855
|
|
52
|
+
ezmsg/sigproc/math/log.py,sha256=JhjSqLnQnvx_3F4txRYHuUPSJ12Yj2HvRTsCMNvlxpo,2022
|
|
53
|
+
ezmsg/sigproc/math/scale.py,sha256=4_xHcHNuf13E1fxIF5vbkPfkN4En6zkfPIKID7lCERk,1133
|
|
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.10.0.dist-info/METADATA,sha256=lsHkW6Abh6GK3bOOl5MHtXthhwOQWuVUrByPz2AYOfk,1909
|
|
63
|
+
ezmsg_sigproc-2.10.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
64
|
+
ezmsg_sigproc-2.10.0.dist-info/licenses/LICENSE,sha256=seu0tKhhAMPCUgc1XpXGGaCxY1YaYvFJwqFuQZAl2go,1100
|
|
65
|
+
ezmsg_sigproc-2.10.0.dist-info/RECORD,,
|
ezmsg/sigproc/synth.py
DELETED
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from dataclasses import field
|
|
3
|
-
import time
|
|
4
|
-
import typing
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
import ezmsg.core as ez
|
|
8
|
-
from ezmsg.util.generator import consumer
|
|
9
|
-
from ezmsg.util.messages.axisarray import AxisArray
|
|
10
|
-
from ezmsg.util.messages.util import replace
|
|
11
|
-
|
|
12
|
-
from .butterworthfilter import ButterworthFilter, ButterworthFilterSettings
|
|
13
|
-
from .base import GenAxisArray
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def clock(dispatch_rate: float | None) -> typing.Generator[ez.Flag, None, None]:
|
|
17
|
-
"""
|
|
18
|
-
Construct a generator that yields events at a specified rate.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
dispatch_rate: event rate in seconds.
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
A generator object that yields :obj:`ez.Flag` events at a specified rate.
|
|
25
|
-
"""
|
|
26
|
-
n_dispatch = -1
|
|
27
|
-
t_0 = time.time()
|
|
28
|
-
while True:
|
|
29
|
-
if dispatch_rate is not None:
|
|
30
|
-
n_dispatch += 1
|
|
31
|
-
t_next = t_0 + n_dispatch / dispatch_rate
|
|
32
|
-
time.sleep(max(0, t_next - time.time()))
|
|
33
|
-
yield ez.Flag()
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
async def aclock(dispatch_rate: float | None) -> typing.AsyncGenerator[ez.Flag, None]:
|
|
37
|
-
"""
|
|
38
|
-
``asyncio`` version of :obj:`clock`.
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
asynchronous generator object. Must use `anext` or `async for`.
|
|
42
|
-
"""
|
|
43
|
-
t_0 = time.time()
|
|
44
|
-
n_dispatch = -1
|
|
45
|
-
while True:
|
|
46
|
-
if dispatch_rate is not None:
|
|
47
|
-
n_dispatch += 1
|
|
48
|
-
t_next = t_0 + n_dispatch / dispatch_rate
|
|
49
|
-
await asyncio.sleep(t_next - time.time())
|
|
50
|
-
yield ez.Flag()
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class ClockSettings(ez.Settings):
|
|
54
|
-
"""Settings for :obj:`Clock`. See :obj:`clock` for parameter description."""
|
|
55
|
-
|
|
56
|
-
# Message dispatch rate (Hz), or None (fast as possible)
|
|
57
|
-
dispatch_rate: float | None
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class ClockState(ez.State):
|
|
61
|
-
cur_settings: ClockSettings
|
|
62
|
-
gen: typing.AsyncGenerator
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class Clock(ez.Unit):
|
|
66
|
-
"""Unit for :obj:`clock`."""
|
|
67
|
-
|
|
68
|
-
SETTINGS = ClockSettings
|
|
69
|
-
STATE = ClockState
|
|
70
|
-
|
|
71
|
-
INPUT_SETTINGS = ez.InputStream(ClockSettings)
|
|
72
|
-
OUTPUT_CLOCK = ez.OutputStream(ez.Flag)
|
|
73
|
-
|
|
74
|
-
async def initialize(self) -> None:
|
|
75
|
-
self.STATE.cur_settings = self.SETTINGS
|
|
76
|
-
self.construct_generator()
|
|
77
|
-
|
|
78
|
-
def construct_generator(self):
|
|
79
|
-
self.STATE.gen = aclock(self.STATE.cur_settings.dispatch_rate)
|
|
80
|
-
|
|
81
|
-
@ez.subscriber(INPUT_SETTINGS)
|
|
82
|
-
async def on_settings(self, msg: ClockSettings) -> None:
|
|
83
|
-
self.STATE.cur_settings = msg
|
|
84
|
-
self.construct_generator()
|
|
85
|
-
|
|
86
|
-
@ez.publisher(OUTPUT_CLOCK)
|
|
87
|
-
async def generate(self) -> typing.AsyncGenerator:
|
|
88
|
-
while True:
|
|
89
|
-
out = await self.STATE.gen.__anext__()
|
|
90
|
-
if out:
|
|
91
|
-
yield self.OUTPUT_CLOCK, out
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# COUNTER - Generate incrementing integer. fs and dispatch_rate parameters combine to give many options. #
|
|
95
|
-
async def acounter(
|
|
96
|
-
n_time: int,
|
|
97
|
-
fs: float | None,
|
|
98
|
-
n_ch: int = 1,
|
|
99
|
-
dispatch_rate: float | str | None = None,
|
|
100
|
-
mod: int | None = None,
|
|
101
|
-
) -> typing.AsyncGenerator[AxisArray, None]:
|
|
102
|
-
"""
|
|
103
|
-
Construct an asynchronous generator to generate AxisArray objects at a specified rate
|
|
104
|
-
and with the specified sampling rate.
|
|
105
|
-
|
|
106
|
-
NOTE: This module uses asyncio.sleep to delay appropriately in realtime mode.
|
|
107
|
-
This method of sleeping/yielding execution priority has quirky behavior with
|
|
108
|
-
sub-millisecond sleep periods which may result in unexpected behavior (e.g.
|
|
109
|
-
fs = 2000, n_time = 1, realtime = True -- may result in ~1400 msgs/sec)
|
|
110
|
-
|
|
111
|
-
Args:
|
|
112
|
-
n_time: Number of samples to output per block.
|
|
113
|
-
fs: Sampling rate of signal output in Hz.
|
|
114
|
-
n_ch: Number of channels to synthesize
|
|
115
|
-
dispatch_rate: Message dispatch rate (Hz), 'realtime' or None (fast as possible)
|
|
116
|
-
Note: if dispatch_rate is a float then time offsets will be synthetic and the
|
|
117
|
-
system will run faster or slower than wall clock time.
|
|
118
|
-
mod: If set to an integer, counter will rollover at this number.
|
|
119
|
-
|
|
120
|
-
Returns:
|
|
121
|
-
An asynchronous generator.
|
|
122
|
-
"""
|
|
123
|
-
|
|
124
|
-
# TODO: Adapt this to use ezmsg.util.rate?
|
|
125
|
-
|
|
126
|
-
counter_start: int = 0 # next sample's first value
|
|
127
|
-
|
|
128
|
-
b_realtime = False
|
|
129
|
-
b_manual_dispatch = False
|
|
130
|
-
b_ext_clock = False
|
|
131
|
-
if dispatch_rate is not None:
|
|
132
|
-
if isinstance(dispatch_rate, str):
|
|
133
|
-
if dispatch_rate.lower() == "realtime":
|
|
134
|
-
b_realtime = True
|
|
135
|
-
elif dispatch_rate.lower() == "ext_clock":
|
|
136
|
-
b_ext_clock = True
|
|
137
|
-
else:
|
|
138
|
-
b_manual_dispatch = True
|
|
139
|
-
|
|
140
|
-
n_sent: int = 0 # It is convenient to know how many samples we have sent.
|
|
141
|
-
clock_zero: float = time.time() # time associated with first sample
|
|
142
|
-
template = AxisArray(
|
|
143
|
-
data=np.array([[]]),
|
|
144
|
-
dims=["time", "ch"],
|
|
145
|
-
axes={
|
|
146
|
-
"time": AxisArray.TimeAxis(fs=fs),
|
|
147
|
-
"ch": AxisArray.CoordinateAxis(
|
|
148
|
-
data=np.array([f"Ch{_}" for _ in range(n_ch)]), dims=["ch"]
|
|
149
|
-
),
|
|
150
|
-
},
|
|
151
|
-
key="acounter",
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
while True:
|
|
155
|
-
# 1. Sleep, if necessary, until we are at the end of the current block
|
|
156
|
-
if b_realtime:
|
|
157
|
-
n_next = n_sent + n_time
|
|
158
|
-
t_next = clock_zero + n_next / fs
|
|
159
|
-
await asyncio.sleep(t_next - time.time())
|
|
160
|
-
elif b_manual_dispatch:
|
|
161
|
-
n_disp_next = 1 + n_sent / n_time
|
|
162
|
-
t_disp_next = clock_zero + n_disp_next / dispatch_rate
|
|
163
|
-
await asyncio.sleep(t_disp_next - time.time())
|
|
164
|
-
|
|
165
|
-
# 2. Prepare counter data.
|
|
166
|
-
block_samp = np.arange(counter_start, counter_start + n_time)[:, np.newaxis]
|
|
167
|
-
if mod is not None:
|
|
168
|
-
block_samp %= mod
|
|
169
|
-
block_samp = np.tile(block_samp, (1, n_ch))
|
|
170
|
-
|
|
171
|
-
# 3. Prepare offset - the time associated with block_samp[0]
|
|
172
|
-
if b_realtime:
|
|
173
|
-
offset = t_next - n_time / fs
|
|
174
|
-
elif b_ext_clock:
|
|
175
|
-
offset = time.time()
|
|
176
|
-
else:
|
|
177
|
-
# Purely synthetic.
|
|
178
|
-
offset = n_sent / fs
|
|
179
|
-
# offset += clock_zero # ??
|
|
180
|
-
|
|
181
|
-
# 4. yield output
|
|
182
|
-
yield replace(
|
|
183
|
-
template,
|
|
184
|
-
data=block_samp,
|
|
185
|
-
axes={
|
|
186
|
-
"time": replace(template.axes["time"], offset=offset),
|
|
187
|
-
"ch": template.axes["ch"],
|
|
188
|
-
},
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
# 5. Update state for next iteration (after next yield)
|
|
192
|
-
counter_start = block_samp[-1, 0] + 1 # do not % mod
|
|
193
|
-
n_sent += n_time
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
class CounterSettings(ez.Settings):
|
|
197
|
-
# TODO: Adapt this to use ezmsg.util.rate?
|
|
198
|
-
"""
|
|
199
|
-
Settings for :obj:`Counter`.
|
|
200
|
-
See :obj:`acounter` for a description of the parameters.
|
|
201
|
-
"""
|
|
202
|
-
|
|
203
|
-
n_time: int # Number of samples to output per block
|
|
204
|
-
fs: float # Sampling rate of signal output in Hz
|
|
205
|
-
n_ch: int = 1 # Number of channels to synthesize
|
|
206
|
-
|
|
207
|
-
# Message dispatch rate (Hz), 'realtime', 'ext_clock', or None (fast as possible)
|
|
208
|
-
# Note: if dispatch_rate is a float then time offsets will be synthetic and the
|
|
209
|
-
# system will run faster or slower than wall clock time.
|
|
210
|
-
dispatch_rate: float | str | None = None
|
|
211
|
-
|
|
212
|
-
# If set to an integer, counter will rollover
|
|
213
|
-
mod: int | None = None
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
class CounterState(ez.State):
|
|
217
|
-
gen: typing.AsyncGenerator[AxisArray, ez.Flag | None]
|
|
218
|
-
cur_settings: CounterSettings
|
|
219
|
-
new_generator: asyncio.Event
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
class Counter(ez.Unit):
|
|
223
|
-
"""Generates monotonically increasing counter. Unit for :obj:`acounter`."""
|
|
224
|
-
|
|
225
|
-
SETTINGS = CounterSettings
|
|
226
|
-
STATE = CounterState
|
|
227
|
-
|
|
228
|
-
INPUT_CLOCK = ez.InputStream(ez.Flag)
|
|
229
|
-
INPUT_SETTINGS = ez.InputStream(CounterSettings)
|
|
230
|
-
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
231
|
-
|
|
232
|
-
async def initialize(self) -> None:
|
|
233
|
-
self.STATE.new_generator = asyncio.Event()
|
|
234
|
-
self.validate_settings(self.SETTINGS)
|
|
235
|
-
|
|
236
|
-
@ez.subscriber(INPUT_SETTINGS)
|
|
237
|
-
async def on_settings(self, msg: CounterSettings) -> None:
|
|
238
|
-
self.validate_settings(msg)
|
|
239
|
-
|
|
240
|
-
def validate_settings(self, settings: CounterSettings) -> None:
|
|
241
|
-
if isinstance(
|
|
242
|
-
settings.dispatch_rate, str
|
|
243
|
-
) and self.SETTINGS.dispatch_rate not in ["realtime", "ext_clock"]:
|
|
244
|
-
raise ValueError(f"Unknown dispatch_rate: {self.SETTINGS.dispatch_rate}")
|
|
245
|
-
self.STATE.cur_settings = settings
|
|
246
|
-
self.construct_generator()
|
|
247
|
-
|
|
248
|
-
def construct_generator(self):
|
|
249
|
-
self.STATE.gen = acounter(
|
|
250
|
-
self.STATE.cur_settings.n_time,
|
|
251
|
-
self.STATE.cur_settings.fs,
|
|
252
|
-
n_ch=self.STATE.cur_settings.n_ch,
|
|
253
|
-
dispatch_rate=self.STATE.cur_settings.dispatch_rate,
|
|
254
|
-
mod=self.STATE.cur_settings.mod,
|
|
255
|
-
)
|
|
256
|
-
self.STATE.new_generator.set()
|
|
257
|
-
|
|
258
|
-
@ez.subscriber(INPUT_CLOCK)
|
|
259
|
-
@ez.publisher(OUTPUT_SIGNAL)
|
|
260
|
-
async def on_clock(self, clock: ez.Flag):
|
|
261
|
-
if self.STATE.cur_settings.dispatch_rate == "ext_clock":
|
|
262
|
-
out = await self.STATE.gen.__anext__()
|
|
263
|
-
yield self.OUTPUT_SIGNAL, out
|
|
264
|
-
|
|
265
|
-
@ez.publisher(OUTPUT_SIGNAL)
|
|
266
|
-
async def run_generator(self) -> typing.AsyncGenerator:
|
|
267
|
-
while True:
|
|
268
|
-
await self.STATE.new_generator.wait()
|
|
269
|
-
self.STATE.new_generator.clear()
|
|
270
|
-
|
|
271
|
-
if self.STATE.cur_settings.dispatch_rate == "ext_clock":
|
|
272
|
-
continue
|
|
273
|
-
|
|
274
|
-
while not self.STATE.new_generator.is_set():
|
|
275
|
-
out = await self.STATE.gen.__anext__()
|
|
276
|
-
yield self.OUTPUT_SIGNAL, out
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
@consumer
|
|
280
|
-
def sin(
|
|
281
|
-
axis: str | None = "time",
|
|
282
|
-
freq: float = 1.0,
|
|
283
|
-
amp: float = 1.0,
|
|
284
|
-
phase: float = 0.0,
|
|
285
|
-
) -> typing.Generator[AxisArray, AxisArray, None]:
|
|
286
|
-
"""
|
|
287
|
-
Construct a generator of sinusoidal waveforms in AxisArray objects.
|
|
288
|
-
|
|
289
|
-
Args:
|
|
290
|
-
axis: The name of the axis over which the sinusoid passes.
|
|
291
|
-
Note: The axis must exist in the msg.axes and be of type AxisArray.LinearAxis.
|
|
292
|
-
freq: The frequency of the sinusoid, in Hz.
|
|
293
|
-
amp: The amplitude of the sinusoid.
|
|
294
|
-
phase: The initial phase of the sinusoid, in radians.
|
|
295
|
-
|
|
296
|
-
Returns:
|
|
297
|
-
A primed generator that expects .send(axis_array) of sample counts
|
|
298
|
-
and yields an AxisArray of sinusoids.
|
|
299
|
-
"""
|
|
300
|
-
msg_out = AxisArray(np.array([]), dims=[""])
|
|
301
|
-
|
|
302
|
-
ang_freq = 2.0 * np.pi * freq
|
|
303
|
-
|
|
304
|
-
while True:
|
|
305
|
-
msg_in: AxisArray = yield msg_out
|
|
306
|
-
# msg_in is expected to be sample counts
|
|
307
|
-
|
|
308
|
-
axis_name = axis
|
|
309
|
-
if axis_name is None:
|
|
310
|
-
axis_name = msg_in.dims[0]
|
|
311
|
-
|
|
312
|
-
w = (ang_freq * msg_in.get_axis(axis_name).gain) * msg_in.data
|
|
313
|
-
out_data = amp * np.sin(w + phase)
|
|
314
|
-
msg_out = replace(msg_in, data=out_data)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
class SinGeneratorSettings(ez.Settings):
|
|
318
|
-
"""
|
|
319
|
-
Settings for :obj:`SinGenerator`.
|
|
320
|
-
See :obj:`sin` for parameter descriptions.
|
|
321
|
-
"""
|
|
322
|
-
|
|
323
|
-
time_axis: str | None = "time"
|
|
324
|
-
freq: float = 1.0 # Oscillation frequency in Hz
|
|
325
|
-
amp: float = 1.0 # Amplitude
|
|
326
|
-
phase: float = 0.0 # Phase offset (in radians)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
class SinGenerator(GenAxisArray):
|
|
330
|
-
"""
|
|
331
|
-
Unit for :obj:`sin`.
|
|
332
|
-
"""
|
|
333
|
-
|
|
334
|
-
SETTINGS = SinGeneratorSettings
|
|
335
|
-
|
|
336
|
-
def construct_generator(self):
|
|
337
|
-
self.STATE.gen = sin(
|
|
338
|
-
axis=self.SETTINGS.time_axis,
|
|
339
|
-
freq=self.SETTINGS.freq,
|
|
340
|
-
amp=self.SETTINGS.amp,
|
|
341
|
-
phase=self.SETTINGS.phase,
|
|
342
|
-
)
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
class OscillatorSettings(ez.Settings):
|
|
346
|
-
"""Settings for :obj:`Oscillator`"""
|
|
347
|
-
|
|
348
|
-
n_time: int
|
|
349
|
-
"""Number of samples to output per block."""
|
|
350
|
-
|
|
351
|
-
fs: float
|
|
352
|
-
"""Sampling rate of signal output in Hz"""
|
|
353
|
-
|
|
354
|
-
n_ch: int = 1
|
|
355
|
-
"""Number of channels to output per block"""
|
|
356
|
-
|
|
357
|
-
dispatch_rate: float | str | None = None
|
|
358
|
-
"""(Hz) | 'realtime' | 'ext_clock'"""
|
|
359
|
-
|
|
360
|
-
freq: float = 1.0
|
|
361
|
-
"""Oscillation frequency in Hz"""
|
|
362
|
-
|
|
363
|
-
amp: float = 1.0
|
|
364
|
-
"""Amplitude"""
|
|
365
|
-
|
|
366
|
-
phase: float = 0.0
|
|
367
|
-
"""Phase offset (in radians)"""
|
|
368
|
-
|
|
369
|
-
sync: bool = False
|
|
370
|
-
"""Adjust `freq` to sync with sampling rate"""
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
class Oscillator(ez.Collection):
|
|
374
|
-
"""
|
|
375
|
-
:obj:`Collection that chains :obj:`Counter` and :obj:`SinGenerator`.
|
|
376
|
-
"""
|
|
377
|
-
|
|
378
|
-
SETTINGS = OscillatorSettings
|
|
379
|
-
|
|
380
|
-
INPUT_CLOCK = ez.InputStream(ez.Flag)
|
|
381
|
-
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
382
|
-
|
|
383
|
-
COUNTER = Counter()
|
|
384
|
-
SIN = SinGenerator()
|
|
385
|
-
|
|
386
|
-
def configure(self) -> None:
|
|
387
|
-
# Calculate synchronous settings if necessary
|
|
388
|
-
freq = self.SETTINGS.freq
|
|
389
|
-
mod = None
|
|
390
|
-
if self.SETTINGS.sync:
|
|
391
|
-
period = 1.0 / self.SETTINGS.freq
|
|
392
|
-
mod = round(period * self.SETTINGS.fs)
|
|
393
|
-
freq = 1.0 / (mod / self.SETTINGS.fs)
|
|
394
|
-
|
|
395
|
-
self.COUNTER.apply_settings(
|
|
396
|
-
CounterSettings(
|
|
397
|
-
n_time=self.SETTINGS.n_time,
|
|
398
|
-
fs=self.SETTINGS.fs,
|
|
399
|
-
n_ch=self.SETTINGS.n_ch,
|
|
400
|
-
dispatch_rate=self.SETTINGS.dispatch_rate,
|
|
401
|
-
mod=mod,
|
|
402
|
-
)
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
self.SIN.apply_settings(
|
|
406
|
-
SinGeneratorSettings(
|
|
407
|
-
freq=freq, amp=self.SETTINGS.amp, phase=self.SETTINGS.phase
|
|
408
|
-
)
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
def network(self) -> ez.NetworkDefinition:
|
|
412
|
-
return (
|
|
413
|
-
(self.INPUT_CLOCK, self.COUNTER.INPUT_CLOCK),
|
|
414
|
-
(self.COUNTER.OUTPUT_SIGNAL, self.SIN.INPUT_SIGNAL),
|
|
415
|
-
(self.SIN.OUTPUT_SIGNAL, self.OUTPUT_SIGNAL),
|
|
416
|
-
)
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
class RandomGeneratorSettings(ez.Settings):
|
|
420
|
-
loc: float = 0.0
|
|
421
|
-
"""loc argument for :obj:`numpy.random.normal`"""
|
|
422
|
-
|
|
423
|
-
scale: float = 1.0
|
|
424
|
-
"""scale argument for :obj:`numpy.random.normal`"""
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
class RandomGenerator(ez.Unit):
|
|
428
|
-
"""
|
|
429
|
-
Replaces input data with random data and yields the result.
|
|
430
|
-
"""
|
|
431
|
-
|
|
432
|
-
SETTINGS = RandomGeneratorSettings
|
|
433
|
-
|
|
434
|
-
INPUT_SIGNAL = ez.InputStream(AxisArray)
|
|
435
|
-
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
436
|
-
|
|
437
|
-
@ez.subscriber(INPUT_SIGNAL)
|
|
438
|
-
@ez.publisher(OUTPUT_SIGNAL)
|
|
439
|
-
async def generate(self, msg: AxisArray) -> typing.AsyncGenerator:
|
|
440
|
-
random_data = np.random.normal(
|
|
441
|
-
size=msg.shape, loc=self.SETTINGS.loc, scale=self.SETTINGS.scale
|
|
442
|
-
)
|
|
443
|
-
|
|
444
|
-
yield self.OUTPUT_SIGNAL, replace(msg, data=random_data)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
class NoiseSettings(ez.Settings):
|
|
448
|
-
"""
|
|
449
|
-
See :obj:`CounterSettings` and :obj:`RandomGeneratorSettings`.
|
|
450
|
-
"""
|
|
451
|
-
|
|
452
|
-
n_time: int # Number of samples to output per block
|
|
453
|
-
fs: float # Sampling rate of signal output in Hz
|
|
454
|
-
n_ch: int = 1 # Number of channels to output
|
|
455
|
-
dispatch_rate: float | str | None = None
|
|
456
|
-
"""(Hz), 'realtime', or 'ext_clock'"""
|
|
457
|
-
loc: float = 0.0 # DC offset
|
|
458
|
-
scale: float = 1.0 # Scale (in standard deviations)
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
WhiteNoiseSettings = NoiseSettings
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
class WhiteNoise(ez.Collection):
|
|
465
|
-
"""
|
|
466
|
-
A :obj:`Collection` that chains a :obj:`Counter` and :obj:`RandomGenerator`.
|
|
467
|
-
"""
|
|
468
|
-
|
|
469
|
-
SETTINGS = NoiseSettings
|
|
470
|
-
|
|
471
|
-
INPUT_CLOCK = ez.InputStream(ez.Flag)
|
|
472
|
-
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
473
|
-
|
|
474
|
-
COUNTER = Counter()
|
|
475
|
-
RANDOM = RandomGenerator()
|
|
476
|
-
|
|
477
|
-
def configure(self) -> None:
|
|
478
|
-
self.RANDOM.apply_settings(
|
|
479
|
-
RandomGeneratorSettings(loc=self.SETTINGS.loc, scale=self.SETTINGS.scale)
|
|
480
|
-
)
|
|
481
|
-
|
|
482
|
-
self.COUNTER.apply_settings(
|
|
483
|
-
CounterSettings(
|
|
484
|
-
n_time=self.SETTINGS.n_time,
|
|
485
|
-
fs=self.SETTINGS.fs,
|
|
486
|
-
n_ch=self.SETTINGS.n_ch,
|
|
487
|
-
dispatch_rate=self.SETTINGS.dispatch_rate,
|
|
488
|
-
mod=None,
|
|
489
|
-
)
|
|
490
|
-
)
|
|
491
|
-
|
|
492
|
-
def network(self) -> ez.NetworkDefinition:
|
|
493
|
-
return (
|
|
494
|
-
(self.INPUT_CLOCK, self.COUNTER.INPUT_CLOCK),
|
|
495
|
-
(self.COUNTER.OUTPUT_SIGNAL, self.RANDOM.INPUT_SIGNAL),
|
|
496
|
-
(self.RANDOM.OUTPUT_SIGNAL, self.OUTPUT_SIGNAL),
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
PinkNoiseSettings = NoiseSettings
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
class PinkNoise(ez.Collection):
|
|
504
|
-
"""
|
|
505
|
-
A :obj:`Collection` that chains :obj:`WhiteNoise` and :obj:`ButterworthFilter`.
|
|
506
|
-
"""
|
|
507
|
-
|
|
508
|
-
SETTINGS = PinkNoiseSettings
|
|
509
|
-
|
|
510
|
-
INPUT_CLOCK = ez.InputStream(ez.Flag)
|
|
511
|
-
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
512
|
-
|
|
513
|
-
WHITE_NOISE = WhiteNoise()
|
|
514
|
-
FILTER = ButterworthFilter()
|
|
515
|
-
|
|
516
|
-
def configure(self) -> None:
|
|
517
|
-
self.WHITE_NOISE.apply_settings(self.SETTINGS)
|
|
518
|
-
self.FILTER.apply_settings(
|
|
519
|
-
ButterworthFilterSettings(
|
|
520
|
-
axis="time",
|
|
521
|
-
order=1,
|
|
522
|
-
cutoff=self.SETTINGS.fs * 0.01, # Hz
|
|
523
|
-
)
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
def network(self) -> ez.NetworkDefinition:
|
|
527
|
-
return (
|
|
528
|
-
(self.INPUT_CLOCK, self.WHITE_NOISE.INPUT_CLOCK),
|
|
529
|
-
(self.WHITE_NOISE.OUTPUT_SIGNAL, self.FILTER.INPUT_SIGNAL),
|
|
530
|
-
(self.FILTER.OUTPUT_SIGNAL, self.OUTPUT_SIGNAL),
|
|
531
|
-
)
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
class AddState(ez.State):
|
|
535
|
-
queue_a: "asyncio.Queue[AxisArray]" = field(default_factory=asyncio.Queue)
|
|
536
|
-
queue_b: "asyncio.Queue[AxisArray]" = field(default_factory=asyncio.Queue)
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
class Add(ez.Unit):
|
|
540
|
-
"""Add two signals together. Assumes compatible/similar axes/dimensions."""
|
|
541
|
-
|
|
542
|
-
STATE = AddState
|
|
543
|
-
|
|
544
|
-
INPUT_SIGNAL_A = ez.InputStream(AxisArray)
|
|
545
|
-
INPUT_SIGNAL_B = ez.InputStream(AxisArray)
|
|
546
|
-
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
547
|
-
|
|
548
|
-
@ez.subscriber(INPUT_SIGNAL_A)
|
|
549
|
-
async def on_a(self, msg: AxisArray) -> None:
|
|
550
|
-
self.STATE.queue_a.put_nowait(msg)
|
|
551
|
-
|
|
552
|
-
@ez.subscriber(INPUT_SIGNAL_B)
|
|
553
|
-
async def on_b(self, msg: AxisArray) -> None:
|
|
554
|
-
self.STATE.queue_b.put_nowait(msg)
|
|
555
|
-
|
|
556
|
-
@ez.publisher(OUTPUT_SIGNAL)
|
|
557
|
-
async def output(self) -> typing.AsyncGenerator:
|
|
558
|
-
while True:
|
|
559
|
-
a = await self.STATE.queue_a.get()
|
|
560
|
-
b = await self.STATE.queue_b.get()
|
|
561
|
-
|
|
562
|
-
yield self.OUTPUT_SIGNAL, replace(a, data=a.data + b.data)
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
class EEGSynthSettings(ez.Settings):
|
|
566
|
-
"""See :obj:`OscillatorSettings`."""
|
|
567
|
-
|
|
568
|
-
fs: float = 500.0 # Hz
|
|
569
|
-
n_time: int = 100
|
|
570
|
-
alpha_freq: float = 10.5 # Hz
|
|
571
|
-
n_ch: int = 8
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
class EEGSynth(ez.Collection):
|
|
575
|
-
"""
|
|
576
|
-
A :obj:`Collection` that chains a :obj:`Clock` to both :obj:`PinkNoise`
|
|
577
|
-
and :obj:`Oscillator`, then :obj:`Add` s the result.
|
|
578
|
-
"""
|
|
579
|
-
|
|
580
|
-
SETTINGS = EEGSynthSettings
|
|
581
|
-
|
|
582
|
-
OUTPUT_SIGNAL = ez.OutputStream(AxisArray)
|
|
583
|
-
|
|
584
|
-
CLOCK = Clock()
|
|
585
|
-
NOISE = PinkNoise()
|
|
586
|
-
OSC = Oscillator()
|
|
587
|
-
ADD = Add()
|
|
588
|
-
|
|
589
|
-
def configure(self) -> None:
|
|
590
|
-
self.CLOCK.apply_settings(
|
|
591
|
-
ClockSettings(dispatch_rate=self.SETTINGS.fs / self.SETTINGS.n_time)
|
|
592
|
-
)
|
|
593
|
-
|
|
594
|
-
self.OSC.apply_settings(
|
|
595
|
-
OscillatorSettings(
|
|
596
|
-
n_time=self.SETTINGS.n_time,
|
|
597
|
-
fs=self.SETTINGS.fs,
|
|
598
|
-
n_ch=self.SETTINGS.n_ch,
|
|
599
|
-
dispatch_rate="ext_clock",
|
|
600
|
-
freq=self.SETTINGS.alpha_freq,
|
|
601
|
-
)
|
|
602
|
-
)
|
|
603
|
-
|
|
604
|
-
self.NOISE.apply_settings(
|
|
605
|
-
PinkNoiseSettings(
|
|
606
|
-
n_time=self.SETTINGS.n_time,
|
|
607
|
-
fs=self.SETTINGS.fs,
|
|
608
|
-
n_ch=self.SETTINGS.n_ch,
|
|
609
|
-
dispatch_rate="ext_clock",
|
|
610
|
-
scale=5.0,
|
|
611
|
-
)
|
|
612
|
-
)
|
|
613
|
-
|
|
614
|
-
def network(self) -> ez.NetworkDefinition:
|
|
615
|
-
return (
|
|
616
|
-
(self.CLOCK.OUTPUT_CLOCK, self.OSC.INPUT_CLOCK),
|
|
617
|
-
(self.CLOCK.OUTPUT_CLOCK, self.NOISE.INPUT_CLOCK),
|
|
618
|
-
(self.OSC.OUTPUT_SIGNAL, self.ADD.INPUT_SIGNAL_A),
|
|
619
|
-
(self.NOISE.OUTPUT_SIGNAL, self.ADD.INPUT_SIGNAL_B),
|
|
620
|
-
(self.ADD.OUTPUT_SIGNAL, self.OUTPUT_SIGNAL),
|
|
621
|
-
)
|