acoular 24.10__py3-none-any.whl → 25.3__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 +5 -2
- acoular/aiaa/__init__.py +12 -0
- acoular/{tools → aiaa}/aiaa.py +23 -28
- acoular/base.py +75 -55
- acoular/calib.py +129 -34
- acoular/configuration.py +11 -9
- acoular/demo/__init__.py +1 -0
- acoular/demo/acoular_demo.py +31 -18
- acoular/deprecation.py +85 -0
- acoular/environments.py +481 -229
- acoular/fastFuncs.py +90 -84
- acoular/fbeamform.py +203 -411
- acoular/fprocess.py +233 -123
- acoular/grids.py +793 -424
- acoular/h5cache.py +29 -40
- acoular/h5files.py +2 -6
- acoular/microphones.py +197 -74
- acoular/process.py +660 -149
- acoular/sdinput.py +23 -20
- acoular/signals.py +461 -159
- acoular/sources.py +1311 -489
- acoular/spectra.py +328 -352
- acoular/tbeamform.py +79 -202
- acoular/tfastfuncs.py +21 -21
- acoular/tools/__init__.py +2 -8
- acoular/tools/helpers.py +216 -2
- acoular/tools/metrics.py +4 -4
- acoular/tools/utils.py +106 -200
- acoular/tprocess.py +348 -309
- acoular/traitsviews.py +10 -10
- acoular/trajectory.py +126 -53
- acoular/version.py +2 -2
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/METADATA +39 -17
- acoular-25.3.dist-info/RECORD +56 -0
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/WHEEL +1 -1
- acoular-24.10.dist-info/RECORD +0 -54
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/licenses/LICENSE +0 -0
acoular/tprocess.py
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
# imports from other packages
|
|
34
34
|
import wave
|
|
35
|
+
from abc import abstractmethod
|
|
35
36
|
from datetime import datetime, timezone
|
|
36
37
|
from os import path
|
|
37
38
|
from warnings import warn
|
|
@@ -70,7 +71,7 @@ from numpy import (
|
|
|
70
71
|
split,
|
|
71
72
|
sqrt,
|
|
72
73
|
stack,
|
|
73
|
-
sum,
|
|
74
|
+
sum, # noqa: A004
|
|
74
75
|
tile,
|
|
75
76
|
unique,
|
|
76
77
|
zeros,
|
|
@@ -83,19 +84,20 @@ from scipy.spatial import Delaunay
|
|
|
83
84
|
from traits.api import (
|
|
84
85
|
Bool,
|
|
85
86
|
CArray,
|
|
86
|
-
|
|
87
|
+
CInt,
|
|
87
88
|
Constant,
|
|
88
89
|
Delegate,
|
|
89
90
|
Dict,
|
|
91
|
+
Enum,
|
|
90
92
|
File,
|
|
91
93
|
Float,
|
|
92
94
|
Instance,
|
|
93
95
|
Int,
|
|
94
96
|
List,
|
|
95
|
-
|
|
97
|
+
Map,
|
|
96
98
|
Property,
|
|
97
99
|
Str,
|
|
98
|
-
|
|
100
|
+
Union,
|
|
99
101
|
cached_property,
|
|
100
102
|
observe,
|
|
101
103
|
on_trait_change,
|
|
@@ -104,12 +106,15 @@ from traits.api import (
|
|
|
104
106
|
# acoular imports
|
|
105
107
|
from .base import SamplesGenerator, TimeOut
|
|
106
108
|
from .configuration import config
|
|
109
|
+
from .deprecation import deprecated_alias
|
|
107
110
|
from .environments import cartToCyl, cylToCart
|
|
108
111
|
from .h5files import _get_h5file_class
|
|
109
112
|
from .internal import digest, ldigest
|
|
110
113
|
from .microphones import MicGeom
|
|
114
|
+
from .tools.utils import find_basename
|
|
111
115
|
|
|
112
116
|
|
|
117
|
+
@deprecated_alias({'numchannels_total': 'num_channels_total', 'numsamples_total': 'num_samples_total'})
|
|
113
118
|
class MaskedTimeOut(TimeOut):
|
|
114
119
|
"""Signal processing block for channel and sample selection.
|
|
115
120
|
|
|
@@ -120,35 +125,39 @@ class MaskedTimeOut(TimeOut):
|
|
|
120
125
|
and generates output via the generator :meth:`result`.
|
|
121
126
|
"""
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
124
129
|
source = Instance(SamplesGenerator)
|
|
125
130
|
|
|
126
|
-
|
|
127
|
-
start =
|
|
131
|
+
# Index of the first sample to be considered valid.
|
|
132
|
+
start = CInt(0, desc='start of valid samples')
|
|
128
133
|
|
|
129
|
-
|
|
130
|
-
stop =
|
|
134
|
+
# Index of the last sample to be considered valid.
|
|
135
|
+
stop = Union(None, CInt, desc='stop of valid samples')
|
|
131
136
|
|
|
132
|
-
|
|
133
|
-
invalid_channels =
|
|
137
|
+
# Channels that are to be treated as invalid.
|
|
138
|
+
invalid_channels = List(int, desc='list of invalid channels')
|
|
134
139
|
|
|
135
|
-
|
|
136
|
-
channels = Property(depends_on=['invalid_channels', 'source.
|
|
140
|
+
# Channel mask to serve as an index for all valid channels, is set automatically.
|
|
141
|
+
channels = Property(depends_on=['invalid_channels', 'source.num_channels'], desc='channel mask')
|
|
137
142
|
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
# Number of channels in input, as given by :attr:`~acoular.base.TimeOut.source`.
|
|
144
|
+
num_channels_total = Delegate('source', 'num_channels')
|
|
140
145
|
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
# Number of samples in input, as given by :attr:`~acoular.base.TimeOut.source`.
|
|
147
|
+
num_samples_total = Delegate('source', 'num_samples')
|
|
143
148
|
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
# Number of valid channels, is set automatically.
|
|
150
|
+
num_channels = Property(
|
|
151
|
+
depends_on=['invalid_channels', 'source.num_channels'], desc='number of valid input channels'
|
|
152
|
+
)
|
|
146
153
|
|
|
147
|
-
|
|
148
|
-
|
|
154
|
+
# Number of valid time samples, is set automatically.
|
|
155
|
+
num_samples = Property(
|
|
156
|
+
depends_on=['start', 'stop', 'source.num_samples'], desc='number of valid samples per channel'
|
|
157
|
+
)
|
|
149
158
|
|
|
150
|
-
|
|
151
|
-
basename = Property(depends_on='source.digest', desc='basename for cache file')
|
|
159
|
+
# Name of the cache file without extension, readonly.
|
|
160
|
+
basename = Property(depends_on=['source.digest'], desc='basename for cache file')
|
|
152
161
|
|
|
153
162
|
# internal identifier
|
|
154
163
|
digest = Property(depends_on=['source.digest', 'start', 'stop', 'invalid_channels'])
|
|
@@ -159,26 +168,32 @@ class MaskedTimeOut(TimeOut):
|
|
|
159
168
|
|
|
160
169
|
@cached_property
|
|
161
170
|
def _get_basename(self):
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
171
|
+
warn(
|
|
172
|
+
(
|
|
173
|
+
f'The basename attribute of a {self.__class__.__name__} object is deprecated'
|
|
174
|
+
' and will be removed in a future release!'
|
|
175
|
+
),
|
|
176
|
+
DeprecationWarning,
|
|
177
|
+
stacklevel=2,
|
|
178
|
+
)
|
|
179
|
+
return find_basename(self.source, alternative_basename=self.source.__class__.__name__ + self.source.digest)
|
|
165
180
|
|
|
166
181
|
@cached_property
|
|
167
182
|
def _get_channels(self):
|
|
168
183
|
if len(self.invalid_channels) == 0:
|
|
169
184
|
return slice(0, None, None)
|
|
170
|
-
allr = [i for i in range(self.
|
|
185
|
+
allr = [i for i in range(self.num_channels_total) if i not in self.invalid_channels]
|
|
171
186
|
return array(allr)
|
|
172
187
|
|
|
173
188
|
@cached_property
|
|
174
|
-
def
|
|
189
|
+
def _get_num_channels(self):
|
|
175
190
|
if len(self.invalid_channels) == 0:
|
|
176
|
-
return self.
|
|
191
|
+
return self.num_channels_total
|
|
177
192
|
return len(self.channels)
|
|
178
193
|
|
|
179
194
|
@cached_property
|
|
180
|
-
def
|
|
181
|
-
sli = slice(self.start, self.stop).indices(self.
|
|
195
|
+
def _get_num_samples(self):
|
|
196
|
+
sli = slice(self.start, self.stop).indices(self.num_samples_total)
|
|
182
197
|
return sli[1] - sli[0]
|
|
183
198
|
|
|
184
199
|
def result(self, num):
|
|
@@ -192,22 +207,22 @@ class MaskedTimeOut(TimeOut):
|
|
|
192
207
|
|
|
193
208
|
Returns
|
|
194
209
|
-------
|
|
195
|
-
Samples in blocks of shape (num, :attr:`
|
|
210
|
+
Samples in blocks of shape (num, :attr:`num_channels`).
|
|
196
211
|
The last block may be shorter than num.
|
|
197
212
|
|
|
198
213
|
"""
|
|
199
|
-
sli = slice(self.start, self.stop).indices(self.
|
|
214
|
+
sli = slice(self.start, self.stop).indices(self.num_samples_total)
|
|
200
215
|
start = sli[0]
|
|
201
216
|
stop = sli[1]
|
|
202
217
|
if start >= stop:
|
|
203
218
|
msg = 'no samples available'
|
|
204
219
|
raise OSError(msg)
|
|
205
220
|
|
|
206
|
-
if start != 0 or stop != self.
|
|
221
|
+
if start != 0 or stop != self.num_samples_total:
|
|
207
222
|
offset = -start % num
|
|
208
223
|
if offset == 0:
|
|
209
224
|
offset = num
|
|
210
|
-
buf = empty((num + offset, self.
|
|
225
|
+
buf = empty((num + offset, self.num_channels), dtype=float)
|
|
211
226
|
bsize = 0
|
|
212
227
|
i = 0
|
|
213
228
|
fblock = True
|
|
@@ -248,14 +263,14 @@ class ChannelMixer(TimeOut):
|
|
|
248
263
|
Outputs a single channel.
|
|
249
264
|
"""
|
|
250
265
|
|
|
251
|
-
|
|
266
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
252
267
|
source = Instance(SamplesGenerator)
|
|
253
268
|
|
|
254
|
-
|
|
269
|
+
# Amplitude weight(s) for the channels as array. If not set, all channels are equally weighted.
|
|
255
270
|
weights = CArray(desc='channel weights')
|
|
256
271
|
|
|
257
272
|
# Number of channels is always one here.
|
|
258
|
-
|
|
273
|
+
num_channels = Constant(1)
|
|
259
274
|
|
|
260
275
|
# internal identifier
|
|
261
276
|
digest = Property(depends_on=['source.digest', 'weights'])
|
|
@@ -280,10 +295,10 @@ class ChannelMixer(TimeOut):
|
|
|
280
295
|
|
|
281
296
|
"""
|
|
282
297
|
if self.weights.size:
|
|
283
|
-
if self.weights.shape in {(self.source.
|
|
298
|
+
if self.weights.shape in {(self.source.num_channels,), (1,)}:
|
|
284
299
|
weights = self.weights
|
|
285
300
|
else:
|
|
286
|
-
msg = f'Weight factors can not be broadcasted: {self.weights.shape}, {(self.source.
|
|
301
|
+
msg = f'Weight factors can not be broadcasted: {self.weights.shape}, {(self.source.num_channels,)}'
|
|
287
302
|
raise ValueError(msg)
|
|
288
303
|
else:
|
|
289
304
|
weights = 1
|
|
@@ -292,7 +307,7 @@ class ChannelMixer(TimeOut):
|
|
|
292
307
|
yield sum(weights * block, 1, keepdims=True)
|
|
293
308
|
|
|
294
309
|
|
|
295
|
-
class Trigger(TimeOut):
|
|
310
|
+
class Trigger(TimeOut): # pragma: no cover
|
|
296
311
|
"""Class for identifying trigger signals.
|
|
297
312
|
Gets samples from :attr:`source` and stores the trigger samples in :meth:`trigger_data`.
|
|
298
313
|
|
|
@@ -306,59 +321,56 @@ class Trigger(TimeOut):
|
|
|
306
321
|
vary too much.
|
|
307
322
|
"""
|
|
308
323
|
|
|
309
|
-
|
|
324
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
310
325
|
source = Instance(SamplesGenerator)
|
|
311
326
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
327
|
+
# Threshold of trigger. Has different meanings for different
|
|
328
|
+
# :attr:`~acoular.tprocess.Trigger.trigger_type`. The sign is relevant.
|
|
329
|
+
# If a sample of the signal is above/below the positive/negative threshold,
|
|
330
|
+
# it is assumed to be a peak.
|
|
331
|
+
# Default is None, in which case a first estimate is used: The threshold
|
|
332
|
+
# is assumed to be 75% of the max/min difference between all extremums and the
|
|
333
|
+
# mean value of the trigger signal. E.g: the mean value is 0 and there are positive
|
|
334
|
+
# extremums at 400 and negative extremums at -800. Then the estimated threshold would be
|
|
335
|
+
# 0.75 * -800 = -600.
|
|
321
336
|
threshold = Float(None)
|
|
322
337
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
338
|
+
# Maximum allowable variation of length of each revolution duration. Default is
|
|
339
|
+
# 2%. A warning is thrown, if any revolution length surpasses this value:
|
|
340
|
+
# abs(durationEachRev - meanDuration) > 0.02 * meanDuration
|
|
326
341
|
max_variation_of_duration = Float(0.02)
|
|
327
342
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
343
|
+
# Defines the length of hunks via lenHunk = hunk_length * maxOncePerRevDuration.
|
|
344
|
+
# If there are multiple peaks within lenHunk, then the algorithm will
|
|
345
|
+
# cancel all but one out (see :attr:`~acoular.tprocess.Trigger.multiple_peaks_in_hunk`).
|
|
346
|
+
# Default is to 0.1.
|
|
332
347
|
hunk_length = Float(0.1)
|
|
333
348
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
#: 2.: -maximum of number of samples between adjacent trigger samples
|
|
360
|
-
#:
|
|
361
|
-
#: 3.: -minimum of number of samples between adjacent trigger samples
|
|
349
|
+
# Type of trigger.
|
|
350
|
+
#
|
|
351
|
+
# 'dirac': a single pulse is assumed (sign of :attr:`~acoular.tprocess.Trigger.trigger_type` is
|
|
352
|
+
# important). Sample will trigger if its value is above/below the pos/neg threshold.
|
|
353
|
+
#
|
|
354
|
+
# 'rect' : repeating rectangular functions. Only every second edge is assumed to be a trigger.
|
|
355
|
+
# The sign of :attr:`~acoular.tprocess.Trigger.trigger_type` gives information on which edge
|
|
356
|
+
# should be used (+ for rising edge, - for falling edge). Sample will trigger if the difference
|
|
357
|
+
# between its value and its predecessors value is above/below the pos/neg threshold.
|
|
358
|
+
#
|
|
359
|
+
# Default is 'dirac'.
|
|
360
|
+
trigger_type = Enum('dirac', 'rect')
|
|
361
|
+
|
|
362
|
+
# Identifier which peak to consider, if there are multiple peaks in one hunk : (see
|
|
363
|
+
# :attr:`~acoular.tprocess.Trigger.hunk_length`). Default is to 'extremum', : in which case the
|
|
364
|
+
# extremal peak (maximum if threshold > 0, minimum if threshold < 0) is considered.
|
|
365
|
+
multiple_peaks_in_hunk = Enum('extremum', 'first')
|
|
366
|
+
|
|
367
|
+
# Tuple consisting of 3 entries:
|
|
368
|
+
#
|
|
369
|
+
# 1.: -Vector with the sample indices of the 1/Rev trigger samples
|
|
370
|
+
#
|
|
371
|
+
# 2.: -maximum of number of samples between adjacent trigger samples
|
|
372
|
+
#
|
|
373
|
+
# 3.: -minimum of number of samples between adjacent trigger samples
|
|
362
374
|
trigger_data = Property(
|
|
363
375
|
depends_on=[
|
|
364
376
|
'source.digest',
|
|
@@ -436,8 +448,8 @@ class Trigger(TimeOut):
|
|
|
436
448
|
faultyInd = flatnonzero(diffDist > self.max_variation_of_duration * meanDist)
|
|
437
449
|
if faultyInd.size != 0:
|
|
438
450
|
warn(
|
|
439
|
-
'In Trigger-Identification: The distances between the peaks (and
|
|
440
|
-
|
|
451
|
+
f'In Trigger-Identification: The distances between the peaks (and therefore the lengths of the \
|
|
452
|
+
revolutions) vary too much (check samples {peakLoc[faultyInd] + self.source.start}).',
|
|
441
453
|
Warning,
|
|
442
454
|
stacklevel=2,
|
|
443
455
|
)
|
|
@@ -450,7 +462,8 @@ class Trigger(TimeOut):
|
|
|
450
462
|
def _trigger_rect(self, x0, x, threshold):
|
|
451
463
|
# x0 stores the last value of the the last generator cycle
|
|
452
464
|
xNew = append(x0, x)
|
|
453
|
-
# indPeakHunk = abs(xNew[1:] - xNew[:-1]) > abs(threshold)
|
|
465
|
+
# indPeakHunk = abs(xNew[1:] - xNew[:-1]) > abs(threshold)
|
|
466
|
+
# with above line, every edge would be located
|
|
454
467
|
return self._trigger_value_comp(xNew[1:] - xNew[:-1], threshold)
|
|
455
468
|
|
|
456
469
|
def _trigger_value_comp(self, trigger_data, threshold):
|
|
@@ -474,27 +487,33 @@ class Trigger(TimeOut):
|
|
|
474
487
|
maxTriggerHelp = [minVal, maxVal] - meanVal
|
|
475
488
|
argInd = argmax(abs(maxTriggerHelp))
|
|
476
489
|
thresh = maxTriggerHelp[argInd] * 0.75 # 0.75 for 75% of max trigger signal
|
|
477
|
-
warn('No threshold was passed. An estimated threshold of
|
|
490
|
+
warn(f'No threshold was passed. An estimated threshold of {thresh} is assumed.', Warning, stacklevel=2)
|
|
478
491
|
else: # take user defined threshold
|
|
479
492
|
thresh = self.threshold
|
|
480
493
|
return thresh
|
|
481
494
|
|
|
482
495
|
def _check_trigger_existence(self):
|
|
483
|
-
nChannels = self.source.
|
|
484
|
-
if
|
|
485
|
-
|
|
496
|
+
nChannels = self.source.num_channels
|
|
497
|
+
if nChannels != 1:
|
|
498
|
+
msg = f'Trigger signal must consist of ONE channel, instead {nChannels} channels are given!'
|
|
499
|
+
raise Exception(msg)
|
|
486
500
|
return 0
|
|
487
501
|
|
|
502
|
+
def result(self, num):
|
|
503
|
+
msg = 'result method not implemented yet! Data from source will be passed without transformation.'
|
|
504
|
+
warn(msg, Warning, stacklevel=2)
|
|
505
|
+
yield from self.source.result(num)
|
|
506
|
+
|
|
488
507
|
|
|
489
508
|
class AngleTracker(MaskedTimeOut):
|
|
490
509
|
"""Calculates rotation angle and rpm per sample from a trigger signal
|
|
491
510
|
using spline interpolation in the time domain.
|
|
492
511
|
|
|
493
|
-
Gets samples from :attr:`trigger` and stores the angle and rpm samples in :meth:`angle` and
|
|
494
|
-
|
|
512
|
+
Gets samples from :attr:`trigger` and stores the angle and rpm samples in :meth:`angle` and
|
|
513
|
+
:meth:`rpm`.
|
|
495
514
|
"""
|
|
496
515
|
|
|
497
|
-
|
|
516
|
+
# Trigger data from :class:`acoular.tprocess.Trigger`.
|
|
498
517
|
trigger = Instance(Trigger)
|
|
499
518
|
|
|
500
519
|
# internal identifier
|
|
@@ -509,29 +528,29 @@ class AngleTracker(MaskedTimeOut):
|
|
|
509
528
|
],
|
|
510
529
|
)
|
|
511
530
|
|
|
512
|
-
|
|
513
|
-
|
|
531
|
+
# Trigger signals per revolution,
|
|
532
|
+
# defaults to 1.
|
|
514
533
|
trigger_per_revo = Int(1, desc='trigger signals per revolution')
|
|
515
534
|
|
|
516
|
-
|
|
517
|
-
|
|
535
|
+
# Flag to set counter-clockwise (1) or clockwise (-1) rotation,
|
|
536
|
+
# defaults to -1.
|
|
518
537
|
rot_direction = Int(-1, desc='mathematical direction of rotation')
|
|
519
538
|
|
|
520
|
-
|
|
521
|
-
|
|
539
|
+
# Points of interpolation used for spline,
|
|
540
|
+
# defaults to 4.
|
|
522
541
|
interp_points = Int(4, desc='Points of interpolation used for spline')
|
|
523
542
|
|
|
524
|
-
|
|
543
|
+
# rotation angle in radians for first trigger position
|
|
525
544
|
start_angle = Float(0, desc='rotation angle for trigger position')
|
|
526
545
|
|
|
527
|
-
|
|
528
|
-
rpm = Property(depends_on='digest', desc='revolutions per minute for each sample')
|
|
546
|
+
# revolutions per minute for each sample, read-only
|
|
547
|
+
rpm = Property(depends_on=['digest'], desc='revolutions per minute for each sample')
|
|
529
548
|
|
|
530
|
-
|
|
531
|
-
average_rpm = Property(depends_on='digest', desc='average revolutions per minute')
|
|
549
|
+
# average revolutions per minute, read-only
|
|
550
|
+
average_rpm = Property(depends_on=['digest'], desc='average revolutions per minute')
|
|
532
551
|
|
|
533
|
-
|
|
534
|
-
angle = Property(depends_on='digest', desc='rotation angle for each sample')
|
|
552
|
+
# rotation angle in radians for each sample, read-only
|
|
553
|
+
angle = Property(depends_on=['digest'], desc='rotation angle for each sample')
|
|
535
554
|
|
|
536
555
|
# Internal flag to determine whether rpm and angle calculation has been processed,
|
|
537
556
|
# prevents recalculation
|
|
@@ -562,17 +581,17 @@ class AngleTracker(MaskedTimeOut):
|
|
|
562
581
|
# init
|
|
563
582
|
ind = 0
|
|
564
583
|
# trigger data
|
|
565
|
-
peakloc, maxdist, mindist = self.trigger.trigger_data
|
|
584
|
+
peakloc, maxdist, mindist = self.trigger.trigger_data
|
|
566
585
|
TriggerPerRevo = self.trigger_per_revo
|
|
567
586
|
rotDirection = self.rot_direction
|
|
568
|
-
num = self.source.
|
|
587
|
+
num = self.source.num_samples
|
|
569
588
|
samplerate = self.source.sample_freq
|
|
570
589
|
self._rpm = zeros(num)
|
|
571
590
|
self._angle = zeros(num)
|
|
572
591
|
# number of spline points
|
|
573
592
|
InterpPoints = self.interp_points
|
|
574
593
|
|
|
575
|
-
# loop over
|
|
594
|
+
# loop over all timesamples
|
|
576
595
|
while ind < num:
|
|
577
596
|
# when starting spline forward
|
|
578
597
|
if ind < peakloc[InterpPoints]:
|
|
@@ -636,24 +655,24 @@ class AngleTracker(MaskedTimeOut):
|
|
|
636
655
|
|
|
637
656
|
"""
|
|
638
657
|
# trigger indices data
|
|
639
|
-
peakloc = self.trigger.trigger_data
|
|
658
|
+
peakloc = self.trigger.trigger_data[0]
|
|
640
659
|
# calculation of average rpm in 1/min
|
|
641
660
|
return (len(peakloc) - 1) / (peakloc[-1] - peakloc[0]) / self.trigger_per_revo * self.source.sample_freq * 60
|
|
642
661
|
|
|
643
662
|
|
|
644
|
-
class SpatialInterpolator(TimeOut):
|
|
663
|
+
class SpatialInterpolator(TimeOut): # pragma: no cover
|
|
645
664
|
"""Base class for spatial interpolation of microphone data.
|
|
646
665
|
Gets samples from :attr:`source` and generates output via the
|
|
647
666
|
generator :meth:`result`.
|
|
648
667
|
"""
|
|
649
668
|
|
|
650
|
-
|
|
669
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
651
670
|
source = Instance(SamplesGenerator)
|
|
652
671
|
|
|
653
|
-
|
|
672
|
+
# :class:`~acoular.microphones.MicGeom` object that provides the real microphone locations.
|
|
654
673
|
mics = Instance(MicGeom(), desc='microphone geometry')
|
|
655
674
|
|
|
656
|
-
|
|
675
|
+
# :class:`~acoular.microphones.MicGeom` object that provides the virtual microphone locations.
|
|
657
676
|
mics_virtual = Property(desc='microphone geometry')
|
|
658
677
|
|
|
659
678
|
_mics_virtual = Instance(MicGeom, desc='internal microphone geometry;internal usage, read only')
|
|
@@ -666,12 +685,12 @@ class SpatialInterpolator(TimeOut):
|
|
|
666
685
|
def _set_mics_virtual(self, mics_virtual):
|
|
667
686
|
self._mics_virtual = mics_virtual
|
|
668
687
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
method =
|
|
688
|
+
# Interpolation method in spatial domain, defaults to linear
|
|
689
|
+
# linear uses numpy linear interpolation
|
|
690
|
+
# spline uses scipy CloughTocher algorithm
|
|
691
|
+
# rbf is scipy radial basis function with multiquadric, cubic and sinc functions
|
|
692
|
+
# idw refers to the inverse distance weighting algorithm
|
|
693
|
+
method = Enum(
|
|
675
694
|
'linear',
|
|
676
695
|
'spline',
|
|
677
696
|
'rbf-multiquadric',
|
|
@@ -682,42 +701,41 @@ class SpatialInterpolator(TimeOut):
|
|
|
682
701
|
desc='method for interpolation used',
|
|
683
702
|
)
|
|
684
703
|
|
|
685
|
-
|
|
686
|
-
array_dimension =
|
|
704
|
+
# spatial dimensionality of the array geometry
|
|
705
|
+
array_dimension = Enum('1D', '2D', 'ring', '3D', 'custom', desc='spatial dimensionality of the array geometry')
|
|
687
706
|
|
|
688
|
-
|
|
707
|
+
# Sampling frequency of output signal, as given by :attr:`source`.
|
|
689
708
|
sample_freq = Delegate('source', 'sample_freq')
|
|
690
709
|
|
|
691
|
-
|
|
692
|
-
|
|
710
|
+
# Number of channels in output.
|
|
711
|
+
num_channels = Property()
|
|
693
712
|
|
|
694
|
-
|
|
695
|
-
|
|
713
|
+
# Number of samples in output, as given by :attr:`source`.
|
|
714
|
+
num_samples = Delegate('source', 'num_samples')
|
|
696
715
|
|
|
697
|
-
|
|
716
|
+
# Interpolate a point at the origin of the Array geometry
|
|
698
717
|
interp_at_zero = Bool(False)
|
|
699
718
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
719
|
+
# The rotation must be around the z-axis, which means from x to y axis.
|
|
720
|
+
# If the coordinates are not build like that, than this 3x3 orthogonal
|
|
721
|
+
# transformation matrix Q can be used to modify the coordinates.
|
|
722
|
+
# It is assumed that with the modified coordinates the rotation is around the z-axis.
|
|
723
|
+
# The transformation is done via [x,y,z]_mod = Q * [x,y,z]. (default is Identity).
|
|
705
724
|
Q = CArray(dtype=float64, shape=(3, 3), value=identity(3))
|
|
706
725
|
|
|
707
|
-
num_IDW =
|
|
726
|
+
num_IDW = Int(3, desc='number of neighboring microphones, DEFAULT=3') # noqa: N815
|
|
708
727
|
|
|
709
|
-
p_weight =
|
|
728
|
+
p_weight = Float(
|
|
710
729
|
2,
|
|
711
|
-
dtype=float,
|
|
712
730
|
desc='used in interpolation for virtual microphone, weighting power exponent for IDW',
|
|
713
731
|
)
|
|
714
732
|
|
|
715
|
-
|
|
733
|
+
# Stores the output of :meth:`_virtNewCoord_func`; Read-Only
|
|
716
734
|
_virtNewCoord_func = Property( # noqa: N815
|
|
717
735
|
depends_on=['mics.digest', 'mics_virtual.digest', 'method', 'array_dimension', 'interp_at_zero'],
|
|
718
736
|
)
|
|
719
737
|
|
|
720
|
-
|
|
738
|
+
# internal identifier
|
|
721
739
|
digest = Property(
|
|
722
740
|
depends_on=[
|
|
723
741
|
'mics.digest',
|
|
@@ -730,7 +748,7 @@ class SpatialInterpolator(TimeOut):
|
|
|
730
748
|
],
|
|
731
749
|
)
|
|
732
750
|
|
|
733
|
-
def
|
|
751
|
+
def _get_num_channels(self):
|
|
734
752
|
return self.mics_virtual.num_mics
|
|
735
753
|
|
|
736
754
|
@cached_property
|
|
@@ -762,26 +780,30 @@ class SpatialInterpolator(TimeOut):
|
|
|
762
780
|
Returns
|
|
763
781
|
-------
|
|
764
782
|
mesh : List[]
|
|
765
|
-
The items of these lists
|
|
783
|
+
The items of these lists depend on the reduced interpolation dimension of each subarray.
|
|
766
784
|
If the Array is 1D the list items are:
|
|
767
785
|
1. item : float64[nMicsInSpecificSubarray]
|
|
768
|
-
Ordered positions of the real mics on the new 1d axis,
|
|
786
|
+
Ordered positions of the real mics on the new 1d axis,
|
|
787
|
+
to be used as inputs for numpys interp.
|
|
769
788
|
2. item : int64[nMicsInArray]
|
|
770
|
-
Indices identifying how the measured pressures must be evaluated, s.t. the
|
|
771
|
-
correspond to their initial
|
|
789
|
+
Indices identifying how the measured pressures must be evaluated, s.t. the
|
|
790
|
+
entries of the previous item (see last line) correspond to their initial
|
|
791
|
+
pressure values.
|
|
772
792
|
If the Array is 2D or 3d the list items are:
|
|
773
793
|
1. item : Delaunay mesh object
|
|
774
|
-
|
|
794
|
+
Delaunay mesh (see scipy.spatial.Delaunay) for the specific Array
|
|
775
795
|
2. item : int64[nMicsInArray]
|
|
776
|
-
same as 1d case, BUT with the difference, that here the rotational
|
|
777
|
-
|
|
796
|
+
same as 1d case, BUT with the difference, that here the rotational periodicity
|
|
797
|
+
is handled, when constructing the mesh. Therefore, the mesh could have more
|
|
798
|
+
vertices than the actual Array mics.
|
|
778
799
|
|
|
779
800
|
virtNewCoord : float64[3, nVirtualMics]
|
|
780
|
-
Projection of each virtual mic onto its new coordinates. The columns of virtNewCoord
|
|
801
|
+
Projection of each virtual mic onto its new coordinates. The columns of virtNewCoord
|
|
802
|
+
correspond to [phi, rho, z].
|
|
781
803
|
|
|
782
804
|
newCoord : float64[3, nMics]
|
|
783
|
-
Projection of each mic onto its new coordinates. The columns of newCoordinates
|
|
784
|
-
|
|
805
|
+
Projection of each mic onto its new coordinates. The columns of newCoordinates
|
|
806
|
+
correspond to [phi, rho, z].
|
|
785
807
|
"""
|
|
786
808
|
# init positions of virtual mics in cyl coordinates
|
|
787
809
|
nVirtMics = mpos_virt.shape[1]
|
|
@@ -942,7 +964,7 @@ class SpatialInterpolator(TimeOut):
|
|
|
942
964
|
# if no rotation given
|
|
943
965
|
else:
|
|
944
966
|
xInterp = tile(virtNewCoord[0, :], (nTime, 1))
|
|
945
|
-
# get ordered microphone
|
|
967
|
+
# get ordered microphone positions in radiant
|
|
946
968
|
x = newCoord[0]
|
|
947
969
|
for cntTime in range(nTime):
|
|
948
970
|
if self.method == 'linear':
|
|
@@ -1054,7 +1076,7 @@ class SpatialInterpolator(TimeOut):
|
|
|
1054
1076
|
# using inverse distance weighting
|
|
1055
1077
|
elif self.method == 'IDW':
|
|
1056
1078
|
newPoint2_M = newPoint.T
|
|
1057
|
-
newPoint3_M = append(newPoint2_M, zeros([1, self.
|
|
1079
|
+
newPoint3_M = append(newPoint2_M, zeros([1, self.num_channels]), axis=0)
|
|
1058
1080
|
newPointCart = cylToCart(newPoint3_M)
|
|
1059
1081
|
for ind in arange(len(newPoint[:, 0])):
|
|
1060
1082
|
newPoint_Rep = tile(newPointCart[:, ind], (len(newPoint[:, 0]), 1)).T
|
|
@@ -1127,17 +1149,22 @@ class SpatialInterpolator(TimeOut):
|
|
|
1127
1149
|
# return interpolated pressure values
|
|
1128
1150
|
return pInterp
|
|
1129
1151
|
|
|
1152
|
+
def result(self, num):
|
|
1153
|
+
msg = 'result method not implemented yet! Data from source will be passed without transformation.'
|
|
1154
|
+
warn(msg, Warning, stacklevel=2)
|
|
1155
|
+
yield from self.source.result(num)
|
|
1130
1156
|
|
|
1131
|
-
|
|
1157
|
+
|
|
1158
|
+
class SpatialInterpolatorRotation(SpatialInterpolator): # pragma: no cover
|
|
1132
1159
|
"""Spatial Interpolation for rotating sources. Gets samples from :attr:`source`
|
|
1133
1160
|
and angles from :attr:`AngleTracker`.Generates output via the generator :meth:`result`.
|
|
1134
1161
|
|
|
1135
1162
|
"""
|
|
1136
1163
|
|
|
1137
|
-
|
|
1164
|
+
# Angle data from AngleTracker class
|
|
1138
1165
|
angle_source = Instance(AngleTracker)
|
|
1139
1166
|
|
|
1140
|
-
|
|
1167
|
+
# Internal identifier
|
|
1141
1168
|
digest = Property(
|
|
1142
1169
|
depends_on=[
|
|
1143
1170
|
'source.digest',
|
|
@@ -1166,7 +1193,7 @@ class SpatialInterpolatorRotation(SpatialInterpolator):
|
|
|
1166
1193
|
|
|
1167
1194
|
Returns
|
|
1168
1195
|
-------
|
|
1169
|
-
Samples in blocks of shape (num, :attr:`
|
|
1196
|
+
Samples in blocks of shape (num, :attr:`num_channels`).
|
|
1170
1197
|
The last block may be shorter than num.
|
|
1171
1198
|
|
|
1172
1199
|
"""
|
|
@@ -1183,14 +1210,14 @@ class SpatialInterpolatorRotation(SpatialInterpolator):
|
|
|
1183
1210
|
count += num
|
|
1184
1211
|
|
|
1185
1212
|
|
|
1186
|
-
class SpatialInterpolatorConstantRotation(SpatialInterpolator):
|
|
1213
|
+
class SpatialInterpolatorConstantRotation(SpatialInterpolator): # pragma: no cover
|
|
1187
1214
|
"""Spatial linear Interpolation for constantly rotating sources.
|
|
1188
1215
|
Gets samples from :attr:`source` and generates output via the
|
|
1189
1216
|
generator :meth:`result`.
|
|
1190
1217
|
"""
|
|
1191
1218
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1219
|
+
# Rotational speed in rps. Positive, if rotation is around positive z-axis sense,
|
|
1220
|
+
# which means from x to y axis.
|
|
1194
1221
|
rotational_speed = Float(0.0)
|
|
1195
1222
|
|
|
1196
1223
|
# internal identifier
|
|
@@ -1222,7 +1249,7 @@ class SpatialInterpolatorConstantRotation(SpatialInterpolator):
|
|
|
1222
1249
|
|
|
1223
1250
|
Returns
|
|
1224
1251
|
-------
|
|
1225
|
-
Samples in blocks of shape (num, :attr:`
|
|
1252
|
+
Samples in blocks of shape (num, :attr:`num_channels`).
|
|
1226
1253
|
The last block may be shorter than num.
|
|
1227
1254
|
|
|
1228
1255
|
"""
|
|
@@ -1240,21 +1267,21 @@ class SpatialInterpolatorConstantRotation(SpatialInterpolator):
|
|
|
1240
1267
|
class Mixer(TimeOut):
|
|
1241
1268
|
"""Mixes the signals from several sources."""
|
|
1242
1269
|
|
|
1243
|
-
|
|
1244
|
-
source =
|
|
1270
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` object.
|
|
1271
|
+
source = Instance(SamplesGenerator)
|
|
1245
1272
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1273
|
+
# List of additional :class:`~acoular.base.SamplesGenerator` objects
|
|
1274
|
+
# to be mixed.
|
|
1248
1275
|
sources = List(Instance(SamplesGenerator, ()))
|
|
1249
1276
|
|
|
1250
|
-
|
|
1277
|
+
# Sampling frequency of the signal as given by :attr:`source`.
|
|
1251
1278
|
sample_freq = Delegate('source')
|
|
1252
1279
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1280
|
+
# Number of channels in output as given by :attr:`source`.
|
|
1281
|
+
num_channels = Delegate('source')
|
|
1255
1282
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1283
|
+
# Number of samples in output as given by :attr:`source`.
|
|
1284
|
+
num_samples = Delegate('source')
|
|
1258
1285
|
|
|
1259
1286
|
# internal identifier
|
|
1260
1287
|
sdigest = Str()
|
|
@@ -1275,9 +1302,11 @@ class Mixer(TimeOut):
|
|
|
1275
1302
|
if self.source:
|
|
1276
1303
|
for s in self.sources:
|
|
1277
1304
|
if self.sample_freq != s.sample_freq:
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1305
|
+
msg = f'Sample frequency of {s} does not fit'
|
|
1306
|
+
raise ValueError(msg)
|
|
1307
|
+
if self.num_channels != s.num_channels:
|
|
1308
|
+
msg = f'Channel count of {s} does not fit'
|
|
1309
|
+
raise ValueError(msg)
|
|
1281
1310
|
|
|
1282
1311
|
def result(self, num):
|
|
1283
1312
|
"""Python generator that yields the output block-wise.
|
|
@@ -1292,7 +1321,7 @@ class Mixer(TimeOut):
|
|
|
1292
1321
|
|
|
1293
1322
|
Returns
|
|
1294
1323
|
-------
|
|
1295
|
-
Samples in blocks of shape (num,
|
|
1324
|
+
Samples in blocks of shape (num, num_channels).
|
|
1296
1325
|
The last block may be shorter than num.
|
|
1297
1326
|
|
|
1298
1327
|
"""
|
|
@@ -1318,7 +1347,7 @@ class Mixer(TimeOut):
|
|
|
1318
1347
|
class TimePower(TimeOut):
|
|
1319
1348
|
"""Calculates time-depended power of the signal."""
|
|
1320
1349
|
|
|
1321
|
-
|
|
1350
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
1322
1351
|
source = Instance(SamplesGenerator)
|
|
1323
1352
|
|
|
1324
1353
|
def result(self, num):
|
|
@@ -1333,7 +1362,7 @@ class TimePower(TimeOut):
|
|
|
1333
1362
|
Returns
|
|
1334
1363
|
-------
|
|
1335
1364
|
Squared output of source.
|
|
1336
|
-
Yields samples in blocks of shape (num,
|
|
1365
|
+
Yields samples in blocks of shape (num, num_channels).
|
|
1337
1366
|
The last block may be shorter than num.
|
|
1338
1367
|
|
|
1339
1368
|
"""
|
|
@@ -1344,7 +1373,7 @@ class TimePower(TimeOut):
|
|
|
1344
1373
|
class TimeCumAverage(TimeOut):
|
|
1345
1374
|
"""Calculates cumulative average of the signal, useful for Leq."""
|
|
1346
1375
|
|
|
1347
|
-
|
|
1376
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
1348
1377
|
source = Instance(SamplesGenerator)
|
|
1349
1378
|
|
|
1350
1379
|
def result(self, num):
|
|
@@ -1359,7 +1388,7 @@ class TimeCumAverage(TimeOut):
|
|
|
1359
1388
|
Returns
|
|
1360
1389
|
-------
|
|
1361
1390
|
Cumulative average of the output of source.
|
|
1362
|
-
Yields samples in blocks of shape (num,
|
|
1391
|
+
Yields samples in blocks of shape (num, num_channels).
|
|
1363
1392
|
The last block may be shorter than num.
|
|
1364
1393
|
|
|
1365
1394
|
"""
|
|
@@ -1377,7 +1406,7 @@ class TimeCumAverage(TimeOut):
|
|
|
1377
1406
|
class TimeReverse(TimeOut):
|
|
1378
1407
|
"""Calculates the time-reversed signal of a source."""
|
|
1379
1408
|
|
|
1380
|
-
|
|
1409
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
1381
1410
|
source = Instance(SamplesGenerator)
|
|
1382
1411
|
|
|
1383
1412
|
def result(self, num):
|
|
@@ -1391,7 +1420,7 @@ class TimeReverse(TimeOut):
|
|
|
1391
1420
|
|
|
1392
1421
|
Returns
|
|
1393
1422
|
-------
|
|
1394
|
-
Yields samples in blocks of shape (num,
|
|
1423
|
+
Yields samples in blocks of shape (num, num_channels).
|
|
1395
1424
|
Time-reversed output of source.
|
|
1396
1425
|
The last block may be shorter than num.
|
|
1397
1426
|
|
|
@@ -1414,13 +1443,13 @@ class Filter(TimeOut):
|
|
|
1414
1443
|
implements a filter with coefficients that may be changed
|
|
1415
1444
|
during processing.
|
|
1416
1445
|
|
|
1417
|
-
Should not be
|
|
1446
|
+
Should not be instantiated by itself.
|
|
1418
1447
|
"""
|
|
1419
1448
|
|
|
1420
|
-
|
|
1449
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
1421
1450
|
source = Instance(SamplesGenerator)
|
|
1422
1451
|
|
|
1423
|
-
|
|
1452
|
+
# Filter coefficients
|
|
1424
1453
|
sos = Property()
|
|
1425
1454
|
|
|
1426
1455
|
def _get_sos(self):
|
|
@@ -1437,13 +1466,13 @@ class Filter(TimeOut):
|
|
|
1437
1466
|
|
|
1438
1467
|
Returns
|
|
1439
1468
|
-------
|
|
1440
|
-
Samples in blocks of shape (num,
|
|
1469
|
+
Samples in blocks of shape (num, num_channels).
|
|
1441
1470
|
Delivers the bandpass filtered output of source.
|
|
1442
1471
|
The last block may be shorter than num.
|
|
1443
1472
|
|
|
1444
1473
|
"""
|
|
1445
1474
|
sos = self.sos
|
|
1446
|
-
zi = zeros((sos.shape[0], 2, self.source.
|
|
1475
|
+
zi = zeros((sos.shape[0], 2, self.source.num_channels))
|
|
1447
1476
|
for block in self.source.result(num):
|
|
1448
1477
|
sos = self.sos # this line is useful in case of changes
|
|
1449
1478
|
# to self.sos during generator lifetime
|
|
@@ -1454,19 +1483,19 @@ class Filter(TimeOut):
|
|
|
1454
1483
|
class FiltOctave(Filter):
|
|
1455
1484
|
"""Octave or third-octave filter (causal, non-zero phase delay)."""
|
|
1456
1485
|
|
|
1457
|
-
|
|
1486
|
+
# Band center frequency; defaults to 1000.
|
|
1458
1487
|
band = Float(1000.0, desc='band center frequency')
|
|
1459
1488
|
|
|
1460
|
-
|
|
1461
|
-
fraction =
|
|
1489
|
+
# Octave fraction: 'Octave' or 'Third octave'; defaults to 'Octave'.
|
|
1490
|
+
fraction = Map({'Octave': 1, 'Third octave': 3}, default_value='Octave', desc='fraction of octave')
|
|
1462
1491
|
|
|
1463
|
-
|
|
1492
|
+
# Filter order
|
|
1464
1493
|
order = Int(3, desc='IIR filter order')
|
|
1465
1494
|
|
|
1466
1495
|
sos = Property(depends_on=['band', 'fraction', 'source.digest', 'order'])
|
|
1467
1496
|
|
|
1468
1497
|
# internal identifier
|
|
1469
|
-
digest = Property(depends_on=['source.digest', '
|
|
1498
|
+
digest = Property(depends_on=['source.digest', 'band', 'fraction', 'order'])
|
|
1470
1499
|
|
|
1471
1500
|
@cached_property
|
|
1472
1501
|
def _get_digest(self):
|
|
@@ -1499,11 +1528,11 @@ class FiltFiltOctave(FiltOctave):
|
|
|
1499
1528
|
It requires large amounts of memory!
|
|
1500
1529
|
"""
|
|
1501
1530
|
|
|
1502
|
-
|
|
1531
|
+
# Filter order (applied for forward filter and backward filter)
|
|
1503
1532
|
order = Int(2, desc='IIR filter half order')
|
|
1504
1533
|
|
|
1505
1534
|
# internal identifier
|
|
1506
|
-
digest = Property(depends_on=['source.digest', '
|
|
1535
|
+
digest = Property(depends_on=['source.digest', 'band', 'fraction', 'order'])
|
|
1507
1536
|
|
|
1508
1537
|
@cached_property
|
|
1509
1538
|
def _get_digest(self):
|
|
@@ -1541,20 +1570,20 @@ class FiltFiltOctave(FiltOctave):
|
|
|
1541
1570
|
|
|
1542
1571
|
Returns
|
|
1543
1572
|
-------
|
|
1544
|
-
Samples in blocks of shape (num,
|
|
1573
|
+
Samples in blocks of shape (num, num_channels).
|
|
1545
1574
|
Delivers the zero-phase bandpass filtered output of source.
|
|
1546
1575
|
The last block may be shorter than num.
|
|
1547
1576
|
|
|
1548
1577
|
"""
|
|
1549
1578
|
sos = self.sos
|
|
1550
|
-
data = empty((self.source.
|
|
1579
|
+
data = empty((self.source.num_samples, self.source.num_channels))
|
|
1551
1580
|
j = 0
|
|
1552
1581
|
for block in self.source.result(num):
|
|
1553
1582
|
ns, nc = block.shape
|
|
1554
1583
|
data[j : j + ns] = block
|
|
1555
1584
|
j += ns
|
|
1556
1585
|
# filter one channel at a time to save memory
|
|
1557
|
-
for j in range(self.source.
|
|
1586
|
+
for j in range(self.source.num_channels):
|
|
1558
1587
|
data[:, j] = sosfiltfilt(sos, data[:, j])
|
|
1559
1588
|
j = 0
|
|
1560
1589
|
ns = data.shape[0]
|
|
@@ -1569,13 +1598,13 @@ class TimeExpAverage(Filter):
|
|
|
1569
1598
|
I (non-standard) -> 35 ms.
|
|
1570
1599
|
"""
|
|
1571
1600
|
|
|
1572
|
-
|
|
1573
|
-
weight =
|
|
1601
|
+
# time weighting
|
|
1602
|
+
weight = Map({'F': 0.125, 'S': 1.0, 'I': 0.035}, default_value='F', desc='time weighting')
|
|
1574
1603
|
|
|
1575
1604
|
sos = Property(depends_on=['weight', 'source.digest'])
|
|
1576
1605
|
|
|
1577
1606
|
# internal identifier
|
|
1578
|
-
digest = Property(depends_on=['source.digest', '
|
|
1607
|
+
digest = Property(depends_on=['source.digest', 'weight'])
|
|
1579
1608
|
|
|
1580
1609
|
@cached_property
|
|
1581
1610
|
def _get_digest(self):
|
|
@@ -1590,15 +1619,15 @@ class TimeExpAverage(Filter):
|
|
|
1590
1619
|
|
|
1591
1620
|
|
|
1592
1621
|
class FiltFreqWeight(Filter):
|
|
1593
|
-
"""Frequency weighting filter
|
|
1622
|
+
"""Frequency weighting filter according to IEC 61672."""
|
|
1594
1623
|
|
|
1595
|
-
|
|
1596
|
-
weight =
|
|
1624
|
+
# weighting characteristics
|
|
1625
|
+
weight = Enum('A', 'C', 'Z', desc='frequency weighting')
|
|
1597
1626
|
|
|
1598
1627
|
sos = Property(depends_on=['weight', 'source.digest'])
|
|
1599
1628
|
|
|
1600
1629
|
# internal identifier
|
|
1601
|
-
digest = Property(depends_on=['source.digest', '
|
|
1630
|
+
digest = Property(depends_on=['source.digest', 'weight'])
|
|
1602
1631
|
|
|
1603
1632
|
@cached_property
|
|
1604
1633
|
def _get_digest(self):
|
|
@@ -1628,39 +1657,43 @@ class FiltFreqWeight(Filter):
|
|
|
1628
1657
|
return tf2sos(b, a)
|
|
1629
1658
|
|
|
1630
1659
|
|
|
1660
|
+
@deprecated_alias({'numbands': 'num_bands'}, read_only=True)
|
|
1631
1661
|
class FilterBank(TimeOut):
|
|
1632
1662
|
"""Abstract base class for IIR filter banks based on scipy lfilter
|
|
1633
1663
|
implements a bank of parallel filters.
|
|
1634
1664
|
|
|
1635
|
-
Should not be
|
|
1665
|
+
Should not be instantiated by itself.
|
|
1636
1666
|
"""
|
|
1637
1667
|
|
|
1638
|
-
|
|
1668
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
1639
1669
|
source = Instance(SamplesGenerator)
|
|
1640
1670
|
|
|
1641
|
-
|
|
1671
|
+
# List of filter coefficients for all filters
|
|
1642
1672
|
sos = Property()
|
|
1643
1673
|
|
|
1644
|
-
|
|
1674
|
+
# List of labels for bands
|
|
1645
1675
|
bands = Property()
|
|
1646
1676
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1677
|
+
# Number of bands
|
|
1678
|
+
num_bands = Property()
|
|
1649
1679
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1680
|
+
# Number of bands
|
|
1681
|
+
num_channels = Property()
|
|
1652
1682
|
|
|
1683
|
+
@abstractmethod
|
|
1653
1684
|
def _get_sos(self):
|
|
1654
|
-
|
|
1685
|
+
"""Returns a list of second order section coefficients."""
|
|
1655
1686
|
|
|
1687
|
+
@abstractmethod
|
|
1656
1688
|
def _get_bands(self):
|
|
1657
|
-
|
|
1689
|
+
"""Returns a list of labels for the bands."""
|
|
1658
1690
|
|
|
1659
|
-
|
|
1660
|
-
|
|
1691
|
+
@abstractmethod
|
|
1692
|
+
def _get_num_bands(self):
|
|
1693
|
+
"""Returns the number of bands."""
|
|
1661
1694
|
|
|
1662
|
-
def
|
|
1663
|
-
return self.
|
|
1695
|
+
def _get_num_channels(self):
|
|
1696
|
+
return self.num_bands * self.source.num_channels
|
|
1664
1697
|
|
|
1665
1698
|
def result(self, num):
|
|
1666
1699
|
"""Python generator that yields the output block-wise.
|
|
@@ -1673,16 +1706,16 @@ class FilterBank(TimeOut):
|
|
|
1673
1706
|
|
|
1674
1707
|
Returns
|
|
1675
1708
|
-------
|
|
1676
|
-
Samples in blocks of shape (num,
|
|
1709
|
+
Samples in blocks of shape (num, num_channels).
|
|
1677
1710
|
Delivers the bandpass filtered output of source.
|
|
1678
1711
|
The last block may be shorter than num.
|
|
1679
1712
|
|
|
1680
1713
|
"""
|
|
1681
|
-
numbands = self.
|
|
1682
|
-
snumch = self.source.
|
|
1714
|
+
numbands = self.num_bands
|
|
1715
|
+
snumch = self.source.num_channels
|
|
1683
1716
|
sos = self.sos
|
|
1684
1717
|
zi = [zeros((sos[0].shape[0], 2, snumch)) for _ in range(numbands)]
|
|
1685
|
-
res = zeros((num, self.
|
|
1718
|
+
res = zeros((num, self.num_channels), dtype='float')
|
|
1686
1719
|
for block in self.source.result(num):
|
|
1687
1720
|
for i in range(numbands):
|
|
1688
1721
|
res[:, i * snumch : (i + 1) * snumch], zi[i] = sosfilt(sos[i], block, axis=0, zi=zi[i])
|
|
@@ -1692,26 +1725,26 @@ class FilterBank(TimeOut):
|
|
|
1692
1725
|
class OctaveFilterBank(FilterBank):
|
|
1693
1726
|
"""Octave or third-octave filter bank."""
|
|
1694
1727
|
|
|
1695
|
-
|
|
1728
|
+
# Lowest band center frequency index; defaults to 21 (=125 Hz).
|
|
1696
1729
|
lband = Int(21, desc='lowest band center frequency index')
|
|
1697
1730
|
|
|
1698
|
-
|
|
1731
|
+
# Lowest band center frequency index + 1; defaults to 40 (=8000 Hz).
|
|
1699
1732
|
hband = Int(40, desc='lowest band center frequency index')
|
|
1700
1733
|
|
|
1701
|
-
|
|
1702
|
-
fraction =
|
|
1734
|
+
# Octave fraction: 'Octave' or 'Third octave'; defaults to 'Octave'.
|
|
1735
|
+
fraction = Map({'Octave': 1, 'Third octave': 3}, default_value='Octave', desc='fraction of octave')
|
|
1703
1736
|
|
|
1704
|
-
|
|
1737
|
+
# List of filter coefficients for all filters
|
|
1705
1738
|
ba = Property(depends_on=['lband', 'hband', 'fraction', 'source.digest'])
|
|
1706
1739
|
|
|
1707
|
-
|
|
1740
|
+
# List of labels for bands
|
|
1708
1741
|
bands = Property(depends_on=['lband', 'hband', 'fraction'])
|
|
1709
1742
|
|
|
1710
|
-
|
|
1711
|
-
|
|
1743
|
+
# Number of bands
|
|
1744
|
+
num_bands = Property(depends_on=['lband', 'hband', 'fraction'])
|
|
1712
1745
|
|
|
1713
1746
|
# internal identifier
|
|
1714
|
-
digest = Property(depends_on=['source.digest', '
|
|
1747
|
+
digest = Property(depends_on=['source.digest', 'lband', 'hband', 'fraction', 'order'])
|
|
1715
1748
|
|
|
1716
1749
|
@cached_property
|
|
1717
1750
|
def _get_digest(self):
|
|
@@ -1722,7 +1755,7 @@ class OctaveFilterBank(FilterBank):
|
|
|
1722
1755
|
return [10 ** (i / 10) for i in range(self.lband, self.hband, 4 - self.fraction_)]
|
|
1723
1756
|
|
|
1724
1757
|
@cached_property
|
|
1725
|
-
def
|
|
1758
|
+
def _get_num_bands(self):
|
|
1726
1759
|
return len(self.bands)
|
|
1727
1760
|
|
|
1728
1761
|
@cached_property
|
|
@@ -1736,26 +1769,27 @@ class OctaveFilterBank(FilterBank):
|
|
|
1736
1769
|
return sos
|
|
1737
1770
|
|
|
1738
1771
|
|
|
1772
|
+
@deprecated_alias({'name': 'file'})
|
|
1739
1773
|
class WriteWAV(TimeOut):
|
|
1740
1774
|
"""Saves time signal from one or more channels as mono/stereo/multi-channel
|
|
1741
1775
|
`*.wav` file.
|
|
1742
1776
|
"""
|
|
1743
1777
|
|
|
1744
|
-
|
|
1778
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
1745
1779
|
source = Instance(SamplesGenerator)
|
|
1746
1780
|
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1781
|
+
# Name of the file to be saved. If none is given, the name will be
|
|
1782
|
+
# automatically generated from the sources.
|
|
1783
|
+
file = File(filter=['*.wav'], desc='name of wave file')
|
|
1750
1784
|
|
|
1751
|
-
|
|
1752
|
-
basename = Property(depends_on='digest')
|
|
1785
|
+
# Basename for cache, readonly.
|
|
1786
|
+
basename = Property(depends_on=['digest'])
|
|
1753
1787
|
|
|
1754
|
-
|
|
1755
|
-
channels =
|
|
1788
|
+
# Channel(s) to save. List can only contain one or two channels.
|
|
1789
|
+
channels = List(int, desc='channel to save')
|
|
1756
1790
|
|
|
1757
1791
|
# internal identifier
|
|
1758
|
-
digest = Property(depends_on=['source.digest', 'channels'
|
|
1792
|
+
digest = Property(depends_on=['source.digest', 'channels'])
|
|
1759
1793
|
|
|
1760
1794
|
@cached_property
|
|
1761
1795
|
def _get_digest(self):
|
|
@@ -1763,18 +1797,15 @@ class WriteWAV(TimeOut):
|
|
|
1763
1797
|
|
|
1764
1798
|
@cached_property
|
|
1765
1799
|
def _get_basename(self):
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
except AttributeError:
|
|
1776
|
-
basename = 'void' # if no file source is found
|
|
1777
|
-
return basename
|
|
1800
|
+
warn(
|
|
1801
|
+
(
|
|
1802
|
+
f'The basename attribute of a {self.__class__.__name__} object is deprecated'
|
|
1803
|
+
' and will be removed in a future release!'
|
|
1804
|
+
),
|
|
1805
|
+
DeprecationWarning,
|
|
1806
|
+
stacklevel=2,
|
|
1807
|
+
)
|
|
1808
|
+
return find_basename(self.source)
|
|
1778
1809
|
|
|
1779
1810
|
def save(self):
|
|
1780
1811
|
"""Saves source output to one- or multiple-channel `*.wav` file."""
|
|
@@ -1783,54 +1814,59 @@ class WriteWAV(TimeOut):
|
|
|
1783
1814
|
msg = 'No channels given for output.'
|
|
1784
1815
|
raise ValueError(msg)
|
|
1785
1816
|
if nc > 2:
|
|
1786
|
-
warn('More than two channels given for output, exported file will have
|
|
1787
|
-
if self.
|
|
1817
|
+
warn(f'More than two channels given for output, exported file will have {nc:d} channels', stacklevel=1)
|
|
1818
|
+
if self.file == '':
|
|
1788
1819
|
name = self.basename
|
|
1789
1820
|
for nr in self.channels:
|
|
1790
|
-
name += '
|
|
1821
|
+
name += f'{nr:d}'
|
|
1791
1822
|
name += '.wav'
|
|
1792
1823
|
else:
|
|
1793
|
-
name = self.
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1824
|
+
name = self.file
|
|
1825
|
+
with wave.open(name, 'w') as wf:
|
|
1826
|
+
wf.setnchannels(nc)
|
|
1827
|
+
wf.setsampwidth(2)
|
|
1828
|
+
wf.setframerate(self.source.sample_freq)
|
|
1829
|
+
wf.setnframes(self.source.num_samples)
|
|
1830
|
+
mx = 0.0
|
|
1831
|
+
ind = array(self.channels)
|
|
1832
|
+
for data in self.source.result(1024):
|
|
1833
|
+
mx = max(abs(data[:, ind]).max(), mx)
|
|
1834
|
+
scale = 0.9 * 2**15 / mx
|
|
1835
|
+
for data in self.source.result(1024):
|
|
1836
|
+
wf.writeframesraw(array(data[:, ind] * scale, dtype=int16).tostring())
|
|
1837
|
+
|
|
1838
|
+
def result(self, num):
|
|
1839
|
+
msg = 'result method not implemented yet! Data from source will be passed without transformation.'
|
|
1840
|
+
warn(msg, Warning, stacklevel=2)
|
|
1841
|
+
yield from self.source.result(num)
|
|
1807
1842
|
|
|
1808
1843
|
|
|
1844
|
+
@deprecated_alias({'name': 'file', 'numsamples_write': 'num_samples_write', 'writeflag': 'write_flag'})
|
|
1809
1845
|
class WriteH5(TimeOut):
|
|
1810
1846
|
"""Saves time signal as `*.h5` file."""
|
|
1811
1847
|
|
|
1812
|
-
|
|
1848
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
1813
1849
|
source = Instance(SamplesGenerator)
|
|
1814
1850
|
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1851
|
+
# Name of the file to be saved. If none is given, the name will be
|
|
1852
|
+
# automatically generated from a time stamp.
|
|
1853
|
+
file = File(filter=['*.h5'], desc='name of data file')
|
|
1818
1854
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1855
|
+
# Number of samples to write to file by `result` method.
|
|
1856
|
+
# defaults to -1 (write as long as source yields data).
|
|
1857
|
+
num_samples_write = Int(-1)
|
|
1822
1858
|
|
|
1823
1859
|
# flag that can be raised to stop file writing
|
|
1824
|
-
|
|
1860
|
+
write_flag = Bool(True)
|
|
1825
1861
|
|
|
1826
1862
|
# internal identifier
|
|
1827
|
-
digest = Property(depends_on=['source.digest'
|
|
1863
|
+
digest = Property(depends_on=['source.digest'])
|
|
1828
1864
|
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
precision =
|
|
1865
|
+
# The floating-number-precision of entries of H5 File corresponding
|
|
1866
|
+
# to numpy dtypes. Default is 32 bit.
|
|
1867
|
+
precision = Enum('float32', 'float64', desc='precision of H5 File')
|
|
1832
1868
|
|
|
1833
|
-
|
|
1869
|
+
# Metadata to be stored in HDF5 file object
|
|
1834
1870
|
metadata = Dict(desc='metadata to be stored in .h5 file')
|
|
1835
1871
|
|
|
1836
1872
|
@cached_property
|
|
@@ -1838,15 +1874,15 @@ class WriteH5(TimeOut):
|
|
|
1838
1874
|
return digest(self)
|
|
1839
1875
|
|
|
1840
1876
|
def create_filename(self):
|
|
1841
|
-
if self.
|
|
1877
|
+
if self.file == '':
|
|
1842
1878
|
name = datetime.now(tz=timezone.utc).isoformat('_').replace(':', '-').replace('.', '_')
|
|
1843
|
-
self.
|
|
1879
|
+
self.file = path.join(config.td_dir, name + '.h5')
|
|
1844
1880
|
|
|
1845
1881
|
def get_initialized_file(self):
|
|
1846
1882
|
file = _get_h5file_class()
|
|
1847
1883
|
self.create_filename()
|
|
1848
|
-
f5h = file(self.
|
|
1849
|
-
f5h.create_extendable_array('time_data', (0, self.
|
|
1884
|
+
f5h = file(self.file, mode='w')
|
|
1885
|
+
f5h.create_extendable_array('time_data', (0, self.num_channels), self.precision)
|
|
1850
1886
|
ac = f5h.get_data_by_reference('time_data')
|
|
1851
1887
|
f5h.set_node_attribute(ac, 'sample_freq', self.sample_freq)
|
|
1852
1888
|
self.add_metadata(f5h)
|
|
@@ -1866,6 +1902,8 @@ class WriteH5(TimeOut):
|
|
|
1866
1902
|
if nitems > 0:
|
|
1867
1903
|
f5h.create_new_group('metadata', '/')
|
|
1868
1904
|
for key, value in self.metadata.items():
|
|
1905
|
+
if isinstance(value, str):
|
|
1906
|
+
value = array(value, dtype='S')
|
|
1869
1907
|
f5h.create_array('/metadata', key, value)
|
|
1870
1908
|
|
|
1871
1909
|
def result(self, num):
|
|
@@ -1881,19 +1919,19 @@ class WriteH5(TimeOut):
|
|
|
1881
1919
|
|
|
1882
1920
|
Returns
|
|
1883
1921
|
-------
|
|
1884
|
-
Samples in blocks of shape (num,
|
|
1922
|
+
Samples in blocks of shape (num, num_channels).
|
|
1885
1923
|
The last block may be shorter than num.
|
|
1886
1924
|
Echos the source output, but reads it from cache
|
|
1887
|
-
when available and prevents
|
|
1925
|
+
when available and prevents unnecessary recalculation.
|
|
1888
1926
|
|
|
1889
1927
|
"""
|
|
1890
|
-
self.
|
|
1928
|
+
self.write_flag = True
|
|
1891
1929
|
f5h = self.get_initialized_file()
|
|
1892
1930
|
ac = f5h.get_data_by_reference('time_data')
|
|
1893
1931
|
scount = 0
|
|
1894
|
-
stotal = self.
|
|
1932
|
+
stotal = self.num_samples_write
|
|
1895
1933
|
source_gen = self.source.result(num)
|
|
1896
|
-
while self.
|
|
1934
|
+
while self.write_flag:
|
|
1897
1935
|
sleft = stotal - scount
|
|
1898
1936
|
if stotal != -1 and sleft > 0:
|
|
1899
1937
|
anz = min(num, sleft)
|
|
@@ -1913,17 +1951,17 @@ class WriteH5(TimeOut):
|
|
|
1913
1951
|
|
|
1914
1952
|
|
|
1915
1953
|
class TimeConvolve(TimeOut):
|
|
1916
|
-
"""Uniformly partitioned overlap-save method (UPOLS)
|
|
1954
|
+
"""Fast frequency domain convolution with the Uniformly partitioned overlap-save method (UPOLS).
|
|
1917
1955
|
|
|
1918
1956
|
See :cite:`Wefers2015` for details.
|
|
1919
1957
|
"""
|
|
1920
1958
|
|
|
1921
|
-
|
|
1959
|
+
# Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
1922
1960
|
source = Instance(SamplesGenerator)
|
|
1923
1961
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1962
|
+
# Convolution kernel in the time domain. The second dimension of the kernel array has to be
|
|
1963
|
+
# either 1 or match :attr:`~SamplesGenerator.num_channels`. If only a single kernel is supplied,
|
|
1964
|
+
# it is applied to all channels.
|
|
1927
1965
|
kernel = CArray(dtype=float, desc='Convolution kernel.')
|
|
1928
1966
|
|
|
1929
1967
|
_block_size = Int(desc='Block size')
|
|
@@ -1934,7 +1972,7 @@ class TimeConvolve(TimeOut):
|
|
|
1934
1972
|
)
|
|
1935
1973
|
|
|
1936
1974
|
# internal identifier
|
|
1937
|
-
digest = Property(depends_on=['source.digest', 'kernel'
|
|
1975
|
+
digest = Property(depends_on=['source.digest', 'kernel'])
|
|
1938
1976
|
|
|
1939
1977
|
@cached_property
|
|
1940
1978
|
def _get_digest(self):
|
|
@@ -1949,9 +1987,9 @@ class TimeConvolve(TimeOut):
|
|
|
1949
1987
|
if self.kernel.ndim > 2:
|
|
1950
1988
|
msg = 'Only one or two dimensional kernels accepted.'
|
|
1951
1989
|
raise ValueError(msg)
|
|
1952
|
-
# check if number of kernels matches
|
|
1953
|
-
if self.kernel.shape[1] not in (1, self.source.
|
|
1954
|
-
msg = 'Number of kernels must be either `
|
|
1990
|
+
# check if number of kernels matches num_channels
|
|
1991
|
+
if self.kernel.shape[1] not in (1, self.source.num_channels):
|
|
1992
|
+
msg = 'Number of kernels must be either `num_channels` or one.'
|
|
1955
1993
|
raise ValueError(msg)
|
|
1956
1994
|
|
|
1957
1995
|
# compute the rfft of the kernel blockwise
|
|
@@ -1985,7 +2023,7 @@ class TimeConvolve(TimeOut):
|
|
|
1985
2023
|
|
|
1986
2024
|
Returns
|
|
1987
2025
|
-------
|
|
1988
|
-
Samples in blocks of shape (num,
|
|
2026
|
+
Samples in blocks of shape (num, num_channels).
|
|
1989
2027
|
The last block may be shorter than num.
|
|
1990
2028
|
|
|
1991
2029
|
"""
|
|
@@ -1993,8 +2031,8 @@ class TimeConvolve(TimeOut):
|
|
|
1993
2031
|
# initialize variables
|
|
1994
2032
|
self._block_size = num
|
|
1995
2033
|
L = self.kernel.shape[0]
|
|
1996
|
-
N = self.source.
|
|
1997
|
-
M = self.source.
|
|
2034
|
+
N = self.source.num_channels
|
|
2035
|
+
M = self.source.num_samples
|
|
1998
2036
|
numblocks_kernel = int(ceil(L / num)) # number of kernel blocks
|
|
1999
2037
|
Q = int(ceil(M / num)) # number of signal blocks
|
|
2000
2038
|
R = int(ceil((L + M - 1) / num)) # number of output blocks
|
|
@@ -2044,13 +2082,13 @@ class TimeConvolve(TimeOut):
|
|
|
2044
2082
|
|
|
2045
2083
|
|
|
2046
2084
|
@nb.jit(nopython=True, cache=True)
|
|
2047
|
-
def _append_to_fdl(fdl, idx, numblocks_kernel, buff):
|
|
2085
|
+
def _append_to_fdl(fdl, idx, numblocks_kernel, buff): # pragma: no cover
|
|
2048
2086
|
fdl[idx] = buff
|
|
2049
2087
|
idx = int(idx + 1 % numblocks_kernel)
|
|
2050
2088
|
|
|
2051
2089
|
|
|
2052
2090
|
@nb.jit(nopython=True, cache=True)
|
|
2053
|
-
def _spectral_sum(out, fdl, kb):
|
|
2091
|
+
def _spectral_sum(out, fdl, kb): # pragma: no cover
|
|
2054
2092
|
P, B, N = kb.shape
|
|
2055
2093
|
for n in range(N):
|
|
2056
2094
|
for b in range(B):
|
|
@@ -2062,17 +2100,18 @@ def _spectral_sum(out, fdl, kb):
|
|
|
2062
2100
|
|
|
2063
2101
|
|
|
2064
2102
|
class MaskedTimeInOut(MaskedTimeOut):
|
|
2065
|
-
"""Signal processing block for channel and sample selection
|
|
2103
|
+
"""Signal processing block for channel and sample selection.
|
|
2066
2104
|
|
|
2067
2105
|
.. deprecated:: 24.10
|
|
2068
|
-
Using :class:`~acoular.tprocess.MaskedTimeInOut` is deprecated and will be removed in
|
|
2069
|
-
version 25.07. Use :class:`~acoular.tprocess.MaskedTimeOut` instead.
|
|
2106
|
+
Using :class:`~acoular.tprocess.MaskedTimeInOut` is deprecated and will be removed in
|
|
2107
|
+
Acoular version 25.07. Use :class:`~acoular.tprocess.MaskedTimeOut` instead.
|
|
2070
2108
|
"""
|
|
2071
2109
|
|
|
2072
2110
|
def __init__(self, *args, **kwargs):
|
|
2073
2111
|
super().__init__(*args, **kwargs)
|
|
2074
2112
|
warn(
|
|
2075
|
-
'Using MaskedTimeInOut is deprecated and will be removed in Acoular version 25.07.
|
|
2113
|
+
'Using MaskedTimeInOut is deprecated and will be removed in Acoular version 25.07. \
|
|
2114
|
+
Use class MaskedTimeOut instead.',
|
|
2076
2115
|
DeprecationWarning,
|
|
2077
2116
|
stacklevel=2,
|
|
2078
2117
|
)
|