acoular 24.3__py3-none-any.whl → 24.5__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 +118 -50
- acoular/calib.py +29 -38
- acoular/configuration.py +116 -73
- acoular/demo/__init__.py +10 -4
- acoular/demo/acoular_demo.py +78 -53
- acoular/environments.py +265 -262
- acoular/fastFuncs.py +361 -191
- acoular/fbeamform.py +1460 -1404
- acoular/grids.py +501 -545
- acoular/h5cache.py +50 -59
- acoular/h5files.py +154 -137
- acoular/internal.py +10 -11
- acoular/microphones.py +57 -53
- acoular/sdinput.py +47 -52
- acoular/signals.py +167 -179
- acoular/sources.py +818 -693
- acoular/spectra.py +349 -359
- acoular/tbeamform.py +414 -413
- acoular/tfastfuncs.py +178 -101
- acoular/tools/__init__.py +25 -0
- acoular/tools/aiaa.py +186 -0
- acoular/tools/helpers.py +189 -0
- acoular/tools/metrics.py +165 -0
- acoular/tprocess.py +1201 -1143
- acoular/traitsviews.py +513 -501
- acoular/trajectory.py +50 -52
- acoular/version.py +5 -6
- acoular/xml/minidsp_uma-16.xml +20 -0
- acoular/xml/{minidsp_uma16.xml → minidsp_uma-16_mirrored.xml} +3 -0
- {acoular-24.3.dist-info → acoular-24.5.dist-info}/METADATA +45 -46
- acoular-24.5.dist-info/RECORD +50 -0
- {acoular-24.3.dist-info → acoular-24.5.dist-info}/WHEEL +1 -1
- acoular-24.5.dist-info/licenses/LICENSE +28 -0
- acoular/fileimport.py +0 -380
- acoular/nidaqimport.py +0 -273
- acoular/tests/reference_data/BeamformerBase.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFLassoLarsBIC.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFNNLS.npy +0 -0
- acoular/tests/reference_data/BeamformerCapon.npy +0 -0
- acoular/tests/reference_data/BeamformerClean.npy +0 -0
- acoular/tests/reference_data/BeamformerCleansc.npy +0 -0
- acoular/tests/reference_data/BeamformerCleant.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSq.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerDamas.npy +0 -0
- acoular/tests/reference_data/BeamformerDamasPlus.npy +0 -0
- acoular/tests/reference_data/BeamformerEig.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerFunctional.npy +0 -0
- acoular/tests/reference_data/BeamformerGIB.npy +0 -0
- acoular/tests/reference_data/BeamformerGridlessOrth.npy +0 -0
- acoular/tests/reference_data/BeamformerMusic.npy +0 -0
- acoular/tests/reference_data/BeamformerOrth.npy +0 -0
- acoular/tests/reference_data/BeamformerSODIX.npy +0 -0
- acoular/tests/reference_data/BeamformerTime.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeSq.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeTraj.npy +0 -0
- acoular/tests/reference_data/Environment.npy +0 -0
- acoular/tests/reference_data/Example1_numerical_values_testsum.h5 +0 -0
- acoular/tests/reference_data/FiltFiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltFiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_A_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_C_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_Z_.npy +0 -0
- acoular/tests/reference_data/FiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/Filter__.npy +0 -0
- acoular/tests/reference_data/GeneralFlowEnvironment.npy +0 -0
- acoular/tests/reference_data/OctaveFilterBank__.npy +0 -0
- acoular/tests/reference_data/OpenJet.npy +0 -0
- acoular/tests/reference_data/PointSource.npy +0 -0
- acoular/tests/reference_data/PowerSpectra_csm.npy +0 -0
- acoular/tests/reference_data/PowerSpectra_ev.npy +0 -0
- acoular/tests/reference_data/RotatingFlow.npy +0 -0
- acoular/tests/reference_data/SlotJet.npy +0 -0
- acoular/tests/reference_data/TimeAverage__.npy +0 -0
- acoular/tests/reference_data/TimeCumAverage__.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_F_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_I_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_S_.npy +0 -0
- acoular/tests/reference_data/TimeInOut__.npy +0 -0
- acoular/tests/reference_data/TimePower__.npy +0 -0
- acoular/tests/reference_data/TimeReverse__.npy +0 -0
- acoular/tests/reference_data/UniformFlowEnvironment.npy +0 -0
- acoular/tests/reference_data/beamformer_traj_time_data.h5 +0 -0
- acoular/tests/run_tests.sh +0 -18
- acoular/tests/run_tests_osx.sh +0 -16
- acoular/tests/test.npy +0 -0
- acoular/tests/test_beamformer_results.py +0 -213
- acoular/tests/test_classes.py +0 -60
- acoular/tests/test_digest.py +0 -125
- acoular/tests/test_environments.py +0 -73
- acoular/tests/test_example1.py +0 -124
- acoular/tests/test_grid.py +0 -92
- acoular/tests/test_integrate.py +0 -102
- acoular/tests/test_signals.py +0 -60
- acoular/tests/test_sources.py +0 -65
- acoular/tests/test_spectra.py +0 -38
- acoular/tests/test_timecache.py +0 -35
- acoular/tests/test_tprocess.py +0 -90
- acoular/tests/test_traj_beamformer_results.py +0 -164
- acoular/tests/unsupported/SpeedComparison/OvernightTestcasesBeamformer_nMics32_nGridPoints100_nFreqs4_nTrials10.png +0 -0
- acoular/tests/unsupported/SpeedComparison/cythonBeamformer.pyx +0 -237
- acoular/tests/unsupported/SpeedComparison/mainForCython.py +0 -103
- acoular/tests/unsupported/SpeedComparison/mainForParallelJit.py +0 -143
- acoular/tests/unsupported/SpeedComparison/setupCythonOpenMP.py +0 -63
- acoular/tests/unsupported/SpeedComparison/sharedFunctions.py +0 -153
- acoular/tests/unsupported/SpeedComparison/timeOverNMics_AllImportantMethods.png +0 -0
- acoular/tests/unsupported/SpeedComparison/timeOverNMics_faverage.png +0 -0
- acoular/tests/unsupported/SpeedComparison/vglOptimierungFAverage.py +0 -204
- acoular/tests/unsupported/SpeedComparison/vglOptimierungGaussSeidel.py +0 -182
- acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAMFULL_INVERSE.py +0 -764
- acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAM_OS.py +0 -231
- acoular/tests/unsupported/SpeedComparison/whatsFastestWayFor_absASquared.py +0 -48
- acoular/tests/unsupported/functionalBeamformer.py +0 -123
- acoular/tests/unsupported/precisionTest.py +0 -153
- acoular/tests/unsupported/validationOfBeamformerFuncsPOSTAcoularIntegration.py +0 -254
- acoular/tests/unsupported/validationOfBeamformerFuncsPREeAcoularIntegration.py +0 -531
- acoular/tools.py +0 -422
- acoular-24.3.dist-info/RECORD +0 -148
- acoular-24.3.dist-info/licenses/LICENSE +0 -29
- {acoular-24.3.dist-info → acoular-24.5.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/signals.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
#pylint: disable-msg=E0611, E1101, C0103, R0901, R0902, R0903, R0904, W0232
|
|
3
|
-
#------------------------------------------------------------------------------
|
|
1
|
+
# ------------------------------------------------------------------------------
|
|
4
2
|
# Copyright (c) Acoular Development Team.
|
|
5
|
-
|
|
3
|
+
# ------------------------------------------------------------------------------
|
|
6
4
|
"""Implements signal generators for the simulation of acoustic sources.
|
|
7
5
|
|
|
8
6
|
.. autosummary::
|
|
@@ -18,352 +16,342 @@
|
|
|
18
16
|
"""
|
|
19
17
|
|
|
20
18
|
# imports from other packages
|
|
21
|
-
from
|
|
22
|
-
|
|
19
|
+
from warnings import warn
|
|
20
|
+
|
|
21
|
+
from numpy import arange, array, log, pi, repeat, sin, sqrt, tile, zeros
|
|
23
22
|
from numpy.random import RandomState
|
|
24
|
-
from traits.api import HasPrivateTraits, Trait, Float, Int, CLong, Bool, \
|
|
25
|
-
Property, cached_property, Delegate, CArray
|
|
26
23
|
from scipy.signal import resample, sosfilt, tf2sos
|
|
27
|
-
from
|
|
24
|
+
from traits.api import Bool, CArray, CLong, Delegate, Float, HasPrivateTraits, Int, Property, Trait, cached_property
|
|
25
|
+
|
|
26
|
+
from .internal import digest
|
|
28
27
|
|
|
29
28
|
# acoular imports
|
|
30
29
|
from .tprocess import SamplesGenerator
|
|
31
|
-
from .internal import digest
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Virtual base class for a simple one-channel signal generator.
|
|
36
|
-
|
|
31
|
+
|
|
32
|
+
class SignalGenerator(HasPrivateTraits):
|
|
33
|
+
"""Virtual base class for a simple one-channel signal generator.
|
|
34
|
+
|
|
37
35
|
Defines the common interface for all SignalGenerator classes. This class
|
|
38
36
|
may be used as a base for specialized SignalGenerator implementations. It
|
|
39
37
|
should not be used directly as it contains no real functionality.
|
|
40
38
|
"""
|
|
41
39
|
|
|
42
40
|
#: RMS amplitude of source signal (for point source: in 1 m distance).
|
|
43
|
-
rms = Float(1.0,
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
rms = Float(1.0, desc='rms amplitude')
|
|
42
|
+
|
|
46
43
|
#: Sampling frequency of the signal.
|
|
47
|
-
sample_freq = Float(1.0,
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
sample_freq = Float(1.0, desc='sampling frequency')
|
|
45
|
+
|
|
50
46
|
#: Number of samples to generate.
|
|
51
47
|
numsamples = CLong
|
|
52
|
-
|
|
48
|
+
|
|
53
49
|
# internal identifier
|
|
54
50
|
digest = Property
|
|
55
51
|
|
|
56
|
-
def _get_digest(
|
|
52
|
+
def _get_digest(self):
|
|
57
53
|
return ''
|
|
58
54
|
|
|
59
55
|
def signal(self):
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
"""
|
|
63
|
-
pass
|
|
64
|
-
|
|
56
|
+
"""Deliver the signal."""
|
|
57
|
+
|
|
65
58
|
def usignal(self, factor):
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
"""Delivers the signal resampled with a multiple of the sampling freq.
|
|
60
|
+
|
|
69
61
|
Uses fourier transform method for resampling (from scipy.signal).
|
|
70
|
-
|
|
62
|
+
|
|
71
63
|
Parameters
|
|
72
64
|
----------
|
|
73
65
|
factor : integer
|
|
74
66
|
The factor defines how many times the new sampling frequency is
|
|
75
67
|
larger than :attr:`sample_freq`.
|
|
76
|
-
|
|
68
|
+
|
|
77
69
|
Returns
|
|
78
70
|
-------
|
|
79
71
|
array of floats
|
|
80
72
|
The resulting signal of length `factor` * :attr:`numsamples`.
|
|
73
|
+
|
|
81
74
|
"""
|
|
82
|
-
return resample(self.signal(), factor*self.numsamples)
|
|
75
|
+
return resample(self.signal(), factor * self.numsamples)
|
|
83
76
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
White noise signal generator.
|
|
87
|
-
"""
|
|
77
|
+
|
|
78
|
+
class WNoiseGenerator(SignalGenerator):
|
|
79
|
+
"""White noise signal generator."""
|
|
88
80
|
|
|
89
81
|
#: Seed for random number generator, defaults to 0.
|
|
90
82
|
#: This parameter should be set differently for different instances
|
|
91
83
|
#: to guarantee statistically independent (non-correlated) outputs.
|
|
92
|
-
seed = Int(0,
|
|
93
|
-
desc="random seed value")
|
|
84
|
+
seed = Int(0, desc='random seed value')
|
|
94
85
|
|
|
95
86
|
# internal identifier
|
|
96
|
-
digest = Property(
|
|
97
|
-
depends_on
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
digest = Property(
|
|
88
|
+
depends_on=['rms', 'numsamples', 'sample_freq', 'seed', '__class__'],
|
|
89
|
+
)
|
|
90
|
+
|
|
101
91
|
@cached_property
|
|
102
|
-
def _get_digest(
|
|
92
|
+
def _get_digest(self):
|
|
103
93
|
return digest(self)
|
|
104
94
|
|
|
105
95
|
def signal(self):
|
|
106
|
-
"""
|
|
107
|
-
Deliver the signal.
|
|
96
|
+
"""Deliver the signal.
|
|
108
97
|
|
|
109
98
|
Returns
|
|
110
99
|
-------
|
|
111
100
|
Array of floats
|
|
112
101
|
The resulting signal as an array of length :attr:`~SignalGenerator.numsamples`.
|
|
102
|
+
|
|
113
103
|
"""
|
|
114
104
|
rnd_gen = RandomState(self.seed)
|
|
115
|
-
return self.rms*rnd_gen.standard_normal(self.numsamples)
|
|
116
|
-
|
|
105
|
+
return self.rms * rnd_gen.standard_normal(self.numsamples)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class PNoiseGenerator(SignalGenerator):
|
|
109
|
+
"""Pink noise signal generator.
|
|
117
110
|
|
|
118
|
-
class PNoiseGenerator( SignalGenerator ):
|
|
119
|
-
"""
|
|
120
|
-
Pink noise signal generator.
|
|
121
|
-
|
|
122
111
|
Simulation of pink noise is based on the Voss-McCartney algorithm.
|
|
123
112
|
Ref.:
|
|
124
|
-
|
|
113
|
+
|
|
125
114
|
* S.J. Orfanidis: Signal Processing (2010), pp. 729-733
|
|
126
115
|
* online discussion: http://www.firstpr.com.au/dsp/pink-noise/
|
|
127
|
-
|
|
128
|
-
The idea is to iteratively add larger-wavelength noise to get 1/f
|
|
116
|
+
|
|
117
|
+
The idea is to iteratively add larger-wavelength noise to get 1/f
|
|
129
118
|
characteristic.
|
|
130
119
|
"""
|
|
131
|
-
|
|
120
|
+
|
|
132
121
|
#: Seed for random number generator, defaults to 0.
|
|
133
122
|
#: This parameter should be set differently for different instances
|
|
134
123
|
#: to guarantee statistically independent (non-correlated) outputs.
|
|
135
|
-
seed = Int(0,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
#: "Octave depth" -- higher values for 1/f spectrum at low frequencies,
|
|
124
|
+
seed = Int(0, desc='random seed value')
|
|
125
|
+
|
|
126
|
+
#: "Octave depth" -- higher values for 1/f spectrum at low frequencies,
|
|
139
127
|
#: but longer calculation, defaults to 16.
|
|
140
|
-
depth = Int(16,
|
|
141
|
-
desc="octave depth")
|
|
128
|
+
depth = Int(16, desc='octave depth')
|
|
142
129
|
|
|
143
130
|
# internal identifier
|
|
144
|
-
digest = Property(
|
|
145
|
-
depends_on
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
131
|
+
digest = Property(
|
|
132
|
+
depends_on=['rms', 'numsamples', 'sample_freq', 'seed', 'depth', '__class__'],
|
|
133
|
+
)
|
|
134
|
+
|
|
149
135
|
@cached_property
|
|
150
|
-
def _get_digest(
|
|
136
|
+
def _get_digest(self):
|
|
151
137
|
return digest(self)
|
|
152
138
|
|
|
153
139
|
def signal(self):
|
|
154
140
|
nums = self.numsamples
|
|
155
141
|
depth = self.depth
|
|
156
142
|
# maximum depth depending on number of samples
|
|
157
|
-
max_depth = int(
|
|
158
|
-
|
|
143
|
+
max_depth = int(log(nums) / log(2))
|
|
144
|
+
|
|
159
145
|
if depth > max_depth:
|
|
160
146
|
depth = max_depth
|
|
161
|
-
print(
|
|
162
|
-
|
|
147
|
+
print('Pink noise filter depth set to maximum possible value of %d.' % max_depth)
|
|
148
|
+
|
|
163
149
|
rnd_gen = RandomState(self.seed)
|
|
164
150
|
s = rnd_gen.standard_normal(nums)
|
|
165
151
|
for _ in range(depth):
|
|
166
|
-
ind = 2**_-1
|
|
167
|
-
lind = nums-ind
|
|
168
|
-
dind = 2**(_+1)
|
|
169
|
-
s[ind:] += repeat(
|
|
152
|
+
ind = 2**_ - 1
|
|
153
|
+
lind = nums - ind
|
|
154
|
+
dind = 2 ** (_ + 1)
|
|
155
|
+
s[ind:] += repeat(rnd_gen.standard_normal(nums // dind + 1), dind)[:lind]
|
|
170
156
|
# divide by sqrt(depth+1.5) to get same overall level as white noise
|
|
171
|
-
return self.rms/sqrt(depth+1.5) * s
|
|
157
|
+
return self.rms / sqrt(depth + 1.5) * s
|
|
172
158
|
|
|
173
159
|
|
|
174
160
|
class FiltWNoiseGenerator(WNoiseGenerator):
|
|
175
|
-
"""
|
|
176
|
-
Filtered white noise signal following an autoregressive (AR), moving-average
|
|
161
|
+
"""Filtered white noise signal following an autoregressive (AR), moving-average
|
|
177
162
|
(MA) or autoregressive moving-average (ARMA) process.
|
|
178
|
-
|
|
179
|
-
The desired frequency response of the filter can be defined by specifying
|
|
180
|
-
the filter coefficients :attr:`ar` and :attr:`ma`.
|
|
181
|
-
The RMS value specified via the :attr:`rms` attribute belongs to the white noise
|
|
182
|
-
signal and differs from the RMS value of the filtered signal.
|
|
183
|
-
For numerical stability at high orders, the filter is a combination of second order
|
|
184
|
-
sections (sos).
|
|
163
|
+
|
|
164
|
+
The desired frequency response of the filter can be defined by specifying
|
|
165
|
+
the filter coefficients :attr:`ar` and :attr:`ma`.
|
|
166
|
+
The RMS value specified via the :attr:`rms` attribute belongs to the white noise
|
|
167
|
+
signal and differs from the RMS value of the filtered signal.
|
|
168
|
+
For numerical stability at high orders, the filter is a combination of second order
|
|
169
|
+
sections (sos).
|
|
185
170
|
"""
|
|
186
171
|
|
|
187
|
-
ar = CArray(value=array([]),dtype=float,
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
desc="moving-average coefficients (coefficients of the numerator)")
|
|
192
|
-
|
|
172
|
+
ar = CArray(value=array([]), dtype=float, desc='autoregressive coefficients (coefficients of the denominator)')
|
|
173
|
+
|
|
174
|
+
ma = CArray(value=array([]), dtype=float, desc='moving-average coefficients (coefficients of the numerator)')
|
|
175
|
+
|
|
193
176
|
# internal identifier
|
|
194
|
-
digest = Property(
|
|
195
|
-
depends_on
|
|
196
|
-
'ar',
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
177
|
+
digest = Property(
|
|
178
|
+
depends_on=[
|
|
179
|
+
'ar',
|
|
180
|
+
'ma',
|
|
181
|
+
'rms',
|
|
182
|
+
'numsamples',
|
|
183
|
+
'sample_freq',
|
|
184
|
+
'seed',
|
|
185
|
+
'__class__',
|
|
186
|
+
],
|
|
187
|
+
)
|
|
188
|
+
|
|
201
189
|
@cached_property
|
|
202
|
-
def _get_digest(
|
|
190
|
+
def _get_digest(self):
|
|
203
191
|
return digest(self)
|
|
204
192
|
|
|
205
|
-
def handle_empty_coefficients(self,coefficients):
|
|
193
|
+
def handle_empty_coefficients(self, coefficients):
|
|
206
194
|
if coefficients.size == 0:
|
|
207
195
|
return array([1.0])
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
196
|
+
return coefficients
|
|
197
|
+
|
|
211
198
|
def signal(self):
|
|
212
|
-
"""
|
|
213
|
-
Deliver the signal.
|
|
199
|
+
"""Deliver the signal.
|
|
214
200
|
|
|
215
201
|
Returns
|
|
216
202
|
-------
|
|
217
203
|
Array of floats
|
|
218
204
|
The resulting signal as an array of length :attr:`~SignalGenerator.numsamples`.
|
|
205
|
+
|
|
219
206
|
"""
|
|
220
207
|
rnd_gen = RandomState(self.seed)
|
|
221
208
|
ma = self.handle_empty_coefficients(self.ma)
|
|
222
209
|
ar = self.handle_empty_coefficients(self.ar)
|
|
223
210
|
sos = tf2sos(ma, ar)
|
|
224
211
|
ntaps = ma.shape[0]
|
|
225
|
-
sdelay = round(0.5*(ntaps-1))
|
|
226
|
-
wnoise = self.rms*rnd_gen.standard_normal(
|
|
212
|
+
sdelay = round(0.5 * (ntaps - 1))
|
|
213
|
+
wnoise = self.rms * rnd_gen.standard_normal(
|
|
214
|
+
self.numsamples + sdelay,
|
|
215
|
+
) # create longer signal to compensate delay
|
|
227
216
|
return sosfilt(sos, x=wnoise)[sdelay:]
|
|
228
217
|
|
|
229
218
|
|
|
230
|
-
class SineGenerator(
|
|
231
|
-
"""
|
|
232
|
-
Sine signal generator with adjustable frequency and phase.
|
|
233
|
-
"""
|
|
219
|
+
class SineGenerator(SignalGenerator):
|
|
220
|
+
"""Sine signal generator with adjustable frequency and phase."""
|
|
234
221
|
|
|
235
222
|
#: Sine wave frequency, float, defaults to 1000.0.
|
|
236
|
-
freq = Float(1000.0,
|
|
237
|
-
desc="Frequency")
|
|
223
|
+
freq = Float(1000.0, desc='Frequency')
|
|
238
224
|
|
|
239
225
|
#: Sine wave phase (in radians), float, defaults to 0.0.
|
|
240
|
-
phase = Float(0.0,
|
|
241
|
-
desc="Phase")
|
|
226
|
+
phase = Float(0.0, desc='Phase')
|
|
242
227
|
|
|
243
228
|
# Internal shadow trait for rms/amplitude values.
|
|
244
229
|
# Do not set directly.
|
|
245
230
|
_amp = Float(1.0)
|
|
246
|
-
|
|
231
|
+
|
|
247
232
|
#: RMS of source signal (for point source: in 1 m distance).
|
|
248
233
|
#: Deprecated. For amplitude use :attr:`amplitude`.
|
|
249
234
|
rms = Property(desc='rms amplitude')
|
|
250
|
-
|
|
235
|
+
|
|
251
236
|
def _get_rms(self):
|
|
252
|
-
return self._amp/2**0.5
|
|
253
|
-
|
|
237
|
+
return self._amp / 2**0.5
|
|
238
|
+
|
|
254
239
|
def _set_rms(self, rms):
|
|
255
|
-
warn(
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
+
)
|
|
259
247
|
self._amp = rms * 2**0.5
|
|
260
|
-
|
|
261
|
-
#: Amplitude of source signal (for point source: in 1 m distance).
|
|
248
|
+
|
|
249
|
+
#: Amplitude of source signal (for point source: in 1 m distance).
|
|
262
250
|
#: Defaults to 1.0.
|
|
263
251
|
amplitude = Property(desc='amplitude')
|
|
264
|
-
|
|
252
|
+
|
|
265
253
|
def _get_amplitude(self):
|
|
266
254
|
return self._amp
|
|
267
|
-
|
|
255
|
+
|
|
268
256
|
def _set_amplitude(self, amp):
|
|
269
257
|
self._amp = amp
|
|
270
|
-
|
|
258
|
+
|
|
271
259
|
# internal identifier
|
|
272
|
-
digest = Property(
|
|
273
|
-
depends_on
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
260
|
+
digest = Property(
|
|
261
|
+
depends_on=['_amp', 'numsamples', 'sample_freq', 'freq', 'phase', '__class__'],
|
|
262
|
+
)
|
|
263
|
+
|
|
277
264
|
@cached_property
|
|
278
|
-
def _get_digest(
|
|
265
|
+
def _get_digest(self):
|
|
279
266
|
return digest(self)
|
|
280
267
|
|
|
281
268
|
def signal(self):
|
|
282
|
-
"""
|
|
283
|
-
Deliver the signal.
|
|
269
|
+
"""Deliver the signal.
|
|
284
270
|
|
|
285
271
|
Returns
|
|
286
272
|
-------
|
|
287
273
|
array of floats
|
|
288
274
|
The resulting signal as an array of length :attr:`~SignalGenerator.numsamples`.
|
|
275
|
+
|
|
289
276
|
"""
|
|
290
|
-
t = arange(self.numsamples, dtype=float)/self.sample_freq
|
|
291
|
-
return self.amplitude * sin(2*pi*self.freq * t + self.phase)
|
|
277
|
+
t = arange(self.numsamples, dtype=float) / self.sample_freq
|
|
278
|
+
return self.amplitude * sin(2 * pi * self.freq * t + self.phase)
|
|
292
279
|
|
|
293
280
|
|
|
294
|
-
class GenericSignalGenerator(
|
|
295
|
-
"""
|
|
296
|
-
|
|
297
|
-
"""
|
|
281
|
+
class GenericSignalGenerator(SignalGenerator):
|
|
282
|
+
"""Generate signal from output of :class:`~acoular.tprocess.SamplesGenerator` object."""
|
|
283
|
+
|
|
298
284
|
#: Data source; :class:`~acoular.tprocess.SamplesGenerator` or derived object.
|
|
299
285
|
source = Trait(SamplesGenerator)
|
|
300
|
-
|
|
286
|
+
|
|
301
287
|
#: Sampling frequency of output signal, as given by :attr:`source`.
|
|
302
288
|
sample_freq = Delegate('source')
|
|
303
|
-
|
|
289
|
+
|
|
304
290
|
_numsamples = CLong(0)
|
|
305
|
-
|
|
291
|
+
|
|
306
292
|
#: Number of samples to generate. Is set to source.numsamples by default.
|
|
307
293
|
numsamples = Property()
|
|
308
|
-
|
|
309
|
-
def _get_numsamples(
|
|
294
|
+
|
|
295
|
+
def _get_numsamples(self):
|
|
310
296
|
if self._numsamples:
|
|
311
297
|
return self._numsamples
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
def _set_numsamples( self, numsamples ):
|
|
298
|
+
return self.source.numsamples
|
|
299
|
+
|
|
300
|
+
def _set_numsamples(self, numsamples):
|
|
316
301
|
self._numsamples = numsamples
|
|
317
302
|
|
|
318
|
-
#: Boolean flag, if 'True' (default), signal track is repeated if requested
|
|
303
|
+
#: Boolean flag, if 'True' (default), signal track is repeated if requested
|
|
319
304
|
#: :attr:`numsamples` is higher than available sample number
|
|
320
305
|
loop_signal = Bool(True)
|
|
321
|
-
|
|
306
|
+
|
|
322
307
|
# internal identifier
|
|
323
|
-
digest = Property(
|
|
324
|
-
depends_on
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
308
|
+
digest = Property(
|
|
309
|
+
depends_on=['source.digest', 'loop_signal', 'numsamples', 'rms', '__class__'],
|
|
310
|
+
)
|
|
311
|
+
|
|
328
312
|
@cached_property
|
|
329
|
-
def _get_digest(
|
|
313
|
+
def _get_digest(self):
|
|
330
314
|
return digest(self)
|
|
331
|
-
|
|
315
|
+
|
|
332
316
|
def signal(self):
|
|
333
|
-
"""
|
|
334
|
-
Deliver the signal.
|
|
317
|
+
"""Deliver the signal.
|
|
335
318
|
|
|
336
319
|
Returns
|
|
337
320
|
-------
|
|
338
321
|
array of floats
|
|
339
322
|
The resulting signal as an array of length :attr:`~GenericSignalGenerator.numsamples`.
|
|
323
|
+
|
|
340
324
|
"""
|
|
341
325
|
block = 1024
|
|
342
326
|
if self.source.numchannels > 1:
|
|
343
|
-
warn(
|
|
327
|
+
warn(
|
|
328
|
+
'Signal source has more than one channel. Only channel 0 will be used for signal.',
|
|
329
|
+
Warning,
|
|
330
|
+
stacklevel=2,
|
|
331
|
+
)
|
|
344
332
|
nums = self.numsamples
|
|
345
333
|
track = zeros(nums)
|
|
346
|
-
|
|
334
|
+
|
|
347
335
|
# iterate through source generator to fill signal track
|
|
348
336
|
for i, temp in enumerate(self.source.result(block)):
|
|
349
|
-
start = block*i
|
|
350
|
-
stop = start + len(temp[:,0])
|
|
337
|
+
start = block * i
|
|
338
|
+
stop = start + len(temp[:, 0])
|
|
351
339
|
if nums > stop:
|
|
352
|
-
track[start:stop] = temp[:,0]
|
|
353
|
-
else:
|
|
354
|
-
track[start:nums] = temp[:nums-start,0]
|
|
340
|
+
track[start:stop] = temp[:, 0]
|
|
341
|
+
else: # exit loop preliminarily if wanted signal samples are reached
|
|
342
|
+
track[start:nums] = temp[: nums - start, 0]
|
|
355
343
|
break
|
|
356
|
-
|
|
344
|
+
|
|
357
345
|
# if the signal should be repeated after finishing and there are still samples open
|
|
358
346
|
if self.loop_signal and (nums > stop):
|
|
359
|
-
|
|
360
347
|
# fill up empty track with as many full source signals as possible
|
|
361
348
|
nloops = nums // stop
|
|
362
|
-
if nloops>1:
|
|
349
|
+
if nloops > 1:
|
|
350
|
+
track[stop : stop * nloops] = tile(track[:stop], nloops - 1)
|
|
363
351
|
# fill up remaining empty track
|
|
364
|
-
res = nums % stop
|
|
365
|
-
if res > 0:
|
|
366
|
-
|
|
352
|
+
res = nums % stop # last part of unfinished loop
|
|
353
|
+
if res > 0:
|
|
354
|
+
track[stop * nloops :] = track[:res]
|
|
355
|
+
|
|
367
356
|
# The rms value is just an amplification here
|
|
368
|
-
return self.rms*track
|
|
369
|
-
|
|
357
|
+
return self.rms * track
|