acoular 24.3__py3-none-any.whl → 24.7__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.
Files changed (139) hide show
  1. acoular/__init__.py +119 -54
  2. acoular/calib.py +29 -38
  3. acoular/configuration.py +132 -82
  4. acoular/demo/__init__.py +10 -4
  5. acoular/demo/acoular_demo.py +73 -55
  6. acoular/environments.py +270 -264
  7. acoular/fastFuncs.py +366 -196
  8. acoular/fbeamform.py +1797 -1934
  9. acoular/grids.py +504 -548
  10. acoular/h5cache.py +74 -83
  11. acoular/h5files.py +159 -142
  12. acoular/internal.py +13 -14
  13. acoular/microphones.py +57 -53
  14. acoular/sdinput.py +57 -53
  15. acoular/signals.py +180 -178
  16. acoular/sources.py +920 -724
  17. acoular/spectra.py +353 -363
  18. acoular/tbeamform.py +416 -416
  19. acoular/tfastfuncs.py +180 -104
  20. acoular/tools/__init__.py +25 -0
  21. acoular/tools/aiaa.py +185 -0
  22. acoular/tools/helpers.py +189 -0
  23. acoular/tools/metrics.py +165 -0
  24. acoular/tprocess.py +1240 -1182
  25. acoular/traitsviews.py +513 -501
  26. acoular/trajectory.py +50 -52
  27. acoular/version.py +5 -6
  28. acoular/xml/minidsp_uma-16.xml +20 -0
  29. acoular/xml/{minidsp_uma16.xml → minidsp_uma-16_mirrored.xml} +3 -0
  30. {acoular-24.3.dist-info → acoular-24.7.dist-info}/METADATA +58 -39
  31. acoular-24.7.dist-info/RECORD +50 -0
  32. {acoular-24.3.dist-info → acoular-24.7.dist-info}/WHEEL +1 -1
  33. acoular-24.7.dist-info/licenses/LICENSE +28 -0
  34. acoular/fileimport.py +0 -380
  35. acoular/nidaqimport.py +0 -273
  36. acoular/tests/reference_data/BeamformerBase.npy +0 -0
  37. acoular/tests/reference_data/BeamformerBaseFalse1.npy +0 -0
  38. acoular/tests/reference_data/BeamformerBaseFalse2.npy +0 -0
  39. acoular/tests/reference_data/BeamformerBaseFalse3.npy +0 -0
  40. acoular/tests/reference_data/BeamformerBaseFalse4.npy +0 -0
  41. acoular/tests/reference_data/BeamformerBaseTrue1.npy +0 -0
  42. acoular/tests/reference_data/BeamformerBaseTrue2.npy +0 -0
  43. acoular/tests/reference_data/BeamformerBaseTrue3.npy +0 -0
  44. acoular/tests/reference_data/BeamformerBaseTrue4.npy +0 -0
  45. acoular/tests/reference_data/BeamformerCMFLassoLarsBIC.npy +0 -0
  46. acoular/tests/reference_data/BeamformerCMFNNLS.npy +0 -0
  47. acoular/tests/reference_data/BeamformerCapon.npy +0 -0
  48. acoular/tests/reference_data/BeamformerClean.npy +0 -0
  49. acoular/tests/reference_data/BeamformerCleansc.npy +0 -0
  50. acoular/tests/reference_data/BeamformerCleant.npy +0 -0
  51. acoular/tests/reference_data/BeamformerCleantSq.npy +0 -0
  52. acoular/tests/reference_data/BeamformerCleantSqTraj.npy +0 -0
  53. acoular/tests/reference_data/BeamformerCleantTraj.npy +0 -0
  54. acoular/tests/reference_data/BeamformerDamas.npy +0 -0
  55. acoular/tests/reference_data/BeamformerDamasPlus.npy +0 -0
  56. acoular/tests/reference_data/BeamformerEig.npy +0 -0
  57. acoular/tests/reference_data/BeamformerEigFalse1.npy +0 -0
  58. acoular/tests/reference_data/BeamformerEigFalse2.npy +0 -0
  59. acoular/tests/reference_data/BeamformerEigFalse3.npy +0 -0
  60. acoular/tests/reference_data/BeamformerEigFalse4.npy +0 -0
  61. acoular/tests/reference_data/BeamformerEigTrue1.npy +0 -0
  62. acoular/tests/reference_data/BeamformerEigTrue2.npy +0 -0
  63. acoular/tests/reference_data/BeamformerEigTrue3.npy +0 -0
  64. acoular/tests/reference_data/BeamformerEigTrue4.npy +0 -0
  65. acoular/tests/reference_data/BeamformerFunctional.npy +0 -0
  66. acoular/tests/reference_data/BeamformerGIB.npy +0 -0
  67. acoular/tests/reference_data/BeamformerGridlessOrth.npy +0 -0
  68. acoular/tests/reference_data/BeamformerMusic.npy +0 -0
  69. acoular/tests/reference_data/BeamformerOrth.npy +0 -0
  70. acoular/tests/reference_data/BeamformerSODIX.npy +0 -0
  71. acoular/tests/reference_data/BeamformerTime.npy +0 -0
  72. acoular/tests/reference_data/BeamformerTimeSq.npy +0 -0
  73. acoular/tests/reference_data/BeamformerTimeSqTraj.npy +0 -0
  74. acoular/tests/reference_data/BeamformerTimeTraj.npy +0 -0
  75. acoular/tests/reference_data/Environment.npy +0 -0
  76. acoular/tests/reference_data/Example1_numerical_values_testsum.h5 +0 -0
  77. acoular/tests/reference_data/FiltFiltOctave__.npy +0 -0
  78. acoular/tests/reference_data/FiltFiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
  79. acoular/tests/reference_data/FiltFreqWeight_weight_A_.npy +0 -0
  80. acoular/tests/reference_data/FiltFreqWeight_weight_C_.npy +0 -0
  81. acoular/tests/reference_data/FiltFreqWeight_weight_Z_.npy +0 -0
  82. acoular/tests/reference_data/FiltOctave__.npy +0 -0
  83. acoular/tests/reference_data/FiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
  84. acoular/tests/reference_data/Filter__.npy +0 -0
  85. acoular/tests/reference_data/GeneralFlowEnvironment.npy +0 -0
  86. acoular/tests/reference_data/OctaveFilterBank__.npy +0 -0
  87. acoular/tests/reference_data/OpenJet.npy +0 -0
  88. acoular/tests/reference_data/PointSource.npy +0 -0
  89. acoular/tests/reference_data/PowerSpectra_csm.npy +0 -0
  90. acoular/tests/reference_data/PowerSpectra_ev.npy +0 -0
  91. acoular/tests/reference_data/RotatingFlow.npy +0 -0
  92. acoular/tests/reference_data/SlotJet.npy +0 -0
  93. acoular/tests/reference_data/TimeAverage__.npy +0 -0
  94. acoular/tests/reference_data/TimeCumAverage__.npy +0 -0
  95. acoular/tests/reference_data/TimeExpAverage_weight_F_.npy +0 -0
  96. acoular/tests/reference_data/TimeExpAverage_weight_I_.npy +0 -0
  97. acoular/tests/reference_data/TimeExpAverage_weight_S_.npy +0 -0
  98. acoular/tests/reference_data/TimeInOut__.npy +0 -0
  99. acoular/tests/reference_data/TimePower__.npy +0 -0
  100. acoular/tests/reference_data/TimeReverse__.npy +0 -0
  101. acoular/tests/reference_data/UniformFlowEnvironment.npy +0 -0
  102. acoular/tests/reference_data/beamformer_traj_time_data.h5 +0 -0
  103. acoular/tests/run_tests.sh +0 -18
  104. acoular/tests/run_tests_osx.sh +0 -16
  105. acoular/tests/test.npy +0 -0
  106. acoular/tests/test_beamformer_results.py +0 -213
  107. acoular/tests/test_classes.py +0 -60
  108. acoular/tests/test_digest.py +0 -125
  109. acoular/tests/test_environments.py +0 -73
  110. acoular/tests/test_example1.py +0 -124
  111. acoular/tests/test_grid.py +0 -92
  112. acoular/tests/test_integrate.py +0 -102
  113. acoular/tests/test_signals.py +0 -60
  114. acoular/tests/test_sources.py +0 -65
  115. acoular/tests/test_spectra.py +0 -38
  116. acoular/tests/test_timecache.py +0 -35
  117. acoular/tests/test_tprocess.py +0 -90
  118. acoular/tests/test_traj_beamformer_results.py +0 -164
  119. acoular/tests/unsupported/SpeedComparison/OvernightTestcasesBeamformer_nMics32_nGridPoints100_nFreqs4_nTrials10.png +0 -0
  120. acoular/tests/unsupported/SpeedComparison/cythonBeamformer.pyx +0 -237
  121. acoular/tests/unsupported/SpeedComparison/mainForCython.py +0 -103
  122. acoular/tests/unsupported/SpeedComparison/mainForParallelJit.py +0 -143
  123. acoular/tests/unsupported/SpeedComparison/setupCythonOpenMP.py +0 -63
  124. acoular/tests/unsupported/SpeedComparison/sharedFunctions.py +0 -153
  125. acoular/tests/unsupported/SpeedComparison/timeOverNMics_AllImportantMethods.png +0 -0
  126. acoular/tests/unsupported/SpeedComparison/timeOverNMics_faverage.png +0 -0
  127. acoular/tests/unsupported/SpeedComparison/vglOptimierungFAverage.py +0 -204
  128. acoular/tests/unsupported/SpeedComparison/vglOptimierungGaussSeidel.py +0 -182
  129. acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAMFULL_INVERSE.py +0 -764
  130. acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAM_OS.py +0 -231
  131. acoular/tests/unsupported/SpeedComparison/whatsFastestWayFor_absASquared.py +0 -48
  132. acoular/tests/unsupported/functionalBeamformer.py +0 -123
  133. acoular/tests/unsupported/precisionTest.py +0 -153
  134. acoular/tests/unsupported/validationOfBeamformerFuncsPOSTAcoularIntegration.py +0 -254
  135. acoular/tests/unsupported/validationOfBeamformerFuncsPREeAcoularIntegration.py +0 -531
  136. acoular/tools.py +0 -422
  137. acoular-24.3.dist-info/RECORD +0 -148
  138. acoular-24.3.dist-info/licenses/LICENSE +0 -29
  139. {acoular-24.3.dist-info → acoular-24.7.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/sources.py CHANGED
@@ -1,9 +1,7 @@
1
- # -*- coding: utf-8 -*-
2
- #pylint: disable-msg=E0611, E1101, C0103, R0901, R0902, R0903, R0904, W0232
3
- #------------------------------------------------------------------------------
1
+ # ------------------------------------------------------------------------------
4
2
  # Copyright (c) Acoular Development Team.
5
- #------------------------------------------------------------------------------
6
- """Measured multichannel data managment and simulation of acoustic sources.
3
+ # ------------------------------------------------------------------------------
4
+ """Measured multichannel data management and simulation of acoustic sources.
7
5
 
8
6
  .. autosummary::
9
7
  :toctree: generated/
@@ -24,343 +22,505 @@
24
22
 
25
23
  # imports from other packages
26
24
 
27
- from numpy import array, sqrt, ones, empty, newaxis, uint32, arange, dot, int64 ,real, pi, tile,\
28
- cross, zeros, ceil, repeat
29
- from numpy import min as npmin
30
- from numpy import any as npany
31
-
32
- from numpy.fft import ifft, fft
33
- from numpy.linalg import norm
34
-
35
- import numba as nb
36
-
37
- from traits.api import Float, Int, Property, Trait, Delegate, \
38
- cached_property, Tuple, CLong, File, Instance, Any, Str, \
39
- on_trait_change, observe, List, ListInt, CArray, Bool, Dict, Enum
25
+ import contextlib
40
26
  from os import path
41
27
  from warnings import warn
42
28
 
29
+ import numba as nb
30
+ from numpy import any as npany
31
+ from numpy import (
32
+ arange,
33
+ arctan2,
34
+ array,
35
+ ceil,
36
+ complex128,
37
+ cross,
38
+ dot,
39
+ empty,
40
+ int64,
41
+ mod,
42
+ newaxis,
43
+ ones,
44
+ pi,
45
+ real,
46
+ repeat,
47
+ sqrt,
48
+ tile,
49
+ uint32,
50
+ zeros,
51
+ )
52
+ from numpy import min as npmin
53
+ from numpy.fft import fft, ifft
54
+ from numpy.linalg import norm
55
+ from scipy.special import sph_harm, spherical_jn, spherical_yn
56
+ from traits.api import (
57
+ Any,
58
+ Bool,
59
+ CArray,
60
+ CLong,
61
+ Delegate,
62
+ Dict,
63
+ Enum,
64
+ File,
65
+ Float,
66
+ Instance,
67
+ Int,
68
+ List,
69
+ ListInt,
70
+ Property,
71
+ Str,
72
+ Trait,
73
+ Tuple,
74
+ cached_property,
75
+ observe,
76
+ on_trait_change,
77
+ )
78
+
43
79
  # acoular imports
44
80
  from .calib import Calib
45
- from .trajectory import Trajectory
81
+ from .environments import Environment
82
+ from .h5files import H5FileBase, _get_h5file_class
46
83
  from .internal import digest, ldigest
47
84
  from .microphones import MicGeom
48
- from .environments import Environment
49
- from .tprocess import SamplesGenerator, TimeConvolve
50
85
  from .signals import SignalGenerator
51
- from .h5files import H5FileBase, _get_h5file_class
52
- from .tools import get_modes
86
+ from .tprocess import SamplesGenerator, TimeConvolve
87
+ from .trajectory import Trajectory
53
88
 
54
89
 
55
- @nb.njit(cache=True, error_model="numpy") # jit with nopython
56
- def _fill_mic_signal_block(out,signal,rm,ind,blocksize,numchannels,up,prepadding):
90
+ @nb.njit(cache=True, error_model='numpy') # jit with nopython
91
+ def _fill_mic_signal_block(out, signal, rm, ind, blocksize, numchannels, up, prepadding):
57
92
  if prepadding:
58
93
  for b in range(blocksize):
59
94
  for m in range(numchannels):
60
- if ind[0,m]<0:
61
- out[b,m] = 0
95
+ if ind[0, m] < 0:
96
+ out[b, m] = 0
62
97
  else:
63
- out[b,m] = signal[int(0.5+ind[0,m])]/rm[0,m]
98
+ out[b, m] = signal[int(0.5 + ind[0, m])] / rm[0, m]
64
99
  ind += up
65
100
  else:
66
101
  for b in range(blocksize):
67
102
  for m in range(numchannels):
68
- out[b,m] = signal[int(0.5+ind[0,m])]/rm[0,m]
103
+ out[b, m] = signal[int(0.5 + ind[0, m])] / rm[0, m]
69
104
  ind += up
70
105
  return out
71
106
 
72
107
 
73
- class TimeSamples( SamplesGenerator ):
108
+ def spherical_hn1(n, z):
109
+ """Spherical Hankel Function of the First Kind."""
110
+ return spherical_jn(n, z, derivative=False) + 1j * spherical_yn(n, z, derivative=False)
111
+
112
+
113
+ def get_radiation_angles(direction, mpos, sourceposition):
114
+ """Returns azimuthal and elevation angles between the mics and the source.
115
+
116
+ Parameters
117
+ ----------
118
+ direction : array of floats
119
+ Spherical Harmonic orientation
120
+ mpos : array of floats
121
+ x, y, z position of microphones
122
+ sourceposition : array of floats
123
+ position of the source
124
+
125
+ Returns
126
+ -------
127
+ azi, ele : array of floats
128
+ the angle between the mics and the source
129
+
130
+ """
131
+ # direction of the Spherical Harmonics
132
+ direc = array(direction, dtype=float)
133
+ direc = direc / norm(direc)
134
+ # distances
135
+ source_to_mic_vecs = mpos - array(sourceposition).reshape((3, 1))
136
+ source_to_mic_vecs[2] *= -1 # invert z-axis (acoular) #-1
137
+ # z-axis (acoular) -> y-axis (spherical)
138
+ # y-axis (acoular) -> z-axis (spherical)
139
+ # theta
140
+ ele = arctan2(sqrt(source_to_mic_vecs[0] ** 2 + source_to_mic_vecs[2] ** 2), source_to_mic_vecs[1])
141
+ ele += arctan2(sqrt(direc[0] ** 2 + direc[2] ** 2), direc[1])
142
+ ele += pi * 0.5 # convert from [-pi/2, pi/2] to [0,pi] range
143
+ # phi
144
+ azi = arctan2(source_to_mic_vecs[2], source_to_mic_vecs[0])
145
+ azi += arctan2(direc[2], direc[0])
146
+ azi = mod(azi, 2 * pi)
147
+ return azi, ele
148
+
149
+
150
+ def get_modes(lOrder, direction, mpos, sourceposition=None): # noqa: N803
151
+ """Returns Spherical Harmonic Radiation Pattern at the Microphones.
152
+
153
+ Parameters
154
+ ----------
155
+ lOrder : int
156
+ Maximal order of spherical harmonic
157
+ direction : array of floats
158
+ Spherical Harmonic orientation
159
+ mpos : array of floats
160
+ x, y, z position of microphones
161
+ sourceposition : array of floats
162
+ position of the source
163
+
164
+ Returns
165
+ -------
166
+ modes : array of floats
167
+ the radiation values at each microphone for each mode
168
+
74
169
  """
75
- Container for time data in `*.h5` format.
76
-
77
- This class loads measured data from h5 files and
78
- and provides information about this data.
79
- It also serves as an interface where the data can be accessed
80
- (e.g. for use in a block chain) via the :meth:`result` generator.
170
+ sourceposition = sourceposition if sourceposition is not None else array([0, 0, 0])
171
+ azi, ele = get_radiation_angles(direction, mpos, sourceposition) # angles between source and mics
172
+ modes = zeros((azi.shape[0], (lOrder + 1) ** 2), dtype=complex128)
173
+ i = 0
174
+ for lidx in range(lOrder + 1):
175
+ for m in range(-lidx, lidx + 1):
176
+ modes[:, i] = sph_harm(m, lidx, azi, ele)
177
+ if m < 0:
178
+ modes[:, i] = modes[:, i].conj() * 1j
179
+ i += 1
180
+ return modes
181
+
182
+
183
+ class TimeSamples(SamplesGenerator):
184
+ """Container for processing time data in `*.h5` or NumPy array format.
185
+
186
+ This class loads measured data from HDF5 files and provides information about this data.
187
+ It also serves as an interface where the data can be accessed (e.g. for use in a block chain) via the
188
+ :meth:`result` generator.
189
+
190
+ Examples
191
+ --------
192
+ Data can be loaded from a HDF5 file as follows:
193
+
194
+ >>> from acoular import TimeSamples
195
+ >>> name = <some_h5_file.h5> # doctest: +SKIP
196
+ >>> ts = TimeSamples(name=name) # doctest: +SKIP
197
+ >>> print(f'number of channels: {ts.numchannels}') # doctest: +SKIP
198
+ number of channels: 56 # doctest: +SKIP
199
+
200
+ Alternatively, the time data can be specified directly as a numpy array.
201
+ In this case, the :attr:`data` and :attr:`sample_freq` attributes must be set manually.
202
+
203
+ >>> import numpy as np
204
+ >>> data = np.random.rand(1000, 4)
205
+ >>> ts = TimeSamples(data=data, sample_freq=51200)
206
+
207
+ Chunks of the time data can be accessed iteratively via the :meth:`result` generator.
208
+ The last block will be shorter than the block size if the number of samples is not a multiple of the block size.
209
+
210
+ >>> blocksize = 512
211
+ >>> generator = ts.result(num=blocksize)
212
+ >>> for block in generator:
213
+ ... print(block.shape)
214
+ (512, 4)
215
+ (488, 4)
216
+
217
+ See Also
218
+ --------
219
+ acoular.sources.MaskedTimeSamples :
220
+ Extends the functionality of class :class:`TimeSamples` by enabling the definition of start and stop samples
221
+ as well as the specification of invalid channels.
81
222
  """
82
223
 
83
224
  #: Full name of the .h5 file with data.
84
- name = File(filter=['*.h5'],
85
- desc="name of data file")
225
+ name = File(filter=['*.h5'], desc='name of data file')
86
226
 
87
227
  #: Basename of the .h5 file with data, is set automatically.
88
- basename = Property( depends_on = 'name', #filter=['*.h5'],
89
- desc="basename of data file")
90
-
228
+ basename = Property(
229
+ depends_on='name', # filter=['*.h5'],
230
+ desc='basename of data file',
231
+ )
232
+
91
233
  #: Calibration data, instance of :class:`~acoular.calib.Calib` class, optional .
92
- calib = Trait( Calib,
93
- desc="Calibration data")
94
-
234
+ calib = Trait(Calib, desc='Calibration data')
235
+
95
236
  #: Number of channels, is set automatically / read from file.
96
- numchannels = CLong(0,
97
- desc="number of input channels")
237
+ numchannels = CLong(0, desc='number of input channels')
98
238
 
99
239
  #: Number of time data samples, is set automatically / read from file.
100
- numsamples = CLong(0,
101
- desc="number of samples")
240
+ numsamples = CLong(0, desc='number of samples')
102
241
 
103
242
  #: The time data as array of floats with dimension (numsamples, numchannels).
104
- data = Any( transient = True,
105
- desc="the actual time data array")
243
+ data = Any(transient=True, desc='the actual time data array')
106
244
 
107
245
  #: HDF5 file object
108
- h5f = Instance(H5FileBase, transient = True)
109
-
246
+ h5f = Instance(H5FileBase, transient=True)
247
+
110
248
  #: Provides metadata stored in HDF5 file object
111
- metadata = Dict(
112
- desc="metadata contained in .h5 file")
113
-
249
+ metadata = Dict(desc='metadata contained in .h5 file')
250
+
114
251
  # Checksum over first data entries of all channels
115
252
  _datachecksum = Property()
116
-
253
+
117
254
  # internal identifier
118
- digest = Property( depends_on = ['basename', 'calib.digest', '_datachecksum'])
255
+ digest = Property(depends_on=['basename', 'calib.digest', '_datachecksum'])
256
+
257
+ def _get__datachecksum(self):
258
+ return self.data[0, :].sum()
119
259
 
120
- def _get__datachecksum( self ):
121
- return self.data[0,:].sum()
122
-
123
260
  @cached_property
124
- def _get_digest( self ):
261
+ def _get_digest(self):
125
262
  return digest(self)
126
-
263
+
127
264
  @cached_property
128
- def _get_basename( self ):
265
+ def _get_basename(self):
129
266
  return path.splitext(path.basename(self.name))[0]
130
-
267
+
131
268
  @on_trait_change('basename')
132
- def load_data( self ):
133
- """
134
- Open the .h5 file and set attributes.
135
- """
269
+ def _load_data(self):
270
+ """Open the .h5 file and set attributes."""
136
271
  if not path.isfile(self.name):
137
- # no file there
138
- self.numsamples = 0
139
- self.numchannels = 0
140
272
  self.sample_freq = 0
141
- raise IOError("No such file: %s" % self.name)
142
- if self.h5f != None:
143
- try:
273
+ raise OSError('No such file: %s' % self.name)
274
+ if self.h5f is not None:
275
+ with contextlib.suppress(OSError):
144
276
  self.h5f.close()
145
- except IOError:
146
- pass
147
277
  file = _get_h5file_class()
148
278
  self.h5f = file(self.name)
149
- self.load_timedata()
150
- self.load_metadata()
151
-
152
- def load_timedata( self ):
153
- """ loads timedata from .h5 file. Only for internal use. """
154
- self.data = self.h5f.get_data_by_reference('time_data')
155
- self.sample_freq = self.h5f.get_node_attribute(self.data,'sample_freq')
156
- (self.numsamples, self.numchannels) = self.data.shape
157
-
158
- def load_metadata( self ):
159
- """ loads metadata from .h5 file. Only for internal use. """
279
+ self._load_timedata()
280
+ self._load_metadata()
281
+
282
+ @on_trait_change('data')
283
+ def _load_shapes(self):
284
+ """Set numchannels and numsamples from data."""
285
+ if self.data is not None:
286
+ self.numsamples, self.numchannels = self.data.shape
287
+
288
+ def _load_timedata(self):
289
+ """Loads timedata from .h5 file. Only for internal use."""
290
+ self.data = self.h5f.get_data_by_reference('time_data')
291
+ self.sample_freq = self.h5f.get_node_attribute(self.data, 'sample_freq')
292
+
293
+ def _load_metadata(self):
294
+ """Loads metadata from .h5 file. Only for internal use."""
160
295
  self.metadata = {}
161
296
  if '/metadata' in self.h5f:
162
- for nodename, nodedata in self.h5f.get_child_nodes('/metadata'):
163
- self.metadata[nodename] = nodedata
297
+ self.metadata = self.h5f.node_to_dict('/metadata')
164
298
 
165
299
  def result(self, num=128):
166
- """
167
- Python generator that yields the output block-wise.
168
-
300
+ """Python generator that yields the output block-wise.
301
+
302
+ Reads the time data either from a HDF5 file or from a numpy array given
303
+ by :attr:`data` and iteratively returns a block of size `num` samples.
304
+ Calibrated data is returned if a calibration object is given by :attr:`calib`.
305
+
169
306
  Parameters
170
307
  ----------
171
308
  num : integer, defaults to 128
172
309
  This parameter defines the size of the blocks to be yielded
173
- (i.e. the number of samples per block) .
174
-
175
- Returns
176
- -------
177
- Samples in blocks of shape (num, numchannels).
310
+ (i.e. the number of samples per block).
311
+
312
+ Yields
313
+ ------
314
+ numpy.ndarray
315
+ Samples in blocks of shape (num, numchannels).
178
316
  The last block may be shorter than num.
317
+
179
318
  """
180
319
  if self.numsamples == 0:
181
- raise IOError("no samples available")
182
- self._datachecksum # trigger checksum calculation
320
+ msg = 'no samples available'
321
+ raise OSError(msg)
322
+ self._datachecksum # trigger checksum calculation # noqa: B018
183
323
  i = 0
184
324
  if self.calib:
185
325
  if self.calib.num_mics == self.numchannels:
186
326
  cal_factor = self.calib.data[newaxis]
187
327
  else:
188
- raise ValueError("calibration data not compatible: %i, %i" % \
189
- (self.calib.num_mics, self.numchannels))
328
+ raise ValueError('calibration data not compatible: %i, %i' % (self.calib.num_mics, self.numchannels))
190
329
  while i < self.numsamples:
191
- yield self.data[i:i+num]*cal_factor
330
+ yield self.data[i : i + num] * cal_factor
192
331
  i += num
193
332
  else:
194
333
  while i < self.numsamples:
195
- yield self.data[i:i+num]
334
+ yield self.data[i : i + num]
196
335
  i += num
197
336
 
198
- class MaskedTimeSamples( TimeSamples ):
199
- """
200
- Container for time data in `*.h5` format.
201
-
202
- This class loads measured data from h5 files
203
- and provides information about this data.
204
- It supports storing information about (in)valid samples and (in)valid channels
205
- It also serves as an interface where the data can be accessed
206
- (e.g. for use in a block chain) via the :meth:`result` generator.
207
-
337
+
338
+ class MaskedTimeSamples(TimeSamples):
339
+ """Container for processing time data in `*.h5` or NumPy array format.
340
+
341
+ This class loads measured data from HDF5 files and provides information about this data.
342
+ It supports storing information about (in)valid samples and (in)valid channels and allows
343
+ to specify a start and stop index for the valid samples.
344
+ It also serves as an interface where the data can be accessed (e.g. for use in a block chain) via the
345
+ :meth:`result` generator.
346
+
347
+ Examples
348
+ --------
349
+ Data can be loaded from a HDF5 file and invalid channels can be specified as follows:
350
+
351
+ >>> from acoular import MaskedTimeSamples
352
+ >>> name = <some_h5_file.h5> # doctest: +SKIP
353
+ >>> ts = MaskedTimeSamples(name=name, invalid_channels=[0, 1]) # doctest: +SKIP
354
+ >>> print(f'number of valid channels: {ts.numchannels}') # doctest: +SKIP
355
+ number of valid channels: 54 # doctest: +SKIP
356
+
357
+ Alternatively, the time data can be specified directly as a numpy array.
358
+ In this case, the :attr:`data` and :attr:`sample_freq` attributes must be set manually.
359
+
360
+ >>> from acoular import MaskedTimeSamples
361
+ >>> import numpy as np
362
+ >>> data = np.random.rand(1000, 4)
363
+ >>> ts = MaskedTimeSamples(data=data, sample_freq=51200)
364
+
365
+ Chunks of the time data can be accessed iteratively via the :meth:`result` generator:
366
+
367
+ >>> blocksize = 512
368
+ >>> generator = ts.result(num=blocksize)
369
+ >>> for block in generator:
370
+ ... print(block.shape)
371
+ (512, 4)
372
+ (488, 4)
208
373
  """
209
-
374
+
210
375
  #: Index of the first sample to be considered valid.
211
- start = CLong(0,
212
- desc="start of valid samples")
213
-
376
+ start = CLong(0, desc='start of valid samples')
377
+
214
378
  #: Index of the last sample to be considered valid.
215
- stop = Trait(None, None, CLong,
216
- desc="stop of valid samples")
217
-
379
+ stop = Trait(None, None, CLong, desc='stop of valid samples')
380
+
218
381
  #: Channels that are to be treated as invalid.
219
- invalid_channels = ListInt(
220
- desc="list of invalid channels")
221
-
382
+ invalid_channels = ListInt(desc='list of invalid channels')
383
+
222
384
  #: Channel mask to serve as an index for all valid channels, is set automatically.
223
- channels = Property(depends_on = ['invalid_channels', 'numchannels_total'],
224
- desc="channel mask")
225
-
385
+ channels = Property(depends_on=['invalid_channels', 'numchannels_total'], desc='channel mask')
386
+
226
387
  #: Number of channels (including invalid channels), is set automatically.
227
- numchannels_total = CLong(0,
228
- desc="total number of input channels")
388
+ numchannels_total = CLong(0, desc='total number of input channels')
229
389
 
230
390
  #: Number of time data samples (including invalid samples), is set automatically.
231
- numsamples_total = CLong(0,
232
- desc="total number of samples per channel")
391
+ numsamples_total = CLong(0, desc='total number of samples per channel')
233
392
 
234
393
  #: Number of valid channels, is set automatically.
235
- numchannels = Property(depends_on = ['invalid_channels', \
236
- 'numchannels_total'], desc="number of valid input channels")
394
+ numchannels = Property(depends_on=['invalid_channels', 'numchannels_total'], desc='number of valid input channels')
237
395
 
238
396
  #: Number of valid time data samples, is set automatically.
239
- numsamples = Property(depends_on = ['start', 'stop', 'numsamples_total'],
240
- desc="number of valid samples per channel")
397
+ numsamples = Property(depends_on=['start', 'stop', 'numsamples_total'], desc='number of valid samples per channel')
241
398
 
242
399
  # internal identifier
243
- digest = Property( depends_on = ['basename', 'start', 'stop', \
244
- 'calib.digest', 'invalid_channels','_datachecksum'])
400
+ digest = Property(depends_on=['basename', 'start', 'stop', 'calib.digest', 'invalid_channels', '_datachecksum'])
245
401
 
246
402
  @cached_property
247
- def _get_digest( self ):
403
+ def _get_digest(self):
248
404
  return digest(self)
249
-
405
+
250
406
  @cached_property
251
- def _get_basename( self ):
407
+ def _get_basename(self):
252
408
  return path.splitext(path.basename(self.name))[0]
253
-
409
+
254
410
  @cached_property
255
- def _get_channels( self ):
256
- if len(self.invalid_channels)==0:
411
+ def _get_channels(self):
412
+ if len(self.invalid_channels) == 0:
257
413
  return slice(0, None, None)
258
- allr=[i for i in range(self.numchannels_total) if i not in self.invalid_channels]
414
+ allr = [i for i in range(self.numchannels_total) if i not in self.invalid_channels]
259
415
  return array(allr)
260
-
416
+
261
417
  @cached_property
262
- def _get_numchannels( self ):
263
- if len(self.invalid_channels)==0:
418
+ def _get_numchannels(self):
419
+ if len(self.invalid_channels) == 0:
264
420
  return self.numchannels_total
265
421
  return len(self.channels)
266
-
422
+
267
423
  @cached_property
268
- def _get_numsamples( self ):
424
+ def _get_numsamples(self):
269
425
  sli = slice(self.start, self.stop).indices(self.numsamples_total)
270
- return sli[1]-sli[0]
426
+ return sli[1] - sli[0]
271
427
 
272
428
  @on_trait_change('basename')
273
- def load_data( self ):
274
- #""" open the .h5 file and set attributes
275
- #"""
429
+ def _load_data(self):
430
+ # """ open the .h5 file and set attributes
431
+ # """
276
432
  if not path.isfile(self.name):
277
433
  # no file there
278
- self.numsamples_total = 0
279
- self.numchannels_total = 0
280
434
  self.sample_freq = 0
281
- raise IOError("No such file: %s" % self.name)
282
- if self.h5f != None:
283
- try:
435
+ raise OSError('No such file: %s' % self.name)
436
+ if self.h5f is not None:
437
+ with contextlib.suppress(OSError):
284
438
  self.h5f.close()
285
- except IOError:
286
- pass
287
439
  file = _get_h5file_class()
288
440
  self.h5f = file(self.name)
289
- self.load_timedata()
290
- self.load_metadata()
291
-
292
- def load_timedata( self ):
293
- """ loads timedata from .h5 file. Only for internal use. """
294
- self.data = self.h5f.get_data_by_reference('time_data')
295
- self.sample_freq = self.h5f.get_node_attribute(self.data,'sample_freq')
441
+ self._load_timedata()
442
+ self._load_metadata()
443
+
444
+ @on_trait_change('data')
445
+ def _load_shapes(self):
446
+ """Set numchannels and numsamples from data."""
447
+ if self.data is not None:
448
+ self.numsamples_total, self.numchannels_total = self.data.shape
449
+
450
+ def _load_timedata(self):
451
+ """Loads timedata from .h5 file. Only for internal use."""
452
+ self.data = self.h5f.get_data_by_reference('time_data')
453
+ self.sample_freq = self.h5f.get_node_attribute(self.data, 'sample_freq')
296
454
  (self.numsamples_total, self.numchannels_total) = self.data.shape
297
455
 
298
456
  def result(self, num=128):
299
- """
300
- Python generator that yields the output block-wise.
301
-
457
+ """Python generator that yields the output block-wise.
458
+
459
+ Reads the time data either from a HDF5 file or from a numpy array given
460
+ by :attr:`data` and iteratively returns a block of size `num` samples.
461
+ Calibrated data is returned if a calibration object is given by :attr:`calib`.
462
+
302
463
  Parameters
303
464
  ----------
304
465
  num : integer, defaults to 128
305
466
  This parameter defines the size of the blocks to be yielded
306
467
  (i.e. the number of samples per block).
307
-
308
- Returns
309
- -------
310
- Samples in blocks of shape (num, numchannels).
468
+
469
+ Yields
470
+ ------
471
+ numpy.ndarray
472
+ Samples in blocks of shape (num, numchannels).
311
473
  The last block may be shorter than num.
474
+
312
475
  """
313
476
  sli = slice(self.start, self.stop).indices(self.numsamples_total)
314
477
  i = sli[0]
315
478
  stop = sli[1]
316
479
  cal_factor = 1.0
317
480
  if i >= stop:
318
- raise IOError("no samples available")
319
- self._datachecksum # trigger checksum calculation
481
+ msg = 'no samples available'
482
+ raise OSError(msg)
483
+ self._datachecksum # trigger checksum calculation # noqa: B018
320
484
  if self.calib:
321
485
  if self.calib.num_mics == self.numchannels_total:
322
486
  cal_factor = self.calib.data[self.channels][newaxis]
323
487
  elif self.calib.num_mics == self.numchannels:
324
488
  cal_factor = self.calib.data[newaxis]
325
489
  elif self.calib.num_mics == 0:
326
- warn("No calibration data used.", Warning, stacklevel = 2)
490
+ warn('No calibration data used.', Warning, stacklevel=2)
327
491
  else:
328
- raise ValueError("calibration data not compatible: %i, %i" % \
329
- (self.calib.num_mics, self.numchannels))
492
+ raise ValueError('calibration data not compatible: %i, %i' % (self.calib.num_mics, self.numchannels))
330
493
  while i < stop:
331
- yield self.data[i:min(i+num, stop)][:, self.channels]*cal_factor
494
+ yield self.data[i : min(i + num, stop)][:, self.channels] * cal_factor
332
495
  i += num
333
496
 
334
497
 
335
- class PointSource( SamplesGenerator ):
336
- """
337
- Class to define a fixed point source with an arbitrary signal.
498
+ class PointSource(SamplesGenerator):
499
+ """Class to define a fixed point source with an arbitrary signal.
338
500
  This can be used in simulations.
339
-
501
+
340
502
  The output is being generated via the :meth:`result` generator.
341
503
  """
342
-
504
+
343
505
  #: Emitted signal, instance of the :class:`~acoular.signals.SignalGenerator` class.
344
506
  signal = Trait(SignalGenerator)
345
-
507
+
346
508
  #: Location of source in (`x`, `y`, `z`) coordinates (left-oriented system).
347
- loc = Tuple((0.0, 0.0, 1.0),
348
- desc="source location")
349
-
350
- #: Number of channels in output, is set automatically /
509
+ loc = Tuple((0.0, 0.0, 1.0), desc='source location')
510
+
511
+ #: Number of channels in output, is set automatically /
351
512
  #: depends on used microphone geometry.
352
513
  numchannels = Delegate('mics', 'num_mics')
353
514
 
354
515
  #: :class:`~acoular.microphones.MicGeom` object that provides the microphone locations.
355
- mics = Trait(MicGeom,
356
- desc="microphone geometry")
516
+ mics = Trait(MicGeom, desc='microphone geometry')
357
517
 
358
518
  def _validate_locations(self):
359
519
  dist = self.env._r(array(self.loc).reshape((3, 1)), self.mics.mpos)
360
520
  if npany(dist < 1e-7):
361
- warn("Source and microphone locations are identical.", Warning, stacklevel = 2)
362
-
363
- #: :class:`~acoular.environments.Environment` or derived object,
521
+ warn('Source and microphone locations are identical.', Warning, stacklevel=2)
522
+
523
+ #: :class:`~acoular.environments.Environment` or derived object,
364
524
  #: which provides information about the sound propagation in the medium.
365
525
  env = Trait(Environment(), Environment)
366
526
 
@@ -369,68 +529,73 @@ class PointSource( SamplesGenerator ):
369
529
  # Microphone locations.
370
530
  # Deprecated! Use :attr:`mics` trait instead.
371
531
  mpos = Property()
372
-
532
+
373
533
  def _get_mpos(self):
374
534
  return self.mics
375
-
535
+
376
536
  def _set_mpos(self, mpos):
377
- warn("Deprecated use of 'mpos' trait. ", Warning, stacklevel = 2)
537
+ warn("Deprecated use of 'mpos' trait. ", Warning, stacklevel=2)
378
538
  self.mics = mpos
379
-
539
+
380
540
  # The speed of sound.
381
- # Deprecated! Only kept for backwards compatibility.
541
+ # Deprecated! Only kept for backwards compatibility.
382
542
  # Now governed by :attr:`env` trait.
383
543
  c = Property()
384
-
544
+
385
545
  def _get_c(self):
386
546
  return self.env.c
387
-
547
+
388
548
  def _set_c(self, c):
389
- warn("Deprecated use of 'c' trait. ", Warning, stacklevel = 2)
549
+ warn("Deprecated use of 'c' trait. ", Warning, stacklevel=2)
390
550
  self.env.c = c
391
551
 
392
552
  # --- End of backwards compatibility traits --------------------------------------
393
-
553
+
394
554
  #: Start time of the signal in seconds, defaults to 0 s.
395
- start_t = Float(0.0,
396
- desc="signal start time")
397
-
398
- #: Start time of the data aquisition at microphones in seconds,
555
+ start_t = Float(0.0, desc='signal start time')
556
+
557
+ #: Start time of the data aquisition at microphones in seconds,
399
558
  #: defaults to 0 s.
400
- start = Float(0.0,
401
- desc="sample start time")
559
+ start = Float(0.0, desc='sample start time')
402
560
 
403
561
  #: Signal behaviour for negative time indices, i.e. if :attr:`start` < :attr:start_t.
404
562
  #: `loop` take values from the end of :attr:`signal.signal()` array.
405
563
  #: `zeros` set source signal to zero, advisable for deterministic signals.
406
564
  #: defaults to `loop`.
407
- prepadding = Trait('loop','zeros', desc="Behaviour for negative time indices.")
565
+ prepadding = Trait('loop', 'zeros', desc='Behaviour for negative time indices.')
408
566
 
409
567
  #: Upsampling factor, internal use, defaults to 16.
410
- up = Int(16,
411
- desc="upsampling factor")
412
-
413
- #: Number of samples, is set automatically /
568
+ up = Int(16, desc='upsampling factor')
569
+
570
+ #: Number of samples, is set automatically /
414
571
  #: depends on :attr:`signal`.
415
572
  numsamples = Delegate('signal')
416
-
417
- #: Sampling frequency of the signal, is set automatically /
573
+
574
+ #: Sampling frequency of the signal, is set automatically /
418
575
  #: depends on :attr:`signal`.
419
- sample_freq = Delegate('signal')
576
+ sample_freq = Delegate('signal')
420
577
 
421
578
  # internal identifier
422
- digest = Property(
423
- depends_on = ['mics.digest', 'signal.digest', 'loc', \
424
- 'env.digest', 'start_t', 'start', 'up', 'prepadding', '__class__'],
425
- )
426
-
579
+ digest = Property(
580
+ depends_on=[
581
+ 'mics.digest',
582
+ 'signal.digest',
583
+ 'loc',
584
+ 'env.digest',
585
+ 'start_t',
586
+ 'start',
587
+ 'up',
588
+ 'prepadding',
589
+ '__class__',
590
+ ],
591
+ )
592
+
427
593
  @cached_property
428
- def _get_digest( self ):
594
+ def _get_digest(self):
429
595
  return digest(self)
430
596
 
431
597
  def result(self, num=128):
432
- """
433
- Python generator that yields the output at microphones block-wise.
598
+ """Python generator that yields the output at microphones block-wise.
434
599
 
435
600
  Parameters
436
601
  ----------
@@ -440,542 +605,588 @@ class PointSource( SamplesGenerator ):
440
605
 
441
606
  Returns
442
607
  -------
443
- Samples in blocks of shape (num, numchannels).
608
+ Samples in blocks of shape (num, numchannels).
444
609
  The last block may be shorter than num.
610
+
445
611
  """
446
-
447
612
  self._validate_locations()
448
- N = int(ceil(self.numsamples/num)) # number of output blocks
613
+ N = int(ceil(self.numsamples / num)) # number of output blocks
449
614
  signal = self.signal.usignal(self.up)
450
615
  out = empty((num, self.numchannels))
451
616
  # distances
452
- rm = self.env._r(array(self.loc).reshape((3, 1)), self.mics.mpos).reshape(1,-1)
617
+ rm = self.env._r(array(self.loc).reshape((3, 1)), self.mics.mpos).reshape(1, -1)
453
618
  # emission time relative to start_t (in samples) for first sample
454
- ind = (-rm/self.env.c-self.start_t+self.start)*self.sample_freq*self.up
619
+ ind = (-rm / self.env.c - self.start_t + self.start) * self.sample_freq * self.up
455
620
 
456
621
  if self.prepadding == 'zeros':
457
622
  # number of blocks where signal behaviour is amended
458
- pre = -int(npmin(ind[0])//(self.up*num))
623
+ pre = -int(npmin(ind[0]) // (self.up * num))
459
624
  # amend signal for first blocks
460
625
  # if signal stops during prepadding, terminate
461
- if N <= pre:
462
- for nb in range(N-1):
463
- out = _fill_mic_signal_block(out,signal,rm,ind,num,self.numchannels,self.up,True)
626
+ if pre >= N:
627
+ for _nb in range(N - 1):
628
+ out = _fill_mic_signal_block(out, signal, rm, ind, num, self.numchannels, self.up, True)
464
629
  yield out
465
630
 
466
- blocksize = self.numsamples%num or num
467
- out = _fill_mic_signal_block(out,signal,rm,ind,blocksize,self.numchannels,self.up,True)
631
+ blocksize = self.numsamples % num or num
632
+ out = _fill_mic_signal_block(out, signal, rm, ind, blocksize, self.numchannels, self.up, True)
468
633
  yield out[:blocksize]
469
634
  return
470
635
  else:
471
- for nb in range(pre):
472
- out = _fill_mic_signal_block(out,signal,rm,ind,num,self.numchannels,self.up,True)
636
+ for _nb in range(pre):
637
+ out = _fill_mic_signal_block(out, signal, rm, ind, num, self.numchannels, self.up, True)
473
638
  yield out
474
639
 
475
640
  else:
476
641
  pre = 0
477
642
 
478
643
  # main generator
479
- for nb in range(N-pre-1):
480
- out = _fill_mic_signal_block(out,signal,rm,ind,num,self.numchannels,self.up,False)
644
+ for _nb in range(N - pre - 1):
645
+ out = _fill_mic_signal_block(out, signal, rm, ind, num, self.numchannels, self.up, False)
481
646
  yield out
482
647
 
483
648
  # last block of variable size
484
- blocksize = self.numsamples%num or num
485
- out = _fill_mic_signal_block(out,signal,rm,ind,blocksize,self.numchannels,self.up,False)
649
+ blocksize = self.numsamples % num or num
650
+ out = _fill_mic_signal_block(out, signal, rm, ind, blocksize, self.numchannels, self.up, False)
486
651
  yield out[:blocksize]
487
652
 
488
653
 
489
- class SphericalHarmonicSource( PointSource ):
490
- """
491
- Class to define a fixed Spherical Harmonic Source with an arbitrary signal.
654
+ class SphericalHarmonicSource(PointSource):
655
+ """Class to define a fixed Spherical Harmonic Source with an arbitrary signal.
492
656
  This can be used in simulations.
493
-
657
+
494
658
  The output is being generated via the :meth:`result` generator.
495
659
  """
496
-
660
+
497
661
  #: Order of spherical harmonic source
498
- lOrder = Int(0,
499
- desc ="Order of spherical harmonic")
500
-
501
- alpha = CArray(desc="coefficients of the (lOrder,) spherical harmonic mode")
502
-
503
-
504
- #: Vector to define the orientation of the SphericalHarmonic.
505
- direction = Tuple((1.0, 0.0, 0.0),
506
- desc="Spherical Harmonic orientation")
507
-
508
- prepadding = Enum('loop', desc="Behaviour for negative time indices.")
509
-
662
+ lOrder = Int(0, desc='Order of spherical harmonic') # noqa: N815
663
+
664
+ alpha = CArray(desc='coefficients of the (lOrder,) spherical harmonic mode')
665
+
666
+ #: Vector to define the orientation of the SphericalHarmonic.
667
+ direction = Tuple((1.0, 0.0, 0.0), desc='Spherical Harmonic orientation')
668
+
669
+ prepadding = Enum('loop', desc='Behaviour for negative time indices.')
670
+
510
671
  # internal identifier
511
- digest = Property(
512
- depends_on = ['mics.digest', 'signal.digest', 'loc', \
513
- 'env.digest', 'start_t', 'start', 'up', '__class__', 'alpha','lOrder', 'prepadding'],
514
- )
515
-
672
+ digest = Property(
673
+ depends_on=[
674
+ 'mics.digest',
675
+ 'signal.digest',
676
+ 'loc',
677
+ 'env.digest',
678
+ 'start_t',
679
+ 'start',
680
+ 'up',
681
+ '__class__',
682
+ 'alpha',
683
+ 'lOrder',
684
+ 'prepadding',
685
+ ],
686
+ )
687
+
516
688
  @cached_property
517
- def _get_digest( self ):
689
+ def _get_digest(self):
518
690
  return digest(self)
519
691
 
520
- def transform(self,signals):
521
- Y_lm = get_modes(lOrder = self.lOrder, direction= self.direction, mpos = self.mics.mpos,sourceposition = array(self.loc))
522
- return real(ifft(fft(signals,axis=0) * (Y_lm @ self.alpha),axis=0))
692
+ def transform(self, signals):
693
+ Y_lm = get_modes(
694
+ lOrder=self.lOrder,
695
+ direction=self.direction,
696
+ mpos=self.mics.mpos,
697
+ sourceposition=array(self.loc),
698
+ )
699
+ return real(ifft(fft(signals, axis=0) * (Y_lm @ self.alpha), axis=0))
523
700
 
524
701
  def result(self, num=128):
525
- """
526
- Python generator that yields the output at microphones block-wise.
527
-
702
+ """Python generator that yields the output at microphones block-wise.
703
+
528
704
  Parameters
529
705
  ----------
530
706
  num : integer, defaults to 128
531
707
  This parameter defines the size of the blocks to be yielded
532
708
  (i.e. the number of samples per block) .
533
-
709
+
534
710
  Returns
535
711
  -------
536
- Samples in blocks of shape (num, numchannels).
712
+ Samples in blocks of shape (num, numchannels).
537
713
  The last block may be shorter than num.
714
+
538
715
  """
539
- #If signal samples are needed for te < t_start, then samples are taken
540
- #from the end of the calculated signal.
541
-
716
+ # If signal samples are needed for te < t_start, then samples are taken
717
+ # from the end of the calculated signal.
718
+
542
719
  signal = self.signal.usignal(self.up)
543
720
  # emission time relative to start_t (in samples) for first sample
544
721
  rm = self.env._r(array(self.loc).reshape((3, 1)), self.mics.mpos)
545
- ind = (-rm/self.env.c-self.start_t+self.start)*self.sample_freq + pi/30
722
+ ind = (-rm / self.env.c - self.start_t + self.start) * self.sample_freq + pi / 30
546
723
  i = 0
547
724
  n = self.numsamples
548
725
  out = empty((num, self.numchannels))
549
726
  while n:
550
727
  n -= 1
551
728
  try:
552
- out[i] = signal[array(0.5+ind*self.up, dtype=int64)]/rm
729
+ out[i] = signal[array(0.5 + ind * self.up, dtype=int64)] / rm
553
730
  ind += 1
554
731
  i += 1
555
732
  if i == num:
556
733
  yield self.transform(out)
557
734
  i = 0
558
- except IndexError: #if no more samples available from the source
735
+ except IndexError: # if no more samples available from the source
559
736
  break
560
- if i > 0: # if there are still samples to yield
737
+ if i > 0: # if there are still samples to yield
561
738
  yield self.transform(out[:i])
562
-
563
- class MovingPointSource( PointSource ):
564
- """
565
- Class to define a point source with an arbitrary
739
+
740
+
741
+ class MovingPointSource(PointSource):
742
+ """Class to define a point source with an arbitrary
566
743
  signal moving along a given trajectory.
567
744
  This can be used in simulations.
568
-
745
+
569
746
  The output is being generated via the :meth:`result` generator.
570
747
  """
571
748
 
572
749
  #: Considering of convective amplification
573
- conv_amp = Bool(False,
574
- desc="determines if convective amplification is considered")
750
+ conv_amp = Bool(False, desc='determines if convective amplification is considered')
575
751
 
576
- #: Trajectory of the source,
752
+ #: Trajectory of the source,
577
753
  #: instance of the :class:`~acoular.trajectory.Trajectory` class.
578
754
  #: The start time is assumed to be the same as for the samples.
579
- trajectory = Trait(Trajectory,
580
- desc="trajectory of the source")
755
+ trajectory = Trait(Trajectory, desc='trajectory of the source')
581
756
 
582
- prepadding = Enum('loop', desc="Behaviour for negative time indices.")
757
+ prepadding = Enum('loop', desc='Behaviour for negative time indices.')
583
758
 
584
759
  # internal identifier
585
- digest = Property(
586
- depends_on = ['mics.digest', 'signal.digest', 'loc', 'conv_amp', \
587
- 'env.digest', 'start_t', 'start', 'trajectory.digest', 'prepadding', '__class__'],
588
- )
589
-
760
+ digest = Property(
761
+ depends_on=[
762
+ 'mics.digest',
763
+ 'signal.digest',
764
+ 'loc',
765
+ 'conv_amp',
766
+ 'env.digest',
767
+ 'start_t',
768
+ 'start',
769
+ 'trajectory.digest',
770
+ 'prepadding',
771
+ '__class__',
772
+ ],
773
+ )
774
+
590
775
  @cached_property
591
- def _get_digest( self ):
776
+ def _get_digest(self):
592
777
  return digest(self)
593
778
 
594
779
  def result(self, num=128):
595
- """
596
- Python generator that yields the output at microphones block-wise.
597
-
780
+ """Python generator that yields the output at microphones block-wise.
781
+
598
782
  Parameters
599
783
  ----------
600
784
  num : integer, defaults to 128
601
785
  This parameter defines the size of the blocks to be yielded
602
786
  (i.e. the number of samples per block).
603
-
787
+
604
788
  Returns
605
789
  -------
606
- Samples in blocks of shape (num, numchannels).
790
+ Samples in blocks of shape (num, numchannels).
607
791
  The last block may be shorter than num.
608
- """
609
- #If signal samples are needed for te < t_start, then samples are taken
610
- #from the end of the calculated signal.
611
-
792
+
793
+ """
794
+ # If signal samples are needed for te < t_start, then samples are taken
795
+ # from the end of the calculated signal.
796
+
612
797
  signal = self.signal.usignal(self.up)
613
798
  out = empty((num, self.numchannels))
614
799
  # shortcuts and intial values
615
800
  m = self.mics
616
- t = self.start*ones(m.num_mics)
801
+ t = self.start * ones(m.num_mics)
617
802
  i = 0
618
- epslim = 0.1/self.up/self.sample_freq
803
+ epslim = 0.1 / self.up / self.sample_freq
619
804
  c0 = self.env.c
620
805
  tr = self.trajectory
621
806
  n = self.numsamples
622
807
  while n:
623
808
  n -= 1
624
809
  eps = ones(m.num_mics)
625
- te = t.copy() # init emission time = receiving time
810
+ te = t.copy() # init emission time = receiving time
626
811
  j = 0
627
812
  # Newton-Rhapson iteration
628
- while abs(eps).max()>epslim and j<100:
813
+ while abs(eps).max() > epslim and j < 100:
629
814
  loc = array(tr.location(te))
630
- rm = loc-m.mpos# distance vectors to microphones
631
- rm = sqrt((rm*rm).sum(0))# absolute distance
632
- loc /= sqrt((loc*loc).sum(0))# distance unit vector
815
+ rm = loc - m.mpos # distance vectors to microphones
816
+ rm = sqrt((rm * rm).sum(0)) # absolute distance
817
+ loc /= sqrt((loc * loc).sum(0)) # distance unit vector
633
818
  der = array(tr.location(te, der=1))
634
- Mr = (der*loc).sum(0)/c0# radial Mach number
635
- eps = (te + rm/c0 - t)/(1+Mr)# discrepancy in time
819
+ Mr = (der * loc).sum(0) / c0 # radial Mach number
820
+ eps = (te + rm / c0 - t) / (1 + Mr) # discrepancy in time
636
821
  te -= eps
637
- j += 1 #iteration count
638
- t += 1./self.sample_freq
822
+ j += 1 # iteration count
823
+ t += 1.0 / self.sample_freq
639
824
  # emission time relative to start time
640
- ind = (te-self.start_t+self.start)*self.sample_freq
641
- if self.conv_amp: rm *= (1-Mr)**2
825
+ ind = (te - self.start_t + self.start) * self.sample_freq
826
+ if self.conv_amp:
827
+ rm *= (1 - Mr) ** 2
642
828
  try:
643
- out[i] = signal[array(0.5+ind*self.up, dtype=int64)]/rm
829
+ out[i] = signal[array(0.5 + ind * self.up, dtype=int64)] / rm
644
830
  i += 1
645
831
  if i == num:
646
832
  yield out
647
833
  i = 0
648
- except IndexError: #if no more samples available from the source
834
+ except IndexError: # if no more samples available from the source
649
835
  break
650
- if i > 0: # if there are still samples to yield
836
+ if i > 0: # if there are still samples to yield
651
837
  yield out[:i]
652
-
653
838
 
654
- class PointSourceDipole ( PointSource ):
655
- """
656
- Class to define a fixed point source with an arbitrary signal and
839
+
840
+ class PointSourceDipole(PointSource):
841
+ """Class to define a fixed point source with an arbitrary signal and
657
842
  dipole characteristics via superposition of two nearby inversely
658
843
  phased monopoles.
659
844
  This can be used in simulations.
660
-
845
+
661
846
  The output is being generated via the :meth:`result` generator.
662
847
  """
663
-
848
+
664
849
  #: Vector to define the orientation of the dipole lobes. Its magnitude
665
850
  #: governs the distance between the monopoles
666
851
  #: (dist = [lowest wavelength in spectrum] x [magnitude] x 1e-5).
667
- #: Note: Use vectors with order of magnitude around 1.0 or less
852
+ #: Note: Use vectors with order of magnitude around 1.0 or less
668
853
  #: for good results.
669
- direction = Tuple((0.0, 0.0, 1.0),
670
- desc="dipole orientation and distance of the inversely phased monopoles")
854
+ direction = Tuple((0.0, 0.0, 1.0), desc='dipole orientation and distance of the inversely phased monopoles')
855
+
856
+ prepadding = Enum('loop', desc='Behaviour for negative time indices.')
671
857
 
672
- prepadding = Enum('loop', desc="Behaviour for negative time indices.")
673
-
674
858
  # internal identifier
675
- digest = Property(
676
- depends_on = ['mics.digest', 'signal.digest', 'loc', \
677
- 'env.digest', 'start_t', 'start', 'up', 'direction', 'prepadding', '__class__'],
678
- )
679
-
859
+ digest = Property(
860
+ depends_on=[
861
+ 'mics.digest',
862
+ 'signal.digest',
863
+ 'loc',
864
+ 'env.digest',
865
+ 'start_t',
866
+ 'start',
867
+ 'up',
868
+ 'direction',
869
+ 'prepadding',
870
+ '__class__',
871
+ ],
872
+ )
873
+
680
874
  @cached_property
681
- def _get_digest( self ):
875
+ def _get_digest(self):
682
876
  return digest(self)
683
-
684
-
877
+
685
878
  def result(self, num=128):
686
- """
687
- Python generator that yields the output at microphones block-wise.
688
-
879
+ """Python generator that yields the output at microphones block-wise.
880
+
689
881
  Parameters
690
882
  ----------
691
883
  num : integer, defaults to 128
692
884
  This parameter defines the size of the blocks to be yielded
693
885
  (i.e. the number of samples per block) .
694
-
886
+
695
887
  Returns
696
888
  -------
697
- Samples in blocks of shape (num, numchannels).
889
+ Samples in blocks of shape (num, numchannels).
698
890
  The last block may be shorter than num.
891
+
699
892
  """
700
- #If signal samples are needed for te < t_start, then samples are taken
701
- #from the end of the calculated signal.
702
-
893
+ # If signal samples are needed for te < t_start, then samples are taken
894
+ # from the end of the calculated signal.
895
+
703
896
  mpos = self.mics.mpos
704
897
  # position of the dipole as (3,1) vector
705
- loc = array(self.loc, dtype = float).reshape((3, 1))
898
+ loc = array(self.loc, dtype=float).reshape((3, 1))
706
899
  # direction vector from tuple
707
- direc = array(self.direction, dtype = float) * 1e-5
708
- direc_mag = sqrt(dot(direc,direc))
709
-
900
+ direc = array(self.direction, dtype=float) * 1e-5
901
+ direc_mag = sqrt(dot(direc, direc))
902
+
710
903
  # normed direction vector
711
904
  direc_n = direc / direc_mag
712
-
905
+
713
906
  c = self.env.c
714
-
907
+
715
908
  # distance between monopoles as function of c, sample freq, direction vector
716
909
  dist = c / self.sample_freq * direc_mag
717
-
910
+
718
911
  # vector from dipole center to one of the monopoles
719
912
  dir2 = (direc_n * dist / 2.0).reshape((3, 1))
720
-
913
+
721
914
  signal = self.signal.usignal(self.up)
722
915
  out = empty((num, self.numchannels))
723
-
916
+
724
917
  # distance from dipole center to microphones
725
918
  rm = self.env._r(loc, mpos)
726
-
919
+
727
920
  # distances from monopoles to microphones
728
921
  rm1 = self.env._r(loc + dir2, mpos)
729
922
  rm2 = self.env._r(loc - dir2, mpos)
730
-
923
+
731
924
  # emission time relative to start_t (in samples) for first sample
732
- ind1 = (-rm1 / c - self.start_t + self.start) * self.sample_freq
925
+ ind1 = (-rm1 / c - self.start_t + self.start) * self.sample_freq
733
926
  ind2 = (-rm2 / c - self.start_t + self.start) * self.sample_freq
734
-
927
+
735
928
  i = 0
736
- n = self.numsamples
929
+ n = self.numsamples
737
930
  while n:
738
931
  n -= 1
739
932
  try:
740
933
  # subtract the second signal b/c of phase inversion
741
- out[i] = rm / dist * \
742
- (signal[array(0.5 + ind1 * self.up, dtype=int64)] / rm1 - \
743
- signal[array(0.5 + ind2 * self.up, dtype=int64)] / rm2)
744
- ind1 += 1.
745
- ind2 += 1.
746
-
934
+ out[i] = (
935
+ rm
936
+ / dist
937
+ * (
938
+ signal[array(0.5 + ind1 * self.up, dtype=int64)] / rm1
939
+ - signal[array(0.5 + ind2 * self.up, dtype=int64)] / rm2
940
+ )
941
+ )
942
+ ind1 += 1.0
943
+ ind2 += 1.0
944
+
747
945
  i += 1
748
946
  if i == num:
749
947
  yield out
750
948
  i = 0
751
949
  except IndexError:
752
950
  break
753
-
951
+
754
952
  yield out[:i]
755
953
 
954
+
756
955
  class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
757
-
758
956
  # internal identifier
759
- digest = Property(
760
- depends_on = ['mics.digest', 'signal.digest', 'loc', \
761
- 'env.digest', 'start_t', 'start', 'up', 'direction', '__class__'],
762
- )
763
-
957
+ digest = Property(
958
+ depends_on=[
959
+ 'mics.digest',
960
+ 'signal.digest',
961
+ 'loc',
962
+ 'env.digest',
963
+ 'start_t',
964
+ 'start',
965
+ 'up',
966
+ 'direction',
967
+ '__class__',
968
+ ],
969
+ )
970
+
764
971
  #: Reference vector, perpendicular to the x and y-axis of moving source.
765
972
  #: rotation source directivity around this axis
766
- rvec = CArray( dtype=float, shape=(3, ), value=array((0, 0, 0)),
767
- desc="reference vector")
768
-
973
+ rvec = CArray(dtype=float, shape=(3,), value=array((0, 0, 0)), desc='reference vector')
974
+
769
975
  @cached_property
770
- def _get_digest( self ):
771
- return digest(self)
976
+ def _get_digest(self):
977
+ return digest(self)
772
978
 
773
- def get_emission_time(self,t,direction):
979
+ def get_emission_time(self, t, direction):
774
980
  eps = ones(self.mics.num_mics)
775
- epslim = 0.1/self.up/self.sample_freq
776
- te = t.copy() # init emission time = receiving time
981
+ epslim = 0.1 / self.up / self.sample_freq
982
+ te = t.copy() # init emission time = receiving time
777
983
  j = 0
778
984
  # Newton-Rhapson iteration
779
- while abs(eps).max()>epslim and j<100:
985
+ while abs(eps).max() > epslim and j < 100:
780
986
  xs = array(self.trajectory.location(te))
781
987
  loc = xs.copy()
782
988
  loc += direction
783
- rm = loc-self.mics.mpos# distance vectors to microphones
784
- rm = sqrt((rm*rm).sum(0))# absolute distance
785
- loc /= sqrt((loc*loc).sum(0))# distance unit vector
989
+ rm = loc - self.mics.mpos # distance vectors to microphones
990
+ rm = sqrt((rm * rm).sum(0)) # absolute distance
991
+ loc /= sqrt((loc * loc).sum(0)) # distance unit vector
786
992
  der = array(self.trajectory.location(te, der=1))
787
- Mr = (der*loc).sum(0)/self.env.c# radial Mach number
788
- eps = (te + rm/self.env.c - t)/(1+Mr)# discrepancy in time
993
+ Mr = (der * loc).sum(0) / self.env.c # radial Mach number
994
+ eps = (te + rm / self.env.c - t) / (1 + Mr) # discrepancy in time
789
995
  te -= eps
790
- j += 1 #iteration count
996
+ j += 1 # iteration count
791
997
  return te, rm, Mr, xs
792
-
793
-
794
- def get_moving_direction(self,direction,time=0):
795
- """
796
- function that yields the moving coordinates along the trajectory
797
- """
798
998
 
799
- trajg1 = array(self.trajectory.location( time, der=1))[:,0][:,newaxis]
800
- rflag = (self.rvec == 0).all() #flag translation vs. rotation
999
+ def get_moving_direction(self, direction, time=0):
1000
+ """Function that yields the moving coordinates along the trajectory."""
1001
+ trajg1 = array(self.trajectory.location(time, der=1))[:, 0][:, newaxis]
1002
+ rflag = (self.rvec == 0).all() # flag translation vs. rotation
801
1003
  if rflag:
802
- return direction
803
- else:
804
- dx = array(trajg1.T) #direction vector (new x-axis)
805
- dy = cross(self.rvec, dx) # new y-axis
806
- dz = cross(dx, dy) # new z-axis
807
- RM = array((dx, dy, dz)).T # rotation matrix
808
- RM /= sqrt((RM*RM).sum(0)) # column normalized
809
- newdir = dot(RM, direction)
810
- return cross(newdir[:,0].T,self.rvec.T).T
1004
+ return direction
1005
+ dx = array(trajg1.T) # direction vector (new x-axis)
1006
+ dy = cross(self.rvec, dx) # new y-axis
1007
+ dz = cross(dx, dy) # new z-axis
1008
+ RM = array((dx, dy, dz)).T # rotation matrix
1009
+ RM /= sqrt((RM * RM).sum(0)) # column normalized
1010
+ newdir = dot(RM, direction)
1011
+ return cross(newdir[:, 0].T, self.rvec.T).T
811
1012
 
812
1013
  def result(self, num=128):
813
- """
814
- Python generator that yields the output at microphones block-wise.
815
-
1014
+ """Python generator that yields the output at microphones block-wise.
1015
+
816
1016
  Parameters
817
1017
  ----------
818
1018
  num : integer, defaults to 128
819
1019
  This parameter defines the size of the blocks to be yielded
820
1020
  (i.e. the number of samples per block) .
821
-
1021
+
822
1022
  Returns
823
1023
  -------
824
- Samples in blocks of shape (num, numchannels).
1024
+ Samples in blocks of shape (num, numchannels).
825
1025
  The last block may be shorter than num.
1026
+
826
1027
  """
827
- #If signal samples are needed for te < t_start, then samples are taken
828
- #from the end of the calculated signal.
1028
+ # If signal samples are needed for te < t_start, then samples are taken
1029
+ # from the end of the calculated signal.
829
1030
  mpos = self.mics.mpos
830
-
1031
+
831
1032
  # direction vector from tuple
832
- direc = array(self.direction, dtype = float) * 1e-5
833
- direc_mag = sqrt(dot(direc,direc))
1033
+ direc = array(self.direction, dtype=float) * 1e-5
1034
+ direc_mag = sqrt(dot(direc, direc))
834
1035
  # normed direction vector
835
1036
  direc_n = direc / direc_mag
836
1037
  c = self.env.c
837
1038
  # distance between monopoles as function of c, sample freq, direction vector
838
1039
  dist = c / self.sample_freq * direc_mag * 2
839
-
1040
+
840
1041
  # vector from dipole center to one of the monopoles
841
1042
  dir2 = (direc_n * dist / 2.0).reshape((3, 1))
842
-
1043
+
843
1044
  signal = self.signal.usignal(self.up)
844
1045
  out = empty((num, self.numchannels))
845
1046
  # shortcuts and intial values
846
1047
  m = self.mics
847
- t = self.start*ones(m.num_mics)
1048
+ t = self.start * ones(m.num_mics)
848
1049
 
849
1050
  i = 0
850
- n = self.numsamples
1051
+ n = self.numsamples
851
1052
  while n:
852
1053
  n -= 1
853
- te, rm, Mr, locs = self.get_emission_time(t,0)
854
- t += 1./self.sample_freq
855
- #location of the center
856
- loc = array(self.trajectory.location(te), dtype = float)[:,0][:,newaxis]
857
- #distance of the dipoles from the center
858
- diff = self.get_moving_direction(dir2,te)
859
-
1054
+ te, rm, Mr, locs = self.get_emission_time(t, 0)
1055
+ t += 1.0 / self.sample_freq
1056
+ # location of the center
1057
+ loc = array(self.trajectory.location(te), dtype=float)[:, 0][:, newaxis]
1058
+ # distance of the dipoles from the center
1059
+ diff = self.get_moving_direction(dir2, te)
1060
+
860
1061
  # distance of sources
861
- rm1 = self.env._r(loc + diff, mpos)
1062
+ rm1 = self.env._r(loc + diff, mpos)
862
1063
  rm2 = self.env._r(loc - diff, mpos)
863
-
864
- ind = (te-self.start_t+self.start)*self.sample_freq
865
- if self.conv_amp:
866
- rm *= (1-Mr)**2
867
- rm1 *= (1-Mr)**2 # assume that Mr is the same for both poles
868
- rm2 *= (1-Mr)**2
1064
+
1065
+ ind = (te - self.start_t + self.start) * self.sample_freq
1066
+ if self.conv_amp:
1067
+ rm *= (1 - Mr) ** 2
1068
+ rm1 *= (1 - Mr) ** 2 # assume that Mr is the same for both poles
1069
+ rm2 *= (1 - Mr) ** 2
869
1070
  try:
870
1071
  # subtract the second signal b/c of phase inversion
871
- out[i] = rm / dist * \
872
- (signal[array(0.5 + ind * self.up, dtype=int64)] / rm1 - \
873
- signal[array(0.5 + ind * self.up, dtype=int64)] / rm2)
1072
+ out[i] = (
1073
+ rm
1074
+ / dist
1075
+ * (
1076
+ signal[array(0.5 + ind * self.up, dtype=int64)] / rm1
1077
+ - signal[array(0.5 + ind * self.up, dtype=int64)] / rm2
1078
+ )
1079
+ )
874
1080
  i += 1
875
1081
  if i == num:
876
1082
  yield out
877
1083
  i = 0
878
1084
  except IndexError:
879
1085
  break
880
- yield out[:i]
881
-
1086
+ yield out[:i]
882
1087
 
883
1088
 
884
- class LineSource( PointSource ):
885
- """
886
- Class to define a fixed Line source with an arbitrary signal.
1089
+ class LineSource(PointSource):
1090
+ """Class to define a fixed Line source with an arbitrary signal.
887
1091
  This can be used in simulations.
888
-
1092
+
889
1093
  The output is being generated via the :meth:`result` generator.
890
1094
  """
891
-
1095
+
892
1096
  #: Vector to define the orientation of the line source
893
- direction = Tuple((0.0, 0.0, 1.0),
894
- desc="Line orientation ")
895
-
1097
+ direction = Tuple((0.0, 0.0, 1.0), desc='Line orientation ')
1098
+
896
1099
  #: Vector to define the length of the line source in m
897
- length = Float(1,desc="length of the line source")
898
-
1100
+ length = Float(1, desc='length of the line source')
1101
+
899
1102
  #: number of monopol sources in the line source
900
1103
  num_sources = Int(1)
901
-
1104
+
902
1105
  #: source strength for every monopole
903
- source_strength = CArray(desc="coefficients of the source strength")
904
-
1106
+ source_strength = CArray(desc='coefficients of the source strength')
1107
+
905
1108
  #:coherence
906
- coherence = Trait( 'coherent', 'incoherent',
907
- desc="coherence mode")
908
-
1109
+ coherence = Trait('coherent', 'incoherent', desc='coherence mode')
1110
+
909
1111
  # internal identifier
910
- digest = Property(
911
- depends_on = ['mics.digest', 'signal.digest', 'loc', \
912
- 'env.digest', 'start_t', 'start', 'up', 'direction',\
913
- 'source_strength','coherence','__class__'],
1112
+ digest = Property(
1113
+ depends_on=[
1114
+ 'mics.digest',
1115
+ 'signal.digest',
1116
+ 'loc',
1117
+ 'env.digest',
1118
+ 'start_t',
1119
+ 'start',
1120
+ 'up',
1121
+ 'direction',
1122
+ 'source_strength',
1123
+ 'coherence',
1124
+ '__class__',
1125
+ ],
1126
+ )
914
1127
 
915
- )
916
-
917
1128
  @cached_property
918
- def _get_digest( self ):
1129
+ def _get_digest(self):
919
1130
  return digest(self)
920
1131
 
921
1132
  def result(self, num=128):
922
- """
923
- Python generator that yields the output at microphones block-wise.
924
-
1133
+ """Python generator that yields the output at microphones block-wise.
1134
+
925
1135
  Parameters
926
1136
  ----------
927
1137
  num : integer, defaults to 128
928
1138
  This parameter defines the size of the blocks to be yielded
929
1139
  (i.e. the number of samples per block) .
930
-
1140
+
931
1141
  Returns
932
1142
  -------
933
- Samples in blocks of shape (num, numchannels).
1143
+ Samples in blocks of shape (num, numchannels).
934
1144
  The last block may be shorter than num.
1145
+
935
1146
  """
936
- #If signal samples are needed for te < t_start, then samples are taken
937
- #from the end of the calculated signal.
1147
+ # If signal samples are needed for te < t_start, then samples are taken
1148
+ # from the end of the calculated signal.
938
1149
 
939
1150
  mpos = self.mics.mpos
940
-
1151
+
941
1152
  # direction vector from tuple
942
- direc = array(self.direction, dtype = float)
1153
+ direc = array(self.direction, dtype=float)
943
1154
  # normed direction vector
944
- direc_n = direc/norm(direc)
1155
+ direc_n = direc / norm(direc)
945
1156
  c = self.env.c
946
-
947
- # distance between monopoles in the line
948
- dist = self.length / self.num_sources
949
-
950
- #blocwise output
1157
+
1158
+ # distance between monopoles in the line
1159
+ dist = self.length / self.num_sources
1160
+
1161
+ # blocwise output
951
1162
  out = zeros((num, self.numchannels))
952
-
953
- # distance from line start position to microphones
954
- loc = array(self.loc, dtype = float).reshape((3, 1))
955
-
1163
+
1164
+ # distance from line start position to microphones
1165
+ loc = array(self.loc, dtype=float).reshape((3, 1))
1166
+
956
1167
  # distances from monopoles in the line to microphones
957
- rms = empty(( self.numchannels,self.num_sources))
958
- inds = empty((self.numchannels,self.num_sources))
1168
+ rms = empty((self.numchannels, self.num_sources))
1169
+ inds = empty((self.numchannels, self.num_sources))
959
1170
  signals = empty((self.num_sources, len(self.signal.usignal(self.up))))
960
- #for every source - distances
1171
+ # for every source - distances
961
1172
  for s in range(self.num_sources):
962
- rms[:,s] = self.env._r((loc.T+direc_n*dist*s).T, mpos)
963
- inds[:,s] = (-rms[:,s] / c - self.start_t + self.start) * self.sample_freq
964
- #new seed for every source
1173
+ rms[:, s] = self.env._r((loc.T + direc_n * dist * s).T, mpos)
1174
+ inds[:, s] = (-rms[:, s] / c - self.start_t + self.start) * self.sample_freq
1175
+ # new seed for every source
965
1176
  if self.coherence == 'incoherent':
966
- self.signal.seed = s + abs(int(hash(self.digest)//10e12))
1177
+ self.signal.seed = s + abs(int(hash(self.digest) // 10e12))
967
1178
  self.signal.rms = self.signal.rms * self.source_strength[s]
968
1179
  signals[s] = self.signal.usignal(self.up)
969
1180
  i = 0
970
- n = self.numsamples
1181
+ n = self.numsamples
971
1182
  while n:
972
1183
  n -= 1
973
1184
  try:
974
1185
  for s in range(self.num_sources):
975
- # sum sources
976
- out[i] += (signals[s,array(0.5 + inds[:,s].T * self.up, dtype=int64)] / rms[:,s])
977
-
978
- inds += 1.
1186
+ # sum sources
1187
+ out[i] += signals[s, array(0.5 + inds[:, s].T * self.up, dtype=int64)] / rms[:, s]
1188
+
1189
+ inds += 1.0
979
1190
  i += 1
980
1191
  if i == num:
981
1192
  yield out
@@ -983,139 +1194,141 @@ class LineSource( PointSource ):
983
1194
  i = 0
984
1195
  except IndexError:
985
1196
  break
986
-
1197
+
987
1198
  yield out[:i]
988
-
989
- class MovingLineSource(LineSource,MovingPointSource):
990
-
1199
+
1200
+
1201
+ class MovingLineSource(LineSource, MovingPointSource):
991
1202
  # internal identifier
992
- digest = Property(
993
- depends_on = ['mics.digest', 'signal.digest', 'loc', \
994
- 'env.digest', 'start_t', 'start', 'up', 'direction', '__class__'],
995
- )
996
-
1203
+ digest = Property(
1204
+ depends_on=[
1205
+ 'mics.digest',
1206
+ 'signal.digest',
1207
+ 'loc',
1208
+ 'env.digest',
1209
+ 'start_t',
1210
+ 'start',
1211
+ 'up',
1212
+ 'direction',
1213
+ '__class__',
1214
+ ],
1215
+ )
997
1216
 
998
1217
  #: Reference vector, perpendicular to the x and y-axis of moving source.
999
1218
  #: rotation source directivity around this axis
1000
- rvec = CArray( dtype=float, shape=(3, ), value=array((0, 0, 0)),
1001
- desc="reference vector")
1002
-
1219
+ rvec = CArray(dtype=float, shape=(3,), value=array((0, 0, 0)), desc='reference vector')
1220
+
1003
1221
  @cached_property
1004
- def _get_digest( self ):
1005
- return digest(self)
1006
-
1007
- def get_moving_direction(self,direction,time=0):
1008
- """
1009
- function that yields the moving coordinates along the trajectory
1010
- """
1011
-
1012
- trajg1 = array(self.trajectory.location( time, der=1))[:,0][:,newaxis]
1013
- rflag = (self.rvec == 0).all() #flag translation vs. rotation
1222
+ def _get_digest(self):
1223
+ return digest(self)
1224
+
1225
+ def get_moving_direction(self, direction, time=0):
1226
+ """Function that yields the moving coordinates along the trajectory."""
1227
+ trajg1 = array(self.trajectory.location(time, der=1))[:, 0][:, newaxis]
1228
+ rflag = (self.rvec == 0).all() # flag translation vs. rotation
1014
1229
  if rflag:
1015
- return direction
1016
- else:
1017
- dx = array(trajg1.T) #direction vector (new x-axis)
1018
- dy = cross(self.rvec, dx) # new y-axis
1019
- dz = cross(dx, dy) # new z-axis
1020
- RM = array((dx, dy, dz)).T # rotation matrix
1021
- RM /= sqrt((RM*RM).sum(0)) # column normalized
1022
- newdir = dot(RM, direction)
1023
- return cross(newdir[:,0].T,self.rvec.T).T
1024
-
1025
- def get_emission_time(self,t,direction):
1230
+ return direction
1231
+ dx = array(trajg1.T) # direction vector (new x-axis)
1232
+ dy = cross(self.rvec, dx) # new y-axis
1233
+ dz = cross(dx, dy) # new z-axis
1234
+ RM = array((dx, dy, dz)).T # rotation matrix
1235
+ RM /= sqrt((RM * RM).sum(0)) # column normalized
1236
+ newdir = dot(RM, direction)
1237
+ return cross(newdir[:, 0].T, self.rvec.T).T
1238
+
1239
+ def get_emission_time(self, t, direction):
1026
1240
  eps = ones(self.mics.num_mics)
1027
- epslim = 0.1/self.up/self.sample_freq
1028
- te = t.copy() # init emission time = receiving time
1241
+ epslim = 0.1 / self.up / self.sample_freq
1242
+ te = t.copy() # init emission time = receiving time
1029
1243
  j = 0
1030
1244
  # Newton-Rhapson iteration
1031
- while abs(eps).max()>epslim and j<100:
1245
+ while abs(eps).max() > epslim and j < 100:
1032
1246
  xs = array(self.trajectory.location(te))
1033
1247
  loc = xs.copy()
1034
1248
  loc += direction
1035
- rm = loc-self.mics.mpos# distance vectors to microphones
1036
- rm = sqrt((rm*rm).sum(0))# absolute distance
1037
- loc /= sqrt((loc*loc).sum(0))# distance unit vector
1249
+ rm = loc - self.mics.mpos # distance vectors to microphones
1250
+ rm = sqrt((rm * rm).sum(0)) # absolute distance
1251
+ loc /= sqrt((loc * loc).sum(0)) # distance unit vector
1038
1252
  der = array(self.trajectory.location(te, der=1))
1039
- Mr = (der*loc).sum(0)/self.env.c# radial Mach number
1040
- eps = (te + rm/self.env.c - t)/(1+Mr)# discrepancy in time
1253
+ Mr = (der * loc).sum(0) / self.env.c # radial Mach number
1254
+ eps = (te + rm / self.env.c - t) / (1 + Mr) # discrepancy in time
1041
1255
  te -= eps
1042
- j += 1 #iteration count
1256
+ j += 1 # iteration count
1043
1257
  return te, rm, Mr, xs
1044
-
1258
+
1045
1259
  def result(self, num=128):
1046
- """
1047
- Python generator that yields the output at microphones block-wise.
1048
-
1260
+ """Python generator that yields the output at microphones block-wise.
1261
+
1049
1262
  Parameters
1050
1263
  ----------
1051
1264
  num : integer, defaults to 128
1052
1265
  This parameter defines the size of the blocks to be yielded
1053
1266
  (i.e. the number of samples per block) .
1054
-
1267
+
1055
1268
  Returns
1056
1269
  -------
1057
- Samples in blocks of shape (num, numchannels).
1270
+ Samples in blocks of shape (num, numchannels).
1058
1271
  The last block may be shorter than num.
1272
+
1059
1273
  """
1060
-
1061
- #If signal samples are needed for te < t_start, then samples are taken
1062
- #from the end of the calculated signal.
1274
+ # If signal samples are needed for te < t_start, then samples are taken
1275
+ # from the end of the calculated signal.
1063
1276
  mpos = self.mics.mpos
1064
-
1277
+
1065
1278
  # direction vector from tuple
1066
- direc = array(self.direction, dtype = float)
1279
+ direc = array(self.direction, dtype=float)
1067
1280
  # normed direction vector
1068
- direc_n = direc/norm(direc)
1069
-
1070
- # distance between monopoles in the line
1071
- dist = self.length / self.num_sources
1281
+ direc_n = direc / norm(direc)
1282
+
1283
+ # distance between monopoles in the line
1284
+ dist = self.length / self.num_sources
1072
1285
  dir2 = (direc_n * dist).reshape((3, 1))
1073
-
1074
- #blocwise output
1286
+
1287
+ # blocwise output
1075
1288
  out = zeros((num, self.numchannels))
1076
-
1289
+
1077
1290
  # distances from monopoles in the line to microphones
1078
- rms = empty(( self.numchannels,self.num_sources))
1079
- inds = empty((self.numchannels,self.num_sources))
1291
+ rms = empty((self.numchannels, self.num_sources))
1292
+ inds = empty((self.numchannels, self.num_sources))
1080
1293
  signals = empty((self.num_sources, len(self.signal.usignal(self.up))))
1081
- #coherence
1294
+ # coherence
1082
1295
  for s in range(self.num_sources):
1083
- #new seed for every source
1296
+ # new seed for every source
1084
1297
  if self.coherence == 'incoherent':
1085
- self.signal.seed = s + abs(int(hash(self.digest)//10e12))
1298
+ self.signal.seed = s + abs(int(hash(self.digest) // 10e12))
1086
1299
  self.signal.rms = self.signal.rms * self.source_strength[s]
1087
1300
  signals[s] = self.signal.usignal(self.up)
1088
1301
  mpos = self.mics.mpos
1089
-
1302
+
1090
1303
  # shortcuts and intial values
1091
1304
  m = self.mics
1092
- t = self.start*ones(m.num_mics)
1305
+ t = self.start * ones(m.num_mics)
1093
1306
  i = 0
1094
- n = self.numsamples
1307
+ n = self.numsamples
1095
1308
  while n:
1096
- n -= 1
1097
- t += 1./self.sample_freq
1098
- te1, rm1, Mr1, locs1 = self.get_emission_time(t,0)
1099
- #trajg1 = array(self.trajectory.location( te1, der=1))[:,0][:,newaxis]
1100
-
1309
+ n -= 1
1310
+ t += 1.0 / self.sample_freq
1311
+ te1, rm1, Mr1, locs1 = self.get_emission_time(t, 0)
1312
+ # trajg1 = array(self.trajectory.location( te1, der=1))[:,0][:,newaxis]
1313
+
1101
1314
  # get distance and ind for every source in the line
1102
1315
  for s in range(self.num_sources):
1103
- diff = self.get_moving_direction(dir2,te1)
1104
- te, rm, Mr, locs = self.get_emission_time(t,tile((diff*s).T,(self.numchannels,1)).T)
1105
- loc = array(self.trajectory.location(te), dtype = float)[:,0][:,newaxis]
1106
- diff = self.get_moving_direction(dir2,te)
1107
- rms[:,s] = self.env._r((loc+diff*s), mpos)
1108
- inds[:,s] = (te-self.start_t+self.start)*self.sample_freq
1109
-
1110
- if self.conv_amp:
1111
- rm *= (1-Mr)**2
1112
- rms[:,s] *= (1-Mr)**2 # assume that Mr is the same
1316
+ diff = self.get_moving_direction(dir2, te1)
1317
+ te, rm, Mr, locs = self.get_emission_time(t, tile((diff * s).T, (self.numchannels, 1)).T)
1318
+ loc = array(self.trajectory.location(te), dtype=float)[:, 0][:, newaxis]
1319
+ diff = self.get_moving_direction(dir2, te)
1320
+ rms[:, s] = self.env._r((loc + diff * s), mpos)
1321
+ inds[:, s] = (te - self.start_t + self.start) * self.sample_freq
1322
+
1323
+ if self.conv_amp:
1324
+ rm *= (1 - Mr) ** 2
1325
+ rms[:, s] *= (1 - Mr) ** 2 # assume that Mr is the same
1113
1326
  try:
1114
1327
  # subtract the second signal b/c of phase inversion
1115
1328
  for s in range(self.num_sources):
1116
- # sum sources
1117
- out[i] += (signals[s,array(0.5 + inds[:,s].T * self.up, dtype=int64)] / rms[:,s])
1118
-
1329
+ # sum sources
1330
+ out[i] += signals[s, array(0.5 + inds[:, s].T * self.up, dtype=int64)] / rms[:, s]
1331
+
1119
1332
  i += 1
1120
1333
  if i == num:
1121
1334
  yield out
@@ -1126,212 +1339,201 @@ class MovingLineSource(LineSource,MovingPointSource):
1126
1339
  yield out[:i]
1127
1340
 
1128
1341
 
1129
- class UncorrelatedNoiseSource( SamplesGenerator ):
1130
- """
1131
- Class to simulate white or pink noise as uncorrelated signal at each
1342
+ class UncorrelatedNoiseSource(SamplesGenerator):
1343
+ """Class to simulate white or pink noise as uncorrelated signal at each
1132
1344
  channel.
1133
-
1345
+
1134
1346
  The output is being generated via the :meth:`result` generator.
1135
1347
  """
1136
-
1137
- #: Type of noise to generate at the channels.
1138
- #: The `~acoular.signals.SignalGenerator`-derived class has to
1348
+
1349
+ #: Type of noise to generate at the channels.
1350
+ #: The `~acoular.signals.SignalGenerator`-derived class has to
1139
1351
  # feature the parameter "seed" (i.e. white or pink noise).
1140
- signal = Trait(SignalGenerator,
1141
- desc = "type of noise")
1352
+ signal = Trait(SignalGenerator, desc='type of noise')
1142
1353
 
1143
1354
  #: Array with seeds for random number generator.
1144
- #: When left empty, arange(:attr:`numchannels`) + :attr:`signal`.seed
1355
+ #: When left empty, arange(:attr:`numchannels`) + :attr:`signal`.seed
1145
1356
  #: will be used.
1146
- seed = CArray(dtype = uint32,
1147
- desc = "random seed values")
1148
-
1149
- #: Number of channels in output; is set automatically /
1357
+ seed = CArray(dtype=uint32, desc='random seed values')
1358
+
1359
+ #: Number of channels in output; is set automatically /
1150
1360
  #: depends on used microphone geometry.
1151
1361
  numchannels = Delegate('mics', 'num_mics')
1152
1362
 
1153
1363
  #: :class:`~acoular.microphones.MicGeom` object that provides the microphone locations.
1154
- mics = Trait(MicGeom,
1155
- desc="microphone geometry")
1364
+ mics = Trait(MicGeom, desc='microphone geometry')
1156
1365
 
1157
1366
  # --- List of backwards compatibility traits and their setters/getters -----------
1158
1367
 
1159
1368
  # Microphone locations.
1160
1369
  # Deprecated! Use :attr:`mics` trait instead.
1161
1370
  mpos = Property()
1162
-
1371
+
1163
1372
  def _get_mpos(self):
1164
1373
  return self.mics
1165
-
1374
+
1166
1375
  def _set_mpos(self, mpos):
1167
- warn("Deprecated use of 'mpos' trait. ", Warning, stacklevel = 2)
1376
+ warn("Deprecated use of 'mpos' trait. ", Warning, stacklevel=2)
1168
1377
  self.mics = mpos
1169
1378
 
1170
1379
  # --- End of backwards compatibility traits --------------------------------------
1171
-
1380
+
1172
1381
  #: Start time of the signal in seconds, defaults to 0 s.
1173
- start_t = Float(0.0,
1174
- desc="signal start time")
1175
-
1176
- #: Start time of the data aquisition at microphones in seconds,
1382
+ start_t = Float(0.0, desc='signal start time')
1383
+
1384
+ #: Start time of the data aquisition at microphones in seconds,
1177
1385
  #: defaults to 0 s.
1178
- start = Float(0.0,
1179
- desc="sample start time")
1386
+ start = Float(0.0, desc='sample start time')
1180
1387
 
1181
-
1182
- #: Number of samples is set automatically /
1388
+ #: Number of samples is set automatically /
1183
1389
  #: depends on :attr:`signal`.
1184
1390
  numsamples = Delegate('signal')
1185
-
1186
- #: Sampling frequency of the signal; is set automatically /
1391
+
1392
+ #: Sampling frequency of the signal; is set automatically /
1187
1393
  #: depends on :attr:`signal`.
1188
- sample_freq = Delegate('signal')
1189
-
1394
+ sample_freq = Delegate('signal')
1395
+
1190
1396
  # internal identifier
1191
- digest = Property(
1192
- depends_on = ['mics.digest', 'signal.rms', 'signal.numsamples', \
1193
- 'signal.sample_freq', 'signal.__class__' , 'seed', 'loc', \
1194
- 'start_t', 'start', '__class__'],
1195
- )
1397
+ digest = Property(
1398
+ depends_on=[
1399
+ 'mics.digest',
1400
+ 'signal.rms',
1401
+ 'signal.numsamples',
1402
+ 'signal.sample_freq',
1403
+ 'signal.__class__',
1404
+ 'seed',
1405
+ 'loc',
1406
+ 'start_t',
1407
+ 'start',
1408
+ '__class__',
1409
+ ],
1410
+ )
1196
1411
 
1197
1412
  @cached_property
1198
- def _get_digest( self ):
1413
+ def _get_digest(self):
1199
1414
  return digest(self)
1200
-
1201
- def result ( self, num=128 ):
1202
- """
1203
- Python generator that yields the output at microphones block-wise.
1204
-
1415
+
1416
+ def result(self, num=128):
1417
+ """Python generator that yields the output at microphones block-wise.
1418
+
1205
1419
  Parameters
1206
1420
  ----------
1207
1421
  num : integer, defaults to 128
1208
1422
  This parameter defines the size of the blocks to be yielded
1209
1423
  (i.e. the number of samples per block) .
1210
-
1424
+
1211
1425
  Returns
1212
1426
  -------
1213
- Samples in blocks of shape (num, numchannels).
1427
+ Samples in blocks of shape (num, numchannels).
1214
1428
  The last block may be shorter than num.
1215
- """
1216
1429
 
1430
+ """
1217
1431
  Noise = self.signal.__class__
1218
1432
  # create or get the array of random seeds
1219
- if not self.seed:
1433
+ if not self.seed:
1220
1434
  seed = arange(self.numchannels) + self.signal.seed
1221
1435
  elif self.seed.shape == (self.numchannels,):
1222
1436
  seed = self.seed
1223
1437
  else:
1224
- raise ValueError(\
1225
- "Seed array expected to be of shape (%i,), but has shape %s." \
1226
- % (self.numchannels, str(self.seed.shape)) )
1227
-
1228
- # create array with [numchannels] noise signal tracks
1229
- signal = array([Noise(seed = s,
1230
- numsamples = self.numsamples,
1231
- sample_freq = self.sample_freq,
1232
- rms = self.signal.rms).signal() \
1233
- for s in seed]).T
1438
+ raise ValueError(
1439
+ 'Seed array expected to be of shape (%i,), but has shape %s.'
1440
+ % (self.numchannels, str(self.seed.shape)),
1441
+ )
1234
1442
 
1235
- n = num
1443
+ # create array with [numchannels] noise signal tracks
1444
+ signal = array(
1445
+ [
1446
+ Noise(seed=s, numsamples=self.numsamples, sample_freq=self.sample_freq, rms=self.signal.rms).signal()
1447
+ for s in seed
1448
+ ],
1449
+ ).T
1450
+
1451
+ n = num
1236
1452
  while n <= self.numsamples:
1237
- yield signal[n-num:n,:]
1453
+ yield signal[n - num : n, :]
1238
1454
  n += num
1239
1455
  else:
1240
- if (n-num) < self.numsamples:
1241
- yield signal[n-num:,:]
1456
+ if (n - num) < self.numsamples:
1457
+ yield signal[n - num :, :]
1242
1458
  else:
1243
1459
  return
1244
1460
 
1245
1461
 
1246
-
1247
- class SourceMixer( SamplesGenerator ):
1248
- """
1249
- Mixes the signals from several sources.
1250
- """
1462
+ class SourceMixer(SamplesGenerator):
1463
+ """Mixes the signals from several sources."""
1251
1464
 
1252
1465
  #: List of :class:`~acoular.tprocess.SamplesGenerator` objects
1253
1466
  #: to be mixed.
1254
- sources = List( Instance(SamplesGenerator, ()) )
1467
+ sources = List(Instance(SamplesGenerator, ()))
1255
1468
 
1256
1469
  #: Sampling frequency of the signal.
1257
- sample_freq = Property( depends_on=['sdigest'] )
1258
-
1470
+ sample_freq = Property(depends_on=['sdigest'])
1471
+
1259
1472
  #: Number of channels.
1260
- numchannels = Property( depends_on=['sdigest'] )
1261
-
1473
+ numchannels = Property(depends_on=['sdigest'])
1474
+
1262
1475
  #: Number of samples.
1263
- numsamples = Property( depends_on=['sdigest'] )
1264
-
1265
- #: Amplitude weight(s) for the sources as array. If not set,
1476
+ numsamples = Property(depends_on=['sdigest'])
1477
+
1478
+ #: Amplitude weight(s) for the sources as array. If not set,
1266
1479
  #: all source signals are equally weighted.
1267
1480
  #: Must match the number of sources in :attr:`sources`.
1268
- weights = CArray(desc="channel weights")
1481
+ weights = CArray(desc='channel weights')
1269
1482
 
1270
- # internal identifier
1483
+ # internal identifier
1271
1484
  sdigest = Str()
1272
1485
 
1273
1486
  @observe('sources.items.digest')
1274
- def _set_sources_digest( self, event ):
1275
- self.sdigest = ldigest(self.sources)
1487
+ def _set_sources_digest(self, event): # noqa ARG002
1488
+ self.sdigest = ldigest(self.sources)
1276
1489
 
1277
1490
  # internal identifier
1278
- digest = Property( depends_on = ['sdigest', 'weights'])
1491
+ digest = Property(depends_on=['sdigest', 'weights'])
1279
1492
 
1280
1493
  @cached_property
1281
- def _get_digest( self ):
1494
+ def _get_digest(self):
1282
1495
  return digest(self)
1283
1496
 
1284
1497
  @cached_property
1285
- def _get_sample_freq( self ):
1286
- if self.sources:
1287
- sample_freq = self.sources[0].sample_freq
1288
- else:
1289
- sample_freq = 0
1290
- return sample_freq
1498
+ def _get_sample_freq(self):
1499
+ return self.sources[0].sample_freq if self.sources else 0
1291
1500
 
1292
1501
  @cached_property
1293
- def _get_numchannels( self ):
1294
- if self.sources:
1295
- numchannels = self.sources[0].numchannels
1296
- else:
1297
- numchannels = 0
1298
- return numchannels
1502
+ def _get_numchannels(self):
1503
+ return self.sources[0].numchannels if self.sources else 0
1299
1504
 
1300
1505
  @cached_property
1301
- def _get_numsamples( self ):
1302
- if self.sources:
1303
- numsamples = self.sources[0].numsamples
1304
- else:
1305
- numsamples = 0
1306
- return numsamples
1506
+ def _get_numsamples(self):
1507
+ return self.sources[0].numsamples if self.sources else 0
1307
1508
 
1308
- def validate_sources( self ):
1309
- """ Validates if sources fit together. """
1509
+ def validate_sources(self):
1510
+ """Validates if sources fit together."""
1310
1511
  if len(self.sources) < 1:
1311
- raise ValueError("Number of sources in SourceMixer should be at least 1.")
1512
+ msg = 'Number of sources in SourceMixer should be at least 1.'
1513
+ raise ValueError(msg)
1312
1514
  for s in self.sources[1:]:
1313
1515
  if self.sample_freq != s.sample_freq:
1314
- raise ValueError("Sample frequency of %s does not fit" % s)
1516
+ raise ValueError('Sample frequency of %s does not fit' % s)
1315
1517
  if self.numchannels != s.numchannels:
1316
- raise ValueError("Channel count of %s does not fit" % s)
1518
+ raise ValueError('Channel count of %s does not fit' % s)
1317
1519
  if self.numsamples != s.numsamples:
1318
- raise ValueError("Number of samples of %s does not fit" % s)
1520
+ raise ValueError('Number of samples of %s does not fit' % s)
1319
1521
 
1320
1522
  def result(self, num):
1321
- """
1322
- Python generator that yields the output block-wise.
1523
+ """Python generator that yields the output block-wise.
1323
1524
  The outputs from the sources in the list are being added.
1324
-
1525
+
1325
1526
  Parameters
1326
1527
  ----------
1327
1528
  num : integer
1328
1529
  This parameter defines the size of the blocks to be yielded
1329
1530
  (i.e. the number of samples per block).
1330
-
1531
+
1331
1532
  Returns
1332
1533
  -------
1333
- Samples in blocks of shape (num, numchannels).
1534
+ Samples in blocks of shape (num, numchannels).
1334
1535
  The last block may be shorter than num.
1536
+
1335
1537
  """
1336
1538
  # check whether all sources fit together
1337
1539
  self.validate_sources()
@@ -1339,63 +1541,58 @@ class SourceMixer( SamplesGenerator ):
1339
1541
  gens = [i.result(num) for i in self.sources[1:]]
1340
1542
  weights = self.weights.copy()
1341
1543
  if weights.size == 0:
1342
- weights = array([1. for j in range(len( self.sources))])
1544
+ weights = array([1.0 for j in range(len(self.sources))])
1343
1545
  assert weights.shape[0] == len(self.sources)
1344
1546
  for temp in self.sources[0].result(num):
1345
1547
  temp *= weights[0]
1346
1548
  sh = temp.shape[0]
1347
- for j,g in enumerate(gens):
1348
- temp1 = next(g)*weights[j+1]
1549
+ for j, g in enumerate(gens):
1550
+ temp1 = next(g) * weights[j + 1]
1349
1551
  if temp.shape[0] > temp1.shape[0]:
1350
- temp = temp[:temp1.shape[0]]
1351
- temp += temp1[:temp.shape[0]]
1552
+ temp = temp[: temp1.shape[0]]
1553
+ temp += temp1[: temp.shape[0]]
1352
1554
  yield temp
1353
1555
  if sh > temp.shape[0]:
1354
1556
  break
1355
1557
 
1356
1558
 
1357
- class PointSourceConvolve( PointSource ):
1358
- """
1359
- Class to blockwise convolve an arbitrary source signal with a spatial room impulse response
1360
- """
1559
+ class PointSourceConvolve(PointSource):
1560
+ """Class to blockwise convolve an arbitrary source signal with a spatial room impulse response."""
1361
1561
 
1362
1562
  #: Convolution kernel in the time domain.
1363
1563
  #: The second dimension of the kernel array has to be either 1 or match :attr:`~SamplesGenerator.numchannels`.
1364
1564
  #: If only a single kernel is supplied, it is applied to all channels.
1365
- kernel = CArray(dtype=float, desc="Convolution kernel.")
1565
+ kernel = CArray(dtype=float, desc='Convolution kernel.')
1366
1566
 
1367
1567
  # ------------- overwrite traits that are not supported by this class -------------
1368
-
1568
+
1369
1569
  #: Start time of the signal in seconds, defaults to 0 s.
1370
- start_t = Enum(0.0,
1371
- desc="signal start time")
1372
-
1373
- #: Start time of the data aquisition at microphones in seconds,
1570
+ start_t = Enum(0.0, desc='signal start time')
1571
+
1572
+ #: Start time of the data aquisition at microphones in seconds,
1374
1573
  #: defaults to 0 s.
1375
- start = Enum(0.0,
1376
- desc="sample start time")
1574
+ start = Enum(0.0, desc='sample start time')
1377
1575
 
1378
1576
  #: Signal behaviour for negative time indices, i.e. if :attr:`start` < :attr:start_t.
1379
1577
  #: `loop` take values from the end of :attr:`signal.signal()` array.
1380
1578
  #: `zeros` set source signal to zero, advisable for deterministic signals.
1381
1579
  #: defaults to `loop`.
1382
- prepadding = Enum(None, desc="Behaviour for negative time indices.")
1580
+ prepadding = Enum(None, desc='Behaviour for negative time indices.')
1383
1581
 
1384
1582
  #: Upsampling factor, internal use, defaults to 16.
1385
- up = Enum(None, desc="upsampling factor")
1386
-
1583
+ up = Enum(None, desc='upsampling factor')
1584
+
1387
1585
  # internal identifier
1388
- digest = Property(
1389
- depends_on = ['mics.digest', 'signal.digest', 'loc', 'kernel', '__class__'],
1390
- )
1391
-
1586
+ digest = Property(
1587
+ depends_on=['mics.digest', 'signal.digest', 'loc', 'kernel', '__class__'],
1588
+ )
1589
+
1392
1590
  @cached_property
1393
- def _get_digest( self ):
1591
+ def _get_digest(self):
1394
1592
  return digest(self)
1395
1593
 
1396
1594
  def result(self, num=128):
1397
- """
1398
- Python generator that yields the output at microphones block-wise.
1595
+ """Python generator that yields the output at microphones block-wise.
1399
1596
 
1400
1597
  Parameters
1401
1598
  ----------
@@ -1405,11 +1602,11 @@ class PointSourceConvolve( PointSource ):
1405
1602
 
1406
1603
  Returns
1407
1604
  -------
1408
- Samples in blocks of shape (num, numchannels).
1605
+ Samples in blocks of shape (num, numchannels).
1409
1606
  The last block may be shorter than num.
1607
+
1410
1608
  """
1411
- data = repeat(
1412
- self.signal.signal()[:,newaxis],self.mics.num_mics,axis=1)
1609
+ data = repeat(self.signal.signal()[:, newaxis], self.mics.num_mics, axis=1)
1413
1610
  source = TimeSamples(
1414
1611
  data=data,
1415
1612
  sample_freq=self.sample_freq,
@@ -1417,8 +1614,7 @@ class PointSourceConvolve( PointSource ):
1417
1614
  numchannels=self.mics.num_mics,
1418
1615
  )
1419
1616
  time_convolve = TimeConvolve(
1420
- source = source,
1421
- kernel = self.kernel,
1617
+ source=source,
1618
+ kernel=self.kernel,
1422
1619
  )
1423
- for block in time_convolve.result(num):
1424
- yield block
1620
+ yield from time_convolve.result(num)