acoular 24.7__py3-none-any.whl → 25.1__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.
- acoular/__init__.py +21 -9
- acoular/aiaa/__init__.py +12 -0
- acoular/{tools → aiaa}/aiaa.py +26 -31
- acoular/base.py +332 -0
- acoular/calib.py +129 -34
- acoular/configuration.py +13 -11
- acoular/demo/__init__.py +1 -0
- acoular/demo/acoular_demo.py +30 -17
- acoular/deprecation.py +85 -0
- acoular/environments.py +38 -24
- acoular/fastFuncs.py +90 -84
- acoular/fbeamform.py +342 -387
- acoular/fprocess.py +376 -0
- acoular/grids.py +122 -150
- acoular/h5cache.py +29 -40
- acoular/h5files.py +2 -6
- acoular/microphones.py +50 -59
- acoular/process.py +771 -0
- acoular/sdinput.py +35 -21
- acoular/signals.py +120 -113
- acoular/sources.py +208 -234
- acoular/spectra.py +59 -254
- acoular/tbeamform.py +280 -280
- acoular/tfastfuncs.py +21 -21
- acoular/tools/__init__.py +3 -7
- acoular/tools/helpers.py +218 -4
- acoular/tools/metrics.py +5 -5
- acoular/tools/utils.py +116 -0
- acoular/tprocess.py +416 -741
- acoular/traitsviews.py +15 -13
- acoular/trajectory.py +7 -10
- acoular/version.py +2 -2
- {acoular-24.7.dist-info → acoular-25.1.dist-info}/METADATA +63 -21
- acoular-25.1.dist-info/RECORD +56 -0
- {acoular-24.7.dist-info → acoular-25.1.dist-info}/WHEEL +1 -1
- acoular-24.7.dist-info/RECORD +0 -50
- {acoular-24.7.dist-info → acoular-25.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-24.7.dist-info → acoular-25.1.dist-info}/licenses/LICENSE +0 -0
acoular/sdinput.py
CHANGED
|
@@ -9,16 +9,19 @@
|
|
|
9
9
|
SoundDeviceSamplesGenerator
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from traits.api import Any, Bool,
|
|
12
|
+
from traits.api import Any, Bool, Enum, Float, Int, Property, cached_property, observe
|
|
13
13
|
|
|
14
|
+
# acoular imports
|
|
15
|
+
from .base import SamplesGenerator
|
|
14
16
|
from .configuration import config
|
|
17
|
+
from .deprecation import deprecated_alias
|
|
15
18
|
from .internal import digest
|
|
16
|
-
from .tprocess import SamplesGenerator
|
|
17
19
|
|
|
18
20
|
if config.have_sounddevice:
|
|
19
21
|
import sounddevice as sd
|
|
20
22
|
|
|
21
23
|
|
|
24
|
+
@deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples', 'collectsamples': 'collect_samples'})
|
|
22
25
|
class SoundDeviceSamplesGenerator(SamplesGenerator):
|
|
23
26
|
"""Controller for sound card hardware using sounddevice library.
|
|
24
27
|
|
|
@@ -37,18 +40,23 @@ class SoundDeviceSamplesGenerator(SamplesGenerator):
|
|
|
37
40
|
device = Int(0, desc='input device index')
|
|
38
41
|
|
|
39
42
|
#: Number of input channels, maximum depends on device
|
|
40
|
-
|
|
43
|
+
num_channels = Int(1, desc='number of analog input channels that collects data')
|
|
41
44
|
|
|
42
|
-
#: Number of samples to collect; defaults to -1.
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
+
#: Number of samples to collect; defaults to -1. If is set to -1 device collects until user
|
|
46
|
+
# breaks streaming by setting Trait: collect_samples = False.
|
|
47
|
+
num_samples = Int(-1, desc='number of samples to collect')
|
|
45
48
|
|
|
46
49
|
#: Indicates if samples are collected, helper trait to break result loop
|
|
47
|
-
|
|
50
|
+
collect_samples = Bool(True, desc='Indicates if samples are collected')
|
|
48
51
|
|
|
49
52
|
#: Sampling frequency of the signal, changes with sinusdevices
|
|
50
53
|
sample_freq = Property(desc='sampling frequency')
|
|
51
54
|
|
|
55
|
+
_sample_freq = Float(default_value=None)
|
|
56
|
+
|
|
57
|
+
#: Datatype (resolution) of the signal, used as `dtype` in a sd `Stream` object
|
|
58
|
+
precision = Enum('float32', 'float16', 'int32', 'int16', 'int8', 'uint8', desc='precision (resolution) of signal')
|
|
59
|
+
|
|
52
60
|
#: Indicates that the sounddevice buffer has overflown
|
|
53
61
|
overflow = Bool(False, desc='Indicates if sounddevice buffer overflow')
|
|
54
62
|
|
|
@@ -59,19 +67,24 @@ class SoundDeviceSamplesGenerator(SamplesGenerator):
|
|
|
59
67
|
stream = Any
|
|
60
68
|
|
|
61
69
|
# internal identifier
|
|
62
|
-
digest = Property(depends_on=['device', '
|
|
70
|
+
digest = Property(depends_on=['device', 'num_channels', 'num_samples'])
|
|
63
71
|
|
|
64
72
|
@cached_property
|
|
65
73
|
def _get_digest(self):
|
|
66
74
|
return digest(self)
|
|
67
75
|
|
|
68
|
-
# checks that
|
|
69
|
-
@observe('device,
|
|
70
|
-
def
|
|
71
|
-
self.
|
|
76
|
+
# checks that num_channels are not more than device can provide
|
|
77
|
+
@observe('device, num_channels')
|
|
78
|
+
def _get_num_channels(self, event): # noqa ARG002
|
|
79
|
+
self.num_channels = min(self.num_channels, sd.query_devices(self.device)['max_input_channels'])
|
|
72
80
|
|
|
73
81
|
def _get_sample_freq(self):
|
|
74
|
-
|
|
82
|
+
if self._sample_freq is None:
|
|
83
|
+
self._sample_freq = sd.query_devices(self.device)['default_samplerate']
|
|
84
|
+
return self._sample_freq
|
|
85
|
+
|
|
86
|
+
def _set_sample_freq(self, f):
|
|
87
|
+
self._sample_freq = f
|
|
75
88
|
|
|
76
89
|
def device_properties(self):
|
|
77
90
|
"""Returns
|
|
@@ -92,29 +105,30 @@ class SoundDeviceSamplesGenerator(SamplesGenerator):
|
|
|
92
105
|
|
|
93
106
|
Returns
|
|
94
107
|
-------
|
|
95
|
-
Samples in blocks of shape (num, :attr:`
|
|
108
|
+
Samples in blocks of shape (num, :attr:`num_channels`).
|
|
96
109
|
The last block may be shorter than num.
|
|
97
110
|
|
|
98
111
|
"""
|
|
99
112
|
print(self.device_properties(), self.sample_freq)
|
|
100
113
|
self.stream = stream_obj = sd.InputStream(
|
|
101
114
|
device=self.device,
|
|
102
|
-
channels=self.
|
|
115
|
+
channels=self.num_channels,
|
|
103
116
|
clip_off=True,
|
|
104
117
|
samplerate=self.sample_freq,
|
|
118
|
+
dtype=self.precision,
|
|
105
119
|
)
|
|
106
120
|
|
|
107
121
|
with stream_obj as stream:
|
|
108
122
|
self.running = True
|
|
109
|
-
if self.
|
|
110
|
-
while self.
|
|
123
|
+
if self.num_samples == -1:
|
|
124
|
+
while self.collect_samples: # yield data as long as collect_samples is True
|
|
111
125
|
data, self.overflow = stream.read(num)
|
|
112
126
|
yield data[:num]
|
|
113
127
|
|
|
114
|
-
elif self.
|
|
115
|
-
samples_count = 0 #
|
|
116
|
-
while samples_count < self.
|
|
117
|
-
anz = min(num, self.
|
|
128
|
+
elif self.num_samples > 0: # amount of samples to collect is specified by user
|
|
129
|
+
samples_count = 0 # num_samples counter
|
|
130
|
+
while samples_count < self.num_samples:
|
|
131
|
+
anz = min(num, self.num_samples - samples_count)
|
|
118
132
|
data, self.overflow = stream.read(num)
|
|
119
133
|
yield data[:anz]
|
|
120
134
|
samples_count += anz
|
acoular/signals.py
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
:toctree: generated/
|
|
8
8
|
|
|
9
9
|
SignalGenerator
|
|
10
|
+
PeriodicSignalGenerator
|
|
11
|
+
NoiseGenerator
|
|
10
12
|
WNoiseGenerator
|
|
11
13
|
PNoiseGenerator
|
|
12
14
|
FiltWNoiseGenerator
|
|
@@ -16,20 +18,33 @@
|
|
|
16
18
|
"""
|
|
17
19
|
|
|
18
20
|
# imports from other packages
|
|
21
|
+
from abc import abstractmethod
|
|
19
22
|
from warnings import warn
|
|
20
23
|
|
|
21
24
|
from numpy import arange, array, log, pi, repeat, sin, sqrt, tile, zeros
|
|
22
25
|
from numpy.random import RandomState
|
|
23
26
|
from scipy.signal import resample, sosfilt, tf2sos
|
|
24
|
-
from traits.api import
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
from traits.api import (
|
|
28
|
+
ABCHasStrictTraits,
|
|
29
|
+
Bool,
|
|
30
|
+
CArray,
|
|
31
|
+
CInt,
|
|
32
|
+
Delegate,
|
|
33
|
+
Float,
|
|
34
|
+
Instance,
|
|
35
|
+
Int,
|
|
36
|
+
Property,
|
|
37
|
+
cached_property,
|
|
38
|
+
)
|
|
27
39
|
|
|
28
40
|
# acoular imports
|
|
29
|
-
from .
|
|
41
|
+
from .base import SamplesGenerator
|
|
42
|
+
from .deprecation import deprecated_alias
|
|
43
|
+
from .internal import digest
|
|
30
44
|
|
|
31
45
|
|
|
32
|
-
|
|
46
|
+
@deprecated_alias({'numsamples': 'num_samples'})
|
|
47
|
+
class SignalGenerator(ABCHasStrictTraits):
|
|
33
48
|
"""Virtual base class for a simple one-channel signal generator.
|
|
34
49
|
|
|
35
50
|
Defines the common interface for all SignalGenerator classes. This class
|
|
@@ -37,21 +52,20 @@ class SignalGenerator(HasPrivateTraits):
|
|
|
37
52
|
should not be used directly as it contains no real functionality.
|
|
38
53
|
"""
|
|
39
54
|
|
|
40
|
-
#: RMS amplitude of source signal (for point source: in 1 m distance).
|
|
41
|
-
rms = Float(1.0, desc='rms amplitude')
|
|
42
|
-
|
|
43
55
|
#: Sampling frequency of the signal.
|
|
44
56
|
sample_freq = Float(1.0, desc='sampling frequency')
|
|
45
57
|
|
|
46
58
|
#: Number of samples to generate.
|
|
47
|
-
|
|
59
|
+
num_samples = CInt
|
|
48
60
|
|
|
49
61
|
# internal identifier
|
|
50
|
-
digest = Property
|
|
62
|
+
digest = Property(depends_on=['sample_freq', 'num_samples'])
|
|
51
63
|
|
|
64
|
+
@abstractmethod
|
|
52
65
|
def _get_digest(self):
|
|
53
|
-
|
|
66
|
+
"""Returns the internal identifier."""
|
|
54
67
|
|
|
68
|
+
@abstractmethod
|
|
55
69
|
def signal(self):
|
|
56
70
|
"""Deliver the signal."""
|
|
57
71
|
|
|
@@ -69,14 +83,52 @@ class SignalGenerator(HasPrivateTraits):
|
|
|
69
83
|
Returns
|
|
70
84
|
-------
|
|
71
85
|
array of floats
|
|
72
|
-
The resulting signal of length `factor` * :attr:`
|
|
73
|
-
|
|
86
|
+
The resulting signal of length `factor` * :attr:`num_samples`.
|
|
74
87
|
"""
|
|
75
|
-
return resample(self.signal(), factor * self.
|
|
88
|
+
return resample(self.signal(), factor * self.num_samples)
|
|
76
89
|
|
|
77
90
|
|
|
78
|
-
class
|
|
79
|
-
"""
|
|
91
|
+
class PeriodicSignalGenerator(SignalGenerator):
|
|
92
|
+
"""
|
|
93
|
+
Abstract base class for periodic signal generators.
|
|
94
|
+
|
|
95
|
+
Defines the common interface for all :class:`SignalGenerator`-derived classes with periodic
|
|
96
|
+
signals. This class may be used as a base for class handling periodic signals that can be
|
|
97
|
+
characterized by their frequency, phase and amplitude. It should not be used directly as it
|
|
98
|
+
contains no real functionality.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
#: Frequency of the signal, float, defaults to 1000.0.
|
|
102
|
+
freq = Float(1000.0, desc='Frequency')
|
|
103
|
+
|
|
104
|
+
#: Phase of the signal (in radians), float, defaults to 0.0.
|
|
105
|
+
phase = Float(0.0, desc='Phase')
|
|
106
|
+
|
|
107
|
+
#: Amplitude of the signal. Defaults to 1.0.
|
|
108
|
+
amplitude = Float(1.0)
|
|
109
|
+
|
|
110
|
+
# internal identifier
|
|
111
|
+
digest = Property(depends_on=['amplitude', 'num_samples', 'sample_freq', 'freq', 'phase'])
|
|
112
|
+
|
|
113
|
+
@abstractmethod
|
|
114
|
+
def _get_digest(self):
|
|
115
|
+
"""Returns the internal identifier."""
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def signal(self):
|
|
119
|
+
"""Deliver the signal."""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class NoiseGenerator(SignalGenerator):
|
|
123
|
+
"""Abstract base class for noise signal generators.
|
|
124
|
+
|
|
125
|
+
Defines the common interface for all :class:`SignalGenerator` classes with noise signals. This
|
|
126
|
+
class may be used as a base for class handling noise signals that can be characterized by their
|
|
127
|
+
RMS amplitude. It should not be used directly as it contains no real functionality.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
#: RMS amplitude of the signal.
|
|
131
|
+
rms = Float(1.0, desc='rms amplitude')
|
|
80
132
|
|
|
81
133
|
#: Seed for random number generator, defaults to 0.
|
|
82
134
|
#: This parameter should be set differently for different instances
|
|
@@ -84,9 +136,22 @@ class WNoiseGenerator(SignalGenerator):
|
|
|
84
136
|
seed = Int(0, desc='random seed value')
|
|
85
137
|
|
|
86
138
|
# internal identifier
|
|
87
|
-
digest = Property(
|
|
88
|
-
|
|
89
|
-
|
|
139
|
+
digest = Property(depends_on=['rms', 'seed', 'sample_freq', 'num_samples'])
|
|
140
|
+
|
|
141
|
+
@abstractmethod
|
|
142
|
+
def _get_digest(self):
|
|
143
|
+
"""Returns the internal identifier."""
|
|
144
|
+
|
|
145
|
+
@abstractmethod
|
|
146
|
+
def signal(self):
|
|
147
|
+
"""Deliver the signal."""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class WNoiseGenerator(NoiseGenerator):
|
|
151
|
+
"""White noise signal generator."""
|
|
152
|
+
|
|
153
|
+
# internal identifier
|
|
154
|
+
digest = Property(depends_on=['rms', 'seed', 'sample_freq', 'num_samples'])
|
|
90
155
|
|
|
91
156
|
@cached_property
|
|
92
157
|
def _get_digest(self):
|
|
@@ -98,14 +163,13 @@ class WNoiseGenerator(SignalGenerator):
|
|
|
98
163
|
Returns
|
|
99
164
|
-------
|
|
100
165
|
Array of floats
|
|
101
|
-
The resulting signal as an array of length :attr:`~SignalGenerator.
|
|
102
|
-
|
|
166
|
+
The resulting signal as an array of length :attr:`~SignalGenerator.num_samples`.
|
|
103
167
|
"""
|
|
104
168
|
rnd_gen = RandomState(self.seed)
|
|
105
|
-
return self.rms * rnd_gen.standard_normal(self.
|
|
169
|
+
return self.rms * rnd_gen.standard_normal(self.num_samples)
|
|
106
170
|
|
|
107
171
|
|
|
108
|
-
class PNoiseGenerator(
|
|
172
|
+
class PNoiseGenerator(NoiseGenerator):
|
|
109
173
|
"""Pink noise signal generator.
|
|
110
174
|
|
|
111
175
|
Simulation of pink noise is based on the Voss-McCartney algorithm.
|
|
@@ -118,33 +182,26 @@ class PNoiseGenerator(SignalGenerator):
|
|
|
118
182
|
characteristic.
|
|
119
183
|
"""
|
|
120
184
|
|
|
121
|
-
#: Seed for random number generator, defaults to 0.
|
|
122
|
-
#: This parameter should be set differently for different instances
|
|
123
|
-
#: to guarantee statistically independent (non-correlated) outputs.
|
|
124
|
-
seed = Int(0, desc='random seed value')
|
|
125
|
-
|
|
126
185
|
#: "Octave depth" -- higher values for 1/f spectrum at low frequencies,
|
|
127
186
|
#: but longer calculation, defaults to 16.
|
|
128
187
|
depth = Int(16, desc='octave depth')
|
|
129
188
|
|
|
130
189
|
# internal identifier
|
|
131
|
-
digest = Property(
|
|
132
|
-
depends_on=['rms', 'numsamples', 'sample_freq', 'seed', 'depth', '__class__'],
|
|
133
|
-
)
|
|
190
|
+
digest = Property(depends_on=['rms', 'seed', 'sample_freq', 'num_samples', 'depth'])
|
|
134
191
|
|
|
135
192
|
@cached_property
|
|
136
193
|
def _get_digest(self):
|
|
137
194
|
return digest(self)
|
|
138
195
|
|
|
139
196
|
def signal(self):
|
|
140
|
-
nums = self.
|
|
197
|
+
nums = self.num_samples
|
|
141
198
|
depth = self.depth
|
|
142
199
|
# maximum depth depending on number of samples
|
|
143
200
|
max_depth = int(log(nums) / log(2))
|
|
144
201
|
|
|
145
202
|
if depth > max_depth:
|
|
146
203
|
depth = max_depth
|
|
147
|
-
print('Pink noise filter depth set to maximum possible value of
|
|
204
|
+
print(f'Pink noise filter depth set to maximum possible value of {max_depth:d}.')
|
|
148
205
|
|
|
149
206
|
rnd_gen = RandomState(self.seed)
|
|
150
207
|
s = rnd_gen.standard_normal(nums)
|
|
@@ -174,17 +231,7 @@ class FiltWNoiseGenerator(WNoiseGenerator):
|
|
|
174
231
|
ma = CArray(value=array([]), dtype=float, desc='moving-average coefficients (coefficients of the numerator)')
|
|
175
232
|
|
|
176
233
|
# internal identifier
|
|
177
|
-
digest = Property(
|
|
178
|
-
depends_on=[
|
|
179
|
-
'ar',
|
|
180
|
-
'ma',
|
|
181
|
-
'rms',
|
|
182
|
-
'numsamples',
|
|
183
|
-
'sample_freq',
|
|
184
|
-
'seed',
|
|
185
|
-
'__class__',
|
|
186
|
-
],
|
|
187
|
-
)
|
|
234
|
+
digest = Property(depends_on=['rms', 'seed', 'sample_freq', 'num_samples', 'ar', 'ma'])
|
|
188
235
|
|
|
189
236
|
@cached_property
|
|
190
237
|
def _get_digest(self):
|
|
@@ -201,8 +248,7 @@ class FiltWNoiseGenerator(WNoiseGenerator):
|
|
|
201
248
|
Returns
|
|
202
249
|
-------
|
|
203
250
|
Array of floats
|
|
204
|
-
The resulting signal as an array of length :attr:`~SignalGenerator.
|
|
205
|
-
|
|
251
|
+
The resulting signal as an array of length :attr:`~SignalGenerator.num_samples`.
|
|
206
252
|
"""
|
|
207
253
|
rnd_gen = RandomState(self.seed)
|
|
208
254
|
ma = self.handle_empty_coefficients(self.ma)
|
|
@@ -211,55 +257,16 @@ class FiltWNoiseGenerator(WNoiseGenerator):
|
|
|
211
257
|
ntaps = ma.shape[0]
|
|
212
258
|
sdelay = round(0.5 * (ntaps - 1))
|
|
213
259
|
wnoise = self.rms * rnd_gen.standard_normal(
|
|
214
|
-
self.
|
|
260
|
+
self.num_samples + sdelay,
|
|
215
261
|
) # create longer signal to compensate delay
|
|
216
262
|
return sosfilt(sos, x=wnoise)[sdelay:]
|
|
217
263
|
|
|
218
264
|
|
|
219
|
-
class SineGenerator(
|
|
220
|
-
"""Sine signal generator with adjustable frequency and phase."""
|
|
221
|
-
|
|
222
|
-
#: Sine wave frequency, float, defaults to 1000.0.
|
|
223
|
-
freq = Float(1000.0, desc='Frequency')
|
|
224
|
-
|
|
225
|
-
#: Sine wave phase (in radians), float, defaults to 0.0.
|
|
226
|
-
phase = Float(0.0, desc='Phase')
|
|
227
|
-
|
|
228
|
-
# Internal shadow trait for rms/amplitude values.
|
|
229
|
-
# Do not set directly.
|
|
230
|
-
_amp = Float(1.0)
|
|
231
|
-
|
|
232
|
-
#: RMS of source signal (for point source: in 1 m distance).
|
|
233
|
-
#: Deprecated. For amplitude use :attr:`amplitude`.
|
|
234
|
-
rms = Property(desc='rms amplitude')
|
|
235
|
-
|
|
236
|
-
def _get_rms(self):
|
|
237
|
-
return self._amp / 2**0.5
|
|
238
|
-
|
|
239
|
-
def _set_rms(self, rms):
|
|
240
|
-
warn(
|
|
241
|
-
'Up to Acoular 20.02, rms is interpreted as sine amplitude. '
|
|
242
|
-
'This has since been corrected (rms now is 1/sqrt(2) of amplitude). '
|
|
243
|
-
"Use 'amplitude' trait to directly set the ampltiude.",
|
|
244
|
-
Warning,
|
|
245
|
-
stacklevel=2,
|
|
246
|
-
)
|
|
247
|
-
self._amp = rms * 2**0.5
|
|
248
|
-
|
|
249
|
-
#: Amplitude of source signal (for point source: in 1 m distance).
|
|
250
|
-
#: Defaults to 1.0.
|
|
251
|
-
amplitude = Property(desc='amplitude')
|
|
252
|
-
|
|
253
|
-
def _get_amplitude(self):
|
|
254
|
-
return self._amp
|
|
255
|
-
|
|
256
|
-
def _set_amplitude(self, amp):
|
|
257
|
-
self._amp = amp
|
|
265
|
+
class SineGenerator(PeriodicSignalGenerator):
|
|
266
|
+
"""Sine signal generator with adjustable amplitude, frequency and phase."""
|
|
258
267
|
|
|
259
268
|
# internal identifier
|
|
260
|
-
digest = Property(
|
|
261
|
-
depends_on=['_amp', 'numsamples', 'sample_freq', 'freq', 'phase', '__class__'],
|
|
262
|
-
)
|
|
269
|
+
digest = Property(depends_on=['num_samples', 'sample_freq', 'amplitude', 'freq', 'phase'])
|
|
263
270
|
|
|
264
271
|
@cached_property
|
|
265
272
|
def _get_digest(self):
|
|
@@ -271,15 +278,15 @@ class SineGenerator(SignalGenerator):
|
|
|
271
278
|
Returns
|
|
272
279
|
-------
|
|
273
280
|
array of floats
|
|
274
|
-
The resulting signal as an array of length :attr:`~SignalGenerator.
|
|
275
|
-
|
|
281
|
+
The resulting signal as an array of length :attr:`~SignalGenerator.num_samples`.
|
|
276
282
|
"""
|
|
277
|
-
t = arange(self.
|
|
283
|
+
t = arange(self.num_samples, dtype=float) / self.sample_freq
|
|
278
284
|
return self.amplitude * sin(2 * pi * self.freq * t + self.phase)
|
|
279
285
|
|
|
280
286
|
|
|
287
|
+
@deprecated_alias({'rms': 'amplitude'})
|
|
281
288
|
class GenericSignalGenerator(SignalGenerator):
|
|
282
|
-
"""Generate signal from output of :class:`~acoular.
|
|
289
|
+
"""Generate signal from output of :class:`~acoular.base.SamplesGenerator` object.
|
|
283
290
|
|
|
284
291
|
This class can be used to inject arbitrary signals into Acoular processing
|
|
285
292
|
chains. For example, it can be used to read signals from a HDF5 file or create any signal
|
|
@@ -292,35 +299,37 @@ class GenericSignalGenerator(SignalGenerator):
|
|
|
292
299
|
>>> data = np.random.rand(1000, 1)
|
|
293
300
|
>>> ts = TimeSamples(data=data, sample_freq=51200)
|
|
294
301
|
>>> sig = GenericSignalGenerator(source=ts)
|
|
295
|
-
|
|
296
302
|
"""
|
|
297
303
|
|
|
298
|
-
#: Data source; :class:`~acoular.
|
|
299
|
-
source =
|
|
304
|
+
#: Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
305
|
+
source = Instance(SamplesGenerator)
|
|
306
|
+
|
|
307
|
+
#: Amplitude of the signal. Defaults to 1.0.
|
|
308
|
+
amplitude = Float(1.0)
|
|
300
309
|
|
|
301
310
|
#: Sampling frequency of output signal, as given by :attr:`source`.
|
|
302
311
|
sample_freq = Delegate('source')
|
|
303
312
|
|
|
304
|
-
|
|
313
|
+
_num_samples = CInt(0)
|
|
305
314
|
|
|
306
|
-
#: Number of samples to generate. Is set to source.
|
|
307
|
-
|
|
315
|
+
#: Number of samples to generate. Is set to source.num_samples by default.
|
|
316
|
+
num_samples = Property()
|
|
308
317
|
|
|
309
|
-
def
|
|
310
|
-
if self.
|
|
311
|
-
return self.
|
|
312
|
-
return self.source.
|
|
318
|
+
def _get_num_samples(self):
|
|
319
|
+
if self._num_samples:
|
|
320
|
+
return self._num_samples
|
|
321
|
+
return self.source.num_samples
|
|
313
322
|
|
|
314
|
-
def
|
|
315
|
-
self.
|
|
323
|
+
def _set_num_samples(self, num_samples):
|
|
324
|
+
self._num_samples = num_samples
|
|
316
325
|
|
|
317
326
|
#: Boolean flag, if 'True' (default), signal track is repeated if requested
|
|
318
|
-
#: :attr:`
|
|
327
|
+
#: :attr:`num_samples` is higher than available sample number
|
|
319
328
|
loop_signal = Bool(True)
|
|
320
329
|
|
|
321
330
|
# internal identifier
|
|
322
331
|
digest = Property(
|
|
323
|
-
depends_on=['source.digest', 'loop_signal', '
|
|
332
|
+
depends_on=['source.digest', 'loop_signal', 'num_samples', 'amplitude'],
|
|
324
333
|
)
|
|
325
334
|
|
|
326
335
|
@cached_property
|
|
@@ -333,17 +342,17 @@ class GenericSignalGenerator(SignalGenerator):
|
|
|
333
342
|
Returns
|
|
334
343
|
-------
|
|
335
344
|
array of floats
|
|
336
|
-
The resulting signal as an array of length :attr:`~GenericSignalGenerator.
|
|
345
|
+
The resulting signal as an array of length :attr:`~GenericSignalGenerator.num_samples`.
|
|
337
346
|
|
|
338
347
|
"""
|
|
339
348
|
block = 1024
|
|
340
|
-
if self.source.
|
|
349
|
+
if self.source.num_channels > 1:
|
|
341
350
|
warn(
|
|
342
351
|
'Signal source has more than one channel. Only channel 0 will be used for signal.',
|
|
343
352
|
Warning,
|
|
344
353
|
stacklevel=2,
|
|
345
354
|
)
|
|
346
|
-
nums = self.
|
|
355
|
+
nums = self.num_samples
|
|
347
356
|
track = zeros(nums)
|
|
348
357
|
|
|
349
358
|
# iterate through source generator to fill signal track
|
|
@@ -366,6 +375,4 @@ class GenericSignalGenerator(SignalGenerator):
|
|
|
366
375
|
res = nums % stop # last part of unfinished loop
|
|
367
376
|
if res > 0:
|
|
368
377
|
track[stop * nloops :] = track[:res]
|
|
369
|
-
|
|
370
|
-
# The rms value is just an amplification here
|
|
371
|
-
return self.rms * track
|
|
378
|
+
return self.amplitude * track
|