acoular 23.6__py3-none-any.whl → 24.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acoular/__init__.py +2 -2
- acoular/configuration.py +37 -1
- acoular/environments.py +15 -9
- acoular/fastFuncs.py +199 -472
- acoular/fbeamform.py +168 -109
- acoular/grids.py +33 -114
- acoular/sources.py +77 -3
- acoular/spectra.py +2 -2
- acoular/tbeamform.py +15 -8
- acoular/tests/reference_data/BeamformerBaseFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFLassoLarsBIC.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFNNLS.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerGIB.npy +0 -0
- acoular/tests/reference_data/BeamformerSODIX.npy +0 -0
- acoular/tests/reference_data/FiltFiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltFiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_A_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_C_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_Z_.npy +0 -0
- acoular/tests/reference_data/FiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/Filter__.npy +0 -0
- acoular/tests/reference_data/OctaveFilterBank__.npy +0 -0
- acoular/tests/reference_data/TimeAverage__.npy +0 -0
- acoular/tests/reference_data/TimeCumAverage__.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_F_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_I_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_S_.npy +0 -0
- acoular/tests/reference_data/TimeInOut__.npy +0 -0
- acoular/tests/reference_data/TimePower__.npy +0 -0
- acoular/tests/reference_data/TimeReverse__.npy +0 -0
- acoular/tests/test_beamformer_results.py +39 -8
- acoular/tests/test_grid.py +92 -0
- acoular/tests/test_integrate.py +102 -0
- acoular/tests/test_tprocess.py +52 -0
- acoular/tests/test_traj_beamformer_results.py +2 -2
- acoular/tfastfuncs.py +24 -25
- acoular/tools.py +144 -2
- acoular/tprocess.py +91 -102
- acoular/version.py +2 -2
- acoular-24.3.dist-info/METADATA +181 -0
- {acoular-23.6.dist-info → acoular-24.3.dist-info}/RECORD +62 -25
- {acoular-23.6.dist-info → acoular-24.3.dist-info}/WHEEL +1 -1
- {acoular-23.6.dist-info → acoular-24.3.dist-info}/licenses/LICENSE +1 -1
- acoular/tests/reference_data/BeamformerCMF.npy +0 -0
- acoular-23.6.dist-info/METADATA +0 -82
- {acoular-23.6.dist-info → acoular-24.3.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/sources.py
CHANGED
|
@@ -19,12 +19,13 @@
|
|
|
19
19
|
MovingLineSource
|
|
20
20
|
UncorrelatedNoiseSource
|
|
21
21
|
SourceMixer
|
|
22
|
+
PointSourceConvolve
|
|
22
23
|
"""
|
|
23
24
|
|
|
24
25
|
# imports from other packages
|
|
25
26
|
|
|
26
27
|
from numpy import array, sqrt, ones, empty, newaxis, uint32, arange, dot, int64 ,real, pi, tile,\
|
|
27
|
-
cross, zeros, ceil
|
|
28
|
+
cross, zeros, ceil, repeat
|
|
28
29
|
from numpy import min as npmin
|
|
29
30
|
from numpy import any as npany
|
|
30
31
|
|
|
@@ -45,7 +46,7 @@ from .trajectory import Trajectory
|
|
|
45
46
|
from .internal import digest, ldigest
|
|
46
47
|
from .microphones import MicGeom
|
|
47
48
|
from .environments import Environment
|
|
48
|
-
from .tprocess import SamplesGenerator
|
|
49
|
+
from .tprocess import SamplesGenerator, TimeConvolve
|
|
49
50
|
from .signals import SignalGenerator
|
|
50
51
|
from .h5files import H5FileBase, _get_h5file_class
|
|
51
52
|
from .tools import get_modes
|
|
@@ -1236,7 +1237,10 @@ class UncorrelatedNoiseSource( SamplesGenerator ):
|
|
|
1236
1237
|
yield signal[n-num:n,:]
|
|
1237
1238
|
n += num
|
|
1238
1239
|
else:
|
|
1239
|
-
|
|
1240
|
+
if (n-num) < self.numsamples:
|
|
1241
|
+
yield signal[n-num:,:]
|
|
1242
|
+
else:
|
|
1243
|
+
return
|
|
1240
1244
|
|
|
1241
1245
|
|
|
1242
1246
|
|
|
@@ -1348,3 +1352,73 @@ class SourceMixer( SamplesGenerator ):
|
|
|
1348
1352
|
yield temp
|
|
1349
1353
|
if sh > temp.shape[0]:
|
|
1350
1354
|
break
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
class PointSourceConvolve( PointSource ):
|
|
1358
|
+
"""
|
|
1359
|
+
Class to blockwise convolve an arbitrary source signal with a spatial room impulse response
|
|
1360
|
+
"""
|
|
1361
|
+
|
|
1362
|
+
#: Convolution kernel in the time domain.
|
|
1363
|
+
#: The second dimension of the kernel array has to be either 1 or match :attr:`~SamplesGenerator.numchannels`.
|
|
1364
|
+
#: If only a single kernel is supplied, it is applied to all channels.
|
|
1365
|
+
kernel = CArray(dtype=float, desc="Convolution kernel.")
|
|
1366
|
+
|
|
1367
|
+
# ------------- overwrite traits that are not supported by this class -------------
|
|
1368
|
+
|
|
1369
|
+
#: 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,
|
|
1374
|
+
#: defaults to 0 s.
|
|
1375
|
+
start = Enum(0.0,
|
|
1376
|
+
desc="sample start time")
|
|
1377
|
+
|
|
1378
|
+
#: Signal behaviour for negative time indices, i.e. if :attr:`start` < :attr:start_t.
|
|
1379
|
+
#: `loop` take values from the end of :attr:`signal.signal()` array.
|
|
1380
|
+
#: `zeros` set source signal to zero, advisable for deterministic signals.
|
|
1381
|
+
#: defaults to `loop`.
|
|
1382
|
+
prepadding = Enum(None, desc="Behaviour for negative time indices.")
|
|
1383
|
+
|
|
1384
|
+
#: Upsampling factor, internal use, defaults to 16.
|
|
1385
|
+
up = Enum(None, desc="upsampling factor")
|
|
1386
|
+
|
|
1387
|
+
# internal identifier
|
|
1388
|
+
digest = Property(
|
|
1389
|
+
depends_on = ['mics.digest', 'signal.digest', 'loc', 'kernel', '__class__'],
|
|
1390
|
+
)
|
|
1391
|
+
|
|
1392
|
+
@cached_property
|
|
1393
|
+
def _get_digest( self ):
|
|
1394
|
+
return digest(self)
|
|
1395
|
+
|
|
1396
|
+
def result(self, num=128):
|
|
1397
|
+
"""
|
|
1398
|
+
Python generator that yields the output at microphones block-wise.
|
|
1399
|
+
|
|
1400
|
+
Parameters
|
|
1401
|
+
----------
|
|
1402
|
+
num : integer, defaults to 128
|
|
1403
|
+
This parameter defines the size of the blocks to be yielded
|
|
1404
|
+
(i.e. the number of samples per block) .
|
|
1405
|
+
|
|
1406
|
+
Returns
|
|
1407
|
+
-------
|
|
1408
|
+
Samples in blocks of shape (num, numchannels).
|
|
1409
|
+
The last block may be shorter than num.
|
|
1410
|
+
"""
|
|
1411
|
+
data = repeat(
|
|
1412
|
+
self.signal.signal()[:,newaxis],self.mics.num_mics,axis=1)
|
|
1413
|
+
source = TimeSamples(
|
|
1414
|
+
data=data,
|
|
1415
|
+
sample_freq=self.sample_freq,
|
|
1416
|
+
numsamples=self.numsamples,
|
|
1417
|
+
numchannels=self.mics.num_mics,
|
|
1418
|
+
)
|
|
1419
|
+
time_convolve = TimeConvolve(
|
|
1420
|
+
source = source,
|
|
1421
|
+
kernel = self.kernel,
|
|
1422
|
+
)
|
|
1423
|
+
for block in time_convolve.result(num):
|
|
1424
|
+
yield block
|
acoular/spectra.py
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
from warnings import warn
|
|
19
19
|
|
|
20
20
|
from numpy import array, ones, hanning, hamming, bartlett, blackman, \
|
|
21
|
-
dot, newaxis, zeros, empty,
|
|
21
|
+
dot, newaxis, zeros, empty, linalg, sqrt,real, imag,\
|
|
22
22
|
searchsorted, isscalar, fill_diagonal, arange, zeros_like, sum, ndarray
|
|
23
|
+
from scipy import fft
|
|
23
24
|
from traits.api import HasPrivateTraits, Int, Property, Instance, Trait, \
|
|
24
25
|
Bool, cached_property, property_depends_on, Delegate, Float, Enum, \
|
|
25
26
|
CArray
|
|
@@ -658,7 +659,6 @@ class PowerSpectraImport( PowerSpectra ):
|
|
|
658
659
|
|
|
659
660
|
#: The cross spectral matrix,
|
|
660
661
|
#: (number of frequencies, numchannels, numchannels) array of complex;
|
|
661
|
-
#: readonly.
|
|
662
662
|
csm = Property(
|
|
663
663
|
desc="cross spectral matrix")
|
|
664
664
|
|
acoular/tbeamform.py
CHANGED
|
@@ -245,13 +245,13 @@ class BeamformerTime( TimeInOut ):
|
|
|
245
245
|
n_index = arange(0,num+1)[:,newaxis]
|
|
246
246
|
c = self.steer.env.c/self.source.sample_freq
|
|
247
247
|
amp = empty((1,self.grid.size,numMics),dtype=fdtype)
|
|
248
|
-
delays = empty((1,self.grid.size,numMics),dtype=fdtype)
|
|
248
|
+
#delays = empty((1,self.grid.size,numMics),dtype=fdtype)
|
|
249
249
|
d_index = empty((1,self.grid.size,numMics),dtype=idtype)
|
|
250
250
|
d_interp2 = empty((1,self.grid.size,numMics),dtype=fdtype)
|
|
251
251
|
_steer_III(self.rm[newaxis,:,:],self.r0[newaxis,:],amp)
|
|
252
|
-
_delays(self.rm[newaxis,:,:],
|
|
252
|
+
_delays(self.rm[newaxis,:,:], c, d_interp2, d_index)
|
|
253
253
|
amp.shape = amp.shape[1:]
|
|
254
|
-
delays.shape = delays.shape[1:]
|
|
254
|
+
#delays.shape = delays.shape[1:]
|
|
255
255
|
d_index.shape = d_index.shape[1:]
|
|
256
256
|
d_interp2.shape = d_interp2.shape[1:]
|
|
257
257
|
maxdelay = int((self.rm/c).max())+2 + num # +2 because of interpolation
|
|
@@ -295,7 +295,7 @@ class BeamformerTime( TimeInOut ):
|
|
|
295
295
|
else:
|
|
296
296
|
powPhi = (Phi[:num]**2).sum(0)
|
|
297
297
|
imax = argmax(powPhi)
|
|
298
|
-
t_float =
|
|
298
|
+
t_float = d_interp2[imax] + d_index[imax] + n_index
|
|
299
299
|
t_ind = t_float.astype(int64)
|
|
300
300
|
for m in range(numMics):
|
|
301
301
|
p_res[t_ind[:num+1,m],m] -= self.damp*interp(t_ind[:num+1,m],
|
|
@@ -485,7 +485,7 @@ class BeamformerTimeTraj( BeamformerTime ):
|
|
|
485
485
|
blockrm = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
486
486
|
blockrmconv = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
487
487
|
amp = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
488
|
-
delays = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
488
|
+
#delays = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
489
489
|
d_index = empty((num,self.grid.size,numMics),dtype=idtype)
|
|
490
490
|
d_interp2 = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
491
491
|
blockr0 = empty((num,self.grid.size),dtype=fdtype)
|
|
@@ -521,7 +521,7 @@ class BeamformerTimeTraj( BeamformerTime ):
|
|
|
521
521
|
steer_func(blockrmconv, blockr0, amp)
|
|
522
522
|
else:
|
|
523
523
|
steer_func(blockrm, blockr0, amp)
|
|
524
|
-
_delays(blockrm,
|
|
524
|
+
_delays(blockrm, c, d_interp2, d_index)
|
|
525
525
|
#_modf(delays, d_interp2, d_index)
|
|
526
526
|
maxdelay = (d_index.max((1,2)) + arange(0,num)).max()+2 # + because of interpolation
|
|
527
527
|
# increase buffer size because of greater delays
|
|
@@ -548,6 +548,11 @@ class BeamformerTimeTraj( BeamformerTime ):
|
|
|
548
548
|
else:
|
|
549
549
|
yield Phi[:num]**2
|
|
550
550
|
else:
|
|
551
|
+
# choose correct distance
|
|
552
|
+
if self.conv_amp:
|
|
553
|
+
blockrm1 = blockrmconv
|
|
554
|
+
else:
|
|
555
|
+
blockrm1 = blockrm
|
|
551
556
|
Gamma = zeros(Phi.shape,dtype=fdtype)
|
|
552
557
|
Gamma_autopow = zeros(Phi.shape,dtype=fdtype)
|
|
553
558
|
J = 0
|
|
@@ -562,7 +567,9 @@ class BeamformerTimeTraj( BeamformerTime ):
|
|
|
562
567
|
# find index of max power focus point
|
|
563
568
|
imax = argmax(powPhi)
|
|
564
569
|
# find backward delays
|
|
565
|
-
t_float = (
|
|
570
|
+
t_float = (d_interp2[:num,imax,m_index] + \
|
|
571
|
+
d_index[:num,imax,m_index] + \
|
|
572
|
+
n_index).astype(fdtype)
|
|
566
573
|
# determine max/min delays in sample units
|
|
567
574
|
# + 2 because we do not want to extrapolate behind the last sample
|
|
568
575
|
ind_max = t_float.max(0).astype(idtype)+2
|
|
@@ -574,7 +581,7 @@ class BeamformerTimeTraj( BeamformerTime ):
|
|
|
574
581
|
p_res[ind_min[m]:ind_max[m],m] -= self.damp*interp(
|
|
575
582
|
t_ind[ind_min[m]:ind_max[m]],
|
|
576
583
|
t_float[:num,m],
|
|
577
|
-
h/
|
|
584
|
+
h/blockrm1[:num,imax,m],
|
|
578
585
|
)
|
|
579
586
|
nextPhi, nextAutopow = self.delay_and_sum(num,p_res,d_interp2,d_index,amp)
|
|
580
587
|
if self.r_diag:
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -21,7 +21,14 @@ RectGrid, BeamformerBase, BeamformerEig, BeamformerOrth, BeamformerCleansc, \
|
|
|
21
21
|
MaskedTimeSamples, BeamformerCMF, \
|
|
22
22
|
BeamformerCapon, BeamformerMusic, BeamformerDamas, BeamformerClean, \
|
|
23
23
|
BeamformerFunctional, BeamformerDamasPlus, BeamformerGIB, BeamformerGridlessOrth,\
|
|
24
|
-
SteeringVector, Environment
|
|
24
|
+
SteeringVector, Environment, BeamformerSODIX
|
|
25
|
+
|
|
26
|
+
# copy CMF classes as workaround so that reference data name is unique later
|
|
27
|
+
class BeamformerCMFLassoLarsBIC (BeamformerCMF):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
class BeamformerCMFNNLS (BeamformerCMF):
|
|
31
|
+
pass
|
|
25
32
|
|
|
26
33
|
# if this flag is set to True
|
|
27
34
|
WRITE_NEW_REFERENCE_DATA = False
|
|
@@ -68,14 +75,16 @@ def fbeamformers():
|
|
|
68
75
|
bm = BeamformerMusic(freq_data=f, steer=st, n=6, cached = False)
|
|
69
76
|
bd = BeamformerDamas(beamformer=bb, n_iter=10, cached = False)
|
|
70
77
|
bdp = BeamformerDamasPlus(beamformer=bb, n_iter=100, cached = False)
|
|
71
|
-
bo = BeamformerOrth(
|
|
78
|
+
bo = BeamformerOrth(freq_data=f, steer=st, r_diag=True, eva_list=list(range(38,54)), cached = False)
|
|
72
79
|
bs = BeamformerCleansc(freq_data=f, steer=st, r_diag=True, cached = False)
|
|
73
|
-
|
|
80
|
+
bcmflassobic = BeamformerCMFLassoLarsBIC(freq_data=f, steer=st, method='LassoLarsBIC', cached = False)
|
|
81
|
+
bcmfnnls = BeamformerCMFNNLS(freq_data=f, steer=st, method='NNLS', cached = False)
|
|
74
82
|
bl = BeamformerClean(beamformer=bb, n_iter=10, cached = False)
|
|
75
83
|
bf = BeamformerFunctional(freq_data=f, steer=st, r_diag=False, gamma=3, cached = False)
|
|
76
84
|
bgib = BeamformerGIB(freq_data=f, steer=st, method= 'LassoLars', n=2, cached = False)
|
|
77
85
|
bgo = BeamformerGridlessOrth(freq_data=f, steer=st, r_diag=False, n=1, shgo={'n':16}, cached = False)
|
|
78
|
-
|
|
86
|
+
bsodix = BeamformerSODIX(freq_data=f, steer=st,max_iter=10, cached = False)
|
|
87
|
+
return (bbase, bc, beig, bm, bl, bo, bs, bd, bcmflassobic, bcmfnnls, bf, bdp, bgib, bgo,bsodix)
|
|
79
88
|
|
|
80
89
|
class acoular_beamformer_test(unittest.TestCase):
|
|
81
90
|
|
|
@@ -90,7 +99,7 @@ class acoular_beamformer_test(unittest.TestCase):
|
|
|
90
99
|
if WRITE_NEW_REFERENCE_DATA:
|
|
91
100
|
np.save(name,actual_data)
|
|
92
101
|
ref_data = np.load(name)
|
|
93
|
-
np.testing.assert_allclose(actual_data, ref_data, rtol=
|
|
102
|
+
np.testing.assert_allclose(actual_data, ref_data, rtol=5e-5, atol=5e-8)
|
|
94
103
|
# we expect the results to be computed and written to cache
|
|
95
104
|
acoular.config.global_caching = 'individual'
|
|
96
105
|
for b in fbeamformers():
|
|
@@ -99,7 +108,7 @@ class acoular_beamformer_test(unittest.TestCase):
|
|
|
99
108
|
name = join('reference_data',f'{b.__class__.__name__}.npy')
|
|
100
109
|
actual_data = np.array([b.synthetic(cf,1) for cf in cfreqs],dtype=np.float32)
|
|
101
110
|
ref_data = np.load(name)
|
|
102
|
-
np.testing.assert_allclose(actual_data, ref_data, rtol=
|
|
111
|
+
np.testing.assert_allclose(actual_data, ref_data, rtol=5e-5, atol=5e-8)
|
|
103
112
|
# we expect the results to be read from cache
|
|
104
113
|
acoular.config.global_caching = 'all'
|
|
105
114
|
for b in fbeamformers():
|
|
@@ -108,7 +117,7 @@ class acoular_beamformer_test(unittest.TestCase):
|
|
|
108
117
|
name = join('reference_data',f'{b.__class__.__name__}.npy')
|
|
109
118
|
actual_data = np.array([b.synthetic(cf,1) for cf in cfreqs],dtype=np.float32)
|
|
110
119
|
ref_data = np.load(name)
|
|
111
|
-
np.testing.assert_allclose(actual_data, ref_data, rtol=
|
|
120
|
+
np.testing.assert_allclose(actual_data, ref_data, rtol=5e-5, atol=5e-8)
|
|
112
121
|
# we expect the cached results to be overwritten
|
|
113
122
|
acoular.config.global_caching = 'overwrite'
|
|
114
123
|
for b0,b1 in zip(fbeamformers(),fbeamformers()):
|
|
@@ -123,7 +132,7 @@ class acoular_beamformer_test(unittest.TestCase):
|
|
|
123
132
|
name = join('reference_data',f'{b1.__class__.__name__}.npy')
|
|
124
133
|
actual_data = np.array([b1.synthetic(cf,1) for cf in cfreqs],dtype=np.float32)
|
|
125
134
|
ref_data = np.load(name)
|
|
126
|
-
np.testing.assert_allclose(actual_data, ref_data, rtol=
|
|
135
|
+
np.testing.assert_allclose(actual_data, ref_data, rtol=5e-5, atol=5e-8)
|
|
127
136
|
|
|
128
137
|
def test_beamformer_caching(self):
|
|
129
138
|
# within each subcase, we need new beamformer objects because result is not updated when
|
|
@@ -174,6 +183,28 @@ class Test_PowerSpectra(unittest.TestCase):
|
|
|
174
183
|
np.testing.assert_allclose(actual_data, ref_data, rtol=1e-5, atol=1e-8)
|
|
175
184
|
|
|
176
185
|
|
|
186
|
+
class TestSteerFormulation(unittest.TestCase):
|
|
187
|
+
|
|
188
|
+
def test_all_steer_formulation(self):
|
|
189
|
+
""" tests all variants of beamformerFreq subroutines
|
|
190
|
+
"""
|
|
191
|
+
st = SteeringVector(grid=g, mics=m, env=env)
|
|
192
|
+
b0 = BeamformerBase(freq_data=f, steer=st, cached = False)
|
|
193
|
+
b1 = BeamformerEig(freq_data=f, steer=st, n=54, cached = False)
|
|
194
|
+
for ki,kind in enumerate(('classic', 'inverse','true level', 'true location')):
|
|
195
|
+
st.steer_type = kind
|
|
196
|
+
for b in (b0,b1):
|
|
197
|
+
for dr in (True,False):
|
|
198
|
+
b.r_diag = dr
|
|
199
|
+
with self.subTest(f"{b.__class__.__name__} r_diag:{dr} steer:{kind}"):
|
|
200
|
+
name = join('reference_data',f"{b.__class__.__name__}{dr}{ki+1}.npy")
|
|
201
|
+
actual_data = np.array([b.synthetic(cf,1) for cf in cfreqs],dtype=np.float32)
|
|
202
|
+
if WRITE_NEW_REFERENCE_DATA:
|
|
203
|
+
np.save(name,actual_data)
|
|
204
|
+
ref_data = np.load(name)
|
|
205
|
+
np.testing.assert_allclose(actual_data, ref_data, rtol=1e-5, atol=1e-8)
|
|
206
|
+
|
|
207
|
+
|
|
177
208
|
if __name__ == '__main__':
|
|
178
209
|
unittest.main() #exit=False
|
|
179
210
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import acoular as ac
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
class GridTest(unittest.TestCase):
|
|
5
|
+
|
|
6
|
+
@staticmethod
|
|
7
|
+
def get_sector_classes():
|
|
8
|
+
# for later testing condition: sector only includes (0,0,1) point
|
|
9
|
+
sectors = [
|
|
10
|
+
ac.CircSector(x=0,y=0,r=0.2),
|
|
11
|
+
ac.RectSector(x_min=-0.2,x_max=0.2,y_min=-0.2,y_max=0.2),
|
|
12
|
+
ac.RectSector3D(x_min=-0.2,x_max=0.2,y_min=-0.2,y_max=0.2,z_min=1,z_max=1),
|
|
13
|
+
ac.PolySector(edges=[0.2,0.2,-0.2,0.2,-0.2,-0.2,0.2,-0.2]),
|
|
14
|
+
ac.ConvexSector(edges=[0.2,0.2,-0.2,0.2,-0.2,-0.2,0.2,-0.2]),
|
|
15
|
+
]
|
|
16
|
+
multi_sector = ac.MultiSector(sectors=deepcopy(sectors))
|
|
17
|
+
return sectors + [multi_sector]
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def get_emtpy_sector_classes():
|
|
21
|
+
# for later testing condition: sector should not cover any grid point
|
|
22
|
+
off = 10
|
|
23
|
+
sectors = [
|
|
24
|
+
ac.CircSector(x=off,y=off,r=0.2, include_border=False, default_nearest=False),
|
|
25
|
+
ac.RectSector(
|
|
26
|
+
x_min=-0.2+off,x_max=0.2+off,y_min=-0.2+off,y_max=0.2+off,
|
|
27
|
+
include_border=False, default_nearest=False),
|
|
28
|
+
ac.RectSector3D(
|
|
29
|
+
x_min=-0.2+off,x_max=0.2+off,y_min=-0.2+off,y_max=0.2+off,z_min=1+off,z_max=1+off,
|
|
30
|
+
include_border=False, default_nearest=False),
|
|
31
|
+
ac.PolySector(
|
|
32
|
+
edges=[0.2+off,0.2+off,-0.2+off,0.2+off,-0.2+off,-0.2+off,0.2+off,-0.2+off],
|
|
33
|
+
include_border=False, default_nearest=False),
|
|
34
|
+
ac.ConvexSector(
|
|
35
|
+
edges=[0.2+off,0.2+off,-0.2+off,0.2+off,-0.2+off,-0.2+off,0.2+off,-0.2+off],
|
|
36
|
+
include_border=False, default_nearest=False)]
|
|
37
|
+
multi_sector = ac.MultiSector(
|
|
38
|
+
sectors=deepcopy(sectors))
|
|
39
|
+
return sectors + [multi_sector]
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def get_rectgrid():
|
|
43
|
+
return ac.RectGrid(x_min=-1,x_max=1,y_min=-1,y_max=1,z=1,increment=1)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def get_rectgrid3D():
|
|
47
|
+
return ac.RectGrid3D(x_min=-1,x_max=1,y_min=-1,y_max=1,z_min=1, z_max=1,increment=1)
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def get_linegrid():
|
|
51
|
+
return ac.LineGrid(loc=(-1,0,1), length=2, numpoints=3)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def get_importgrid():
|
|
55
|
+
return ac.ImportGrid(gpos_file=GridTest.get_rectgrid().gpos)
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def get_mergegrid():
|
|
59
|
+
return ac.MergeGrid(grids=[GridTest.get_rectgrid(), GridTest.get_linegrid()])
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def get_all_grids():
|
|
63
|
+
for grid in [GridTest.get_rectgrid, GridTest.get_rectgrid3D,
|
|
64
|
+
GridTest.get_linegrid, GridTest.get_importgrid, GridTest.get_mergegrid]:
|
|
65
|
+
yield grid()
|
|
66
|
+
|
|
67
|
+
def test_size(self):
|
|
68
|
+
for grid in self.get_all_grids():
|
|
69
|
+
with self.subTest(grid.__class__.__name__):
|
|
70
|
+
self.assertEqual(grid.size, grid.gpos.shape[-1])
|
|
71
|
+
|
|
72
|
+
def test_existing_subdomain(self):
|
|
73
|
+
for grid in self.get_all_grids():
|
|
74
|
+
for sector in self.get_sector_classes():
|
|
75
|
+
with self.subTest(f"Grid: {grid.__class__.__name__} Sector:{sector.__class__.__name__}"):
|
|
76
|
+
indices = grid.subdomain(sector)
|
|
77
|
+
self.assertEqual(indices[0].shape[0], 1)
|
|
78
|
+
|
|
79
|
+
def test_empty_subdomain(self):
|
|
80
|
+
for grid in self.get_all_grids():
|
|
81
|
+
for sector in self.get_emtpy_sector_classes():
|
|
82
|
+
with self.subTest(f"Grid: {grid.__class__.__name__} Sector:{sector.__class__.__name__}"):
|
|
83
|
+
indices = grid.subdomain(sector)
|
|
84
|
+
self.assertEqual(indices[0].shape[0], 0)
|
|
85
|
+
if hasattr(sector, 'default_nearest'):
|
|
86
|
+
sector.default_nearest = True
|
|
87
|
+
indices = grid.subdomain(sector)
|
|
88
|
+
self.assertEqual(indices[0].shape[0], 1)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
unittest.main()
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import acoular as ac
|
|
3
|
+
ac.config.global_caching = "none"
|
|
4
|
+
import numpy as np
|
|
5
|
+
from test_grid import GridTest
|
|
6
|
+
from functools import partial
|
|
7
|
+
|
|
8
|
+
class TestIntegrate(unittest.TestCase):
|
|
9
|
+
|
|
10
|
+
f = [1000,2000]
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def get_sector_args():
|
|
14
|
+
# for later testing condition: sector only includes (0,0,1) point
|
|
15
|
+
return {'RectGrid': np.array([0,0,0.2]),
|
|
16
|
+
'RectGrid3D' : np.array([0,0,1,0,0,1]),}
|
|
17
|
+
|
|
18
|
+
def get_beamformer(self, grid):
|
|
19
|
+
rng1 = np.random.RandomState(1)
|
|
20
|
+
src_pos = np.array([[0],[0],[1]])
|
|
21
|
+
mics = ac.MicGeom(mpos_tot=rng1.normal(size=3*5).reshape((3,5)))
|
|
22
|
+
steer = ac.SteeringVector(
|
|
23
|
+
grid=ac.ImportGrid(
|
|
24
|
+
gpos_file=src_pos),
|
|
25
|
+
mics=mics)
|
|
26
|
+
H = np.empty((len(self.f),mics.num_mics, 1),dtype=complex)
|
|
27
|
+
for i,_f in enumerate(self.f): # calculate only the indices that are needed
|
|
28
|
+
H[i] = steer.transfer(_f).T # transfer functions
|
|
29
|
+
csm = H@H.swapaxes(2,1).conjugate()
|
|
30
|
+
freq_data = ac.PowerSpectraImport(csm = csm, frequencies=self.f)
|
|
31
|
+
steer.grid = grid
|
|
32
|
+
return ac.BeamformerBase(freq_data=freq_data, steer=steer)
|
|
33
|
+
|
|
34
|
+
def test_sector_class_integration_functional(self):
|
|
35
|
+
for sector in GridTest.get_sector_classes():
|
|
36
|
+
for grid in GridTest.get_all_grids():
|
|
37
|
+
bf = self.get_beamformer(grid)
|
|
38
|
+
with self.subTest(
|
|
39
|
+
f"Grid: {grid.__class__.__name__} Sector: {sector.__class__.__name__}"):
|
|
40
|
+
for i,f in enumerate(self.f):
|
|
41
|
+
bf_res = bf.synthetic(f)
|
|
42
|
+
bf_max = bf_res.max()
|
|
43
|
+
integration_res = ac.integrate(
|
|
44
|
+
data=bf_res,sector=sector,grid=grid)
|
|
45
|
+
self.assertEqual(integration_res.shape, ())
|
|
46
|
+
self.assertEqual(integration_res, bf_max)
|
|
47
|
+
|
|
48
|
+
def test_sector_class_integration_class(self):
|
|
49
|
+
for sector in GridTest.get_sector_classes():
|
|
50
|
+
for grid in GridTest.get_all_grids():
|
|
51
|
+
bf = self.get_beamformer(grid)
|
|
52
|
+
with self.subTest(
|
|
53
|
+
f"Grid: {grid.__class__.__name__} Sector:{sector.__class__.__name__}"):
|
|
54
|
+
for i,f in enumerate(self.f):
|
|
55
|
+
bf_res = bf.synthetic(f)
|
|
56
|
+
bf_max = bf_res.max()
|
|
57
|
+
integration_res = bf.integrate(sector)
|
|
58
|
+
self.assertEqual(integration_res.shape, (len(self.f),))
|
|
59
|
+
self.assertEqual(integration_res[i], bf_max)
|
|
60
|
+
|
|
61
|
+
def test_sector_args_integration_functional(self):
|
|
62
|
+
for grid in GridTest.get_all_grids():
|
|
63
|
+
bf = self.get_beamformer(grid)
|
|
64
|
+
with self.subTest(
|
|
65
|
+
f"Grid: {grid.__class__.__name__}"):
|
|
66
|
+
for i,f in enumerate(self.f):
|
|
67
|
+
sector = self.get_sector_args().get(grid.__class__.__name__)
|
|
68
|
+
bf_res = bf.synthetic(f)
|
|
69
|
+
bf_max = bf_res.max()
|
|
70
|
+
if sector is None: # not allowed grid for simple sector args
|
|
71
|
+
sector = np.array([0,0,0.2]) # some random circ sector arguments
|
|
72
|
+
integrate = partial(
|
|
73
|
+
ac.integrate, data=bf_res, grid=grid, sector=sector)
|
|
74
|
+
self.assertRaises(NotImplementedError, integrate)
|
|
75
|
+
else:
|
|
76
|
+
integration_res = ac.integrate(
|
|
77
|
+
data=bf_res,sector=sector,grid=grid)
|
|
78
|
+
self.assertEqual(integration_res.shape, ())
|
|
79
|
+
self.assertEqual(integration_res, bf_max)
|
|
80
|
+
|
|
81
|
+
def test_sector_args_integration_class(self):
|
|
82
|
+
for grid in GridTest.get_all_grids():
|
|
83
|
+
bf = self.get_beamformer(grid)
|
|
84
|
+
with self.subTest(
|
|
85
|
+
f"Grid: {grid.__class__.__name__}"):
|
|
86
|
+
for i,f in enumerate(self.f):
|
|
87
|
+
sector = self.get_sector_args().get(grid.__class__.__name__)
|
|
88
|
+
bf_res = bf.synthetic(f)
|
|
89
|
+
bf_max = bf_res.max()
|
|
90
|
+
if sector is None: # not allowed grid for simple sector args
|
|
91
|
+
sector = np.array([0,0,0.2]) # some random circ sector arguments
|
|
92
|
+
integrate = partial(
|
|
93
|
+
bf.integrate, sector=sector)
|
|
94
|
+
self.assertRaises(NotImplementedError, integrate)
|
|
95
|
+
else:
|
|
96
|
+
integration_res = bf.integrate(sector)
|
|
97
|
+
self.assertEqual(integration_res.shape, (len(self.f),))
|
|
98
|
+
self.assertEqual(integration_res[i], bf_max)
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
|
|
102
|
+
unittest.main()
|