acoular 25.10__py3-none-any.whl → 26.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acoular/aiaa/aiaa.py +1 -1
- acoular/base.py +7 -7
- acoular/calib.py +6 -6
- acoular/demo/__init__.py +97 -9
- acoular/demo/__main__.py +37 -0
- acoular/environments.py +24 -24
- acoular/fbeamform.py +145 -142
- acoular/fprocess.py +11 -9
- acoular/grids.py +45 -211
- acoular/microphones.py +8 -8
- acoular/process.py +7 -14
- acoular/sdinput.py +9 -9
- acoular/signals.py +10 -10
- acoular/sources.py +84 -68
- acoular/spectra.py +27 -36
- acoular/tbeamform.py +26 -26
- acoular/tools/helpers.py +1 -1
- acoular/tools/utils.py +168 -0
- acoular/tprocess.py +76 -63
- acoular/trajectory.py +1 -2
- acoular/version.py +2 -2
- {acoular-25.10.dist-info → acoular-26.1.dist-info}/METADATA +53 -108
- {acoular-25.10.dist-info → acoular-26.1.dist-info}/RECORD +26 -26
- {acoular-25.10.dist-info → acoular-26.1.dist-info}/WHEEL +1 -1
- acoular/demo/acoular_demo.py +0 -135
- {acoular-25.10.dist-info → acoular-26.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-25.10.dist-info → acoular-26.1.dist-info}/licenses/LICENSE +0 -0
acoular/tbeamform.py
CHANGED
|
@@ -83,19 +83,19 @@ class BeamformerTime(TimeOut):
|
|
|
83
83
|
#: Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
84
84
|
source = Instance(SamplesGenerator)
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
#: Instance of :class:`~acoular.fbeamform.SteeringVector` or its derived classes
|
|
87
|
+
#: that contains information about the steering vector. This is a private trait.
|
|
88
|
+
#: Do not set this directly, use :attr:`steer` trait instead.
|
|
89
89
|
steer = Instance(SteeringVector, args=())
|
|
90
90
|
|
|
91
91
|
#: Number of channels in output (=number of grid points).
|
|
92
92
|
num_channels = Property()
|
|
93
93
|
|
|
94
94
|
#: Spatial weighting function.
|
|
95
|
-
weights = Map(possible_weights, default_value='none'
|
|
95
|
+
weights = Map(possible_weights, default_value='none')
|
|
96
96
|
# (from timedomain.possible_weights)
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
#: A unique identifier for the beamformer, based on its properties. (read-only)
|
|
99
99
|
digest = Property(
|
|
100
100
|
depends_on=['steer.digest', 'source.digest', 'weights'],
|
|
101
101
|
)
|
|
@@ -231,9 +231,9 @@ class BeamformerTimeSq(BeamformerTime):
|
|
|
231
231
|
"""
|
|
232
232
|
|
|
233
233
|
#: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
|
|
234
|
-
r_diag = Bool(True
|
|
234
|
+
r_diag = Bool(True)
|
|
235
235
|
|
|
236
|
-
|
|
236
|
+
#: A unique identifier for the beamformer, based on its properties. (read-only)
|
|
237
237
|
digest = Property(
|
|
238
238
|
depends_on=['steer.digest', 'source.digest', 'r_diag', 'weights'],
|
|
239
239
|
)
|
|
@@ -275,18 +275,18 @@ class BeamformerTimeTraj(BeamformerTime):
|
|
|
275
275
|
|
|
276
276
|
#: :class:`~acoular.trajectory.Trajectory` or derived object.
|
|
277
277
|
#: Start time is assumed to be the same as for the samples.
|
|
278
|
-
trajectory = Instance(Trajectory
|
|
278
|
+
trajectory = Instance(Trajectory)
|
|
279
279
|
|
|
280
280
|
#: Reference vector, perpendicular to the y-axis of moving grid.
|
|
281
|
-
rvec = CArray(dtype=float, shape=(3,), value=np.array((0, 0, 0))
|
|
281
|
+
rvec = CArray(dtype=float, shape=(3,), value=np.array((0, 0, 0)))
|
|
282
282
|
|
|
283
283
|
#: Considering of convective amplification in beamforming formula.
|
|
284
|
-
conv_amp = Bool(False
|
|
284
|
+
conv_amp = Bool(False)
|
|
285
285
|
|
|
286
286
|
#: Floating point and integer precision
|
|
287
|
-
precision = Enum(64, 32
|
|
287
|
+
precision = Enum(64, 32)
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
#: A unique identifier for the beamformer, based on its properties. (read-only)
|
|
290
290
|
digest = Property(
|
|
291
291
|
depends_on=[
|
|
292
292
|
'steer.digest',
|
|
@@ -325,7 +325,7 @@ class BeamformerTimeTraj(BeamformerTime):
|
|
|
325
325
|
tpos = gpos + np.array(g)[:, np.newaxis]
|
|
326
326
|
yield tpos
|
|
327
327
|
else:
|
|
328
|
-
for g, g1 in zip(trajg, trajg1):
|
|
328
|
+
for g, g1 in zip(trajg, trajg1, strict=True):
|
|
329
329
|
# grid is both translated and rotated
|
|
330
330
|
loc = np.array(g) # translation array([0., 0.4, 1.])
|
|
331
331
|
dx = np.array(g1) # direction vector (new x-axis)
|
|
@@ -520,7 +520,7 @@ class BeamformerTimeSqTraj(BeamformerTimeSq, BeamformerTimeTraj):
|
|
|
520
520
|
removal for a grid moving along a trajectory.
|
|
521
521
|
"""
|
|
522
522
|
|
|
523
|
-
|
|
523
|
+
#: A unique identifier for the beamformer, based on its properties. (read-only)
|
|
524
524
|
digest = Property(
|
|
525
525
|
depends_on=[
|
|
526
526
|
'steer.digest',
|
|
@@ -571,16 +571,16 @@ class BeamformerCleant(BeamformerTime):
|
|
|
571
571
|
"""
|
|
572
572
|
|
|
573
573
|
#: Boolean flag, always False
|
|
574
|
-
r_diag = Enum(False
|
|
574
|
+
r_diag = Enum(False)
|
|
575
575
|
|
|
576
576
|
#: iteration damping factor also referred as loop gain in Cousson et al.
|
|
577
577
|
#: defaults to 0.6
|
|
578
|
-
damp = Range(0.01, 1.0, 0.6
|
|
578
|
+
damp = Range(0.01, 1.0, 0.6)
|
|
579
579
|
|
|
580
580
|
#: max number of iterations
|
|
581
|
-
n_iter = Int(100
|
|
581
|
+
n_iter = Int(100)
|
|
582
582
|
|
|
583
|
-
|
|
583
|
+
#: A unique identifier for the beamformer, based on its properties. (read-only)
|
|
584
584
|
digest = Property(
|
|
585
585
|
depends_on=['steer.digest', 'source.digest', 'weights', 'damp', 'n_iter'],
|
|
586
586
|
)
|
|
@@ -623,9 +623,9 @@ class BeamformerCleantSq(BeamformerCleant):
|
|
|
623
623
|
"""
|
|
624
624
|
|
|
625
625
|
#: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
|
|
626
|
-
r_diag = Bool(True
|
|
626
|
+
r_diag = Bool(True)
|
|
627
627
|
|
|
628
|
-
|
|
628
|
+
#: A unique identifier for the beamformer, based on its properties. (read-only)
|
|
629
629
|
digest = Property(
|
|
630
630
|
depends_on=['steer.digest', 'source.digest', 'weights', 'damp', 'n_iter', 'r_diag'],
|
|
631
631
|
)
|
|
@@ -668,9 +668,9 @@ class BeamformerCleantTraj(BeamformerCleant, BeamformerTimeTraj):
|
|
|
668
668
|
"""
|
|
669
669
|
|
|
670
670
|
#: Floating point and integer precision
|
|
671
|
-
precision = Enum(32, 64
|
|
671
|
+
precision = Enum(32, 64)
|
|
672
672
|
|
|
673
|
-
|
|
673
|
+
#: A unique identifier for the beamformer, based on its properties. (read-only)
|
|
674
674
|
digest = Property(
|
|
675
675
|
depends_on=[
|
|
676
676
|
'steer.digest',
|
|
@@ -723,9 +723,9 @@ class BeamformerCleantSqTraj(BeamformerCleantTraj, BeamformerTimeSq):
|
|
|
723
723
|
"""
|
|
724
724
|
|
|
725
725
|
#: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
|
|
726
|
-
r_diag = Bool(True
|
|
726
|
+
r_diag = Bool(True)
|
|
727
727
|
|
|
728
|
-
|
|
728
|
+
#: A unique identifier for the beamformer, based on its properties. (read-only)
|
|
729
729
|
digest = Property(
|
|
730
730
|
depends_on=[
|
|
731
731
|
'steer.digest',
|
|
@@ -777,7 +777,7 @@ class IntegratorSectorTime(TimeOut):
|
|
|
777
777
|
source = Instance(SamplesGenerator)
|
|
778
778
|
|
|
779
779
|
#: :class:`~acoular.grids.RectGrid` object that provides the grid locations.
|
|
780
|
-
grid = Instance(RectGrid
|
|
780
|
+
grid = Instance(RectGrid)
|
|
781
781
|
|
|
782
782
|
#: List of sectors in grid
|
|
783
783
|
sectors = List()
|
|
@@ -788,7 +788,7 @@ class IntegratorSectorTime(TimeOut):
|
|
|
788
788
|
#: Number of channels in output (= number of sectors).
|
|
789
789
|
num_channels = Property(depends_on=['sectors'])
|
|
790
790
|
|
|
791
|
-
|
|
791
|
+
#: A unique identifier for the integrator, based on its properties. (read-only)
|
|
792
792
|
digest = Property(
|
|
793
793
|
depends_on=['sectors', 'clip', 'grid.digest', 'source.digest'],
|
|
794
794
|
)
|
acoular/tools/helpers.py
CHANGED
|
@@ -144,7 +144,7 @@ def return_result(source, nmax=-1, num=128):
|
|
|
144
144
|
|
|
145
145
|
if nmax > 0:
|
|
146
146
|
nblocks = (nmax - 1) // num + 1
|
|
147
|
-
return np.concatenate([res for _, res in zip(range(nblocks), resulter)])[:nmax]
|
|
147
|
+
return np.concatenate([res for _, res in zip(range(nblocks), resulter, strict=True)])[:nmax]
|
|
148
148
|
return np.concatenate(list(resulter))
|
|
149
149
|
|
|
150
150
|
|
acoular/tools/utils.py
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
get_file_basename
|
|
7
7
|
find_basename
|
|
8
8
|
mole_fraction_of_water_vapor
|
|
9
|
+
Polygon
|
|
9
10
|
"""
|
|
10
11
|
|
|
11
12
|
from pathlib import Path
|
|
@@ -13,6 +14,173 @@ from pathlib import Path
|
|
|
13
14
|
import numpy as np
|
|
14
15
|
|
|
15
16
|
|
|
17
|
+
def _det(xvert, yvert):
|
|
18
|
+
xvert = np.asarray(xvert, dtype=float)
|
|
19
|
+
yvert = np.asarray(yvert, dtype=float)
|
|
20
|
+
x_prev = np.concatenate(([xvert[-1]], xvert[:-1]))
|
|
21
|
+
y_prev = np.concatenate(([yvert[-1]], yvert[:-1]))
|
|
22
|
+
return np.sum(yvert * x_prev - xvert * y_prev, axis=0)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Polygon:
|
|
26
|
+
"""
|
|
27
|
+
Create an object representing a general polygon in a 2D plane.
|
|
28
|
+
|
|
29
|
+
This class allows defining a polygon by specifying the coordinates of its vertices and provides
|
|
30
|
+
methods for checking whether a set of points lies inside the polygon, or if a point is closer to
|
|
31
|
+
a side or vertex of the polygon.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
x : array_like
|
|
36
|
+
Array of x-coordinates of the vertices that define the polygon. These coordinates should
|
|
37
|
+
form a closed shape (i.e., the last point should be the same as the first point).
|
|
38
|
+
|
|
39
|
+
y : array_like
|
|
40
|
+
Array of y-coordinates of the vertices that define the polygon. These coordinates should
|
|
41
|
+
correspond to the x-coordinates, forming a closed shape.
|
|
42
|
+
|
|
43
|
+
Attributes
|
|
44
|
+
----------
|
|
45
|
+
x : :class:`numpy.ndarray`
|
|
46
|
+
Array of x-coordinates of the polygon vertices.
|
|
47
|
+
|
|
48
|
+
y : :class:`numpy.ndarray`
|
|
49
|
+
Array of y-coordinates of the polygon vertices.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, x, y):
|
|
53
|
+
if len(x) != len(y):
|
|
54
|
+
msg = 'x and y must be equally sized.'
|
|
55
|
+
raise IndexError(msg)
|
|
56
|
+
self.x = np.asarray(x, dtype=float)
|
|
57
|
+
self.y = np.asarray(y, dtype=float)
|
|
58
|
+
# Closes the polygon if it were open
|
|
59
|
+
x1, y1 = x[0], y[0]
|
|
60
|
+
xn, yn = x[-1], y[-1]
|
|
61
|
+
if x1 != xn or y1 != yn:
|
|
62
|
+
self.x = np.concatenate((self.x, [x1]))
|
|
63
|
+
self.y = np.concatenate((self.y, [y1]))
|
|
64
|
+
# Anti-clockwise coordinates
|
|
65
|
+
if _det(self.x, self.y) < 0:
|
|
66
|
+
self.x = self.x[::-1]
|
|
67
|
+
self.y = self.y[::-1]
|
|
68
|
+
|
|
69
|
+
def is_inside(self, xpoint, ypoint, smalld=1e-12):
|
|
70
|
+
"""
|
|
71
|
+
Check if a point or set of points are inside the polygon.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
xpoint : :class:`float` or array_like
|
|
76
|
+
Array of x-coordinates of the points to be tested.
|
|
77
|
+
|
|
78
|
+
ypoint : :class:`float` or array_like
|
|
79
|
+
Array of y-coordinates of the points to be tested.
|
|
80
|
+
|
|
81
|
+
smalld : :class:`float`, optional
|
|
82
|
+
Tolerance used for floating point comparisons when checking if a point is exactly on a
|
|
83
|
+
polygon's edge. The default value is ``1e-12``.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
:class:`float` or array_like
|
|
88
|
+
The distance from the point to the nearest point on the polygon. The values returned
|
|
89
|
+
have the following meanings:
|
|
90
|
+
- ``mindst < 0``: Point is outside the polygon.
|
|
91
|
+
- ``mindst = 0``: Point is on an edge of the polygon.
|
|
92
|
+
- ``mindst > 0``: Point is inside the polygon.
|
|
93
|
+
|
|
94
|
+
Notes
|
|
95
|
+
-----
|
|
96
|
+
The method uses an improved algorithm based on Nordbeck and Rydstedt for determining
|
|
97
|
+
whether a point is inside a polygon :cite:`SLOAN198545`.
|
|
98
|
+
"""
|
|
99
|
+
xpoint = np.asarray(xpoint, dtype=float)
|
|
100
|
+
ypoint = np.asarray(ypoint, dtype=float)
|
|
101
|
+
# Scalar to array
|
|
102
|
+
if xpoint.shape == ():
|
|
103
|
+
xpoint = np.array([xpoint], dtype=float)
|
|
104
|
+
ypoint = np.array([ypoint], dtype=float)
|
|
105
|
+
scalar = True
|
|
106
|
+
else:
|
|
107
|
+
scalar = False
|
|
108
|
+
# Check consistency
|
|
109
|
+
if xpoint.shape != ypoint.shape:
|
|
110
|
+
msg = 'x and y have different shapes'
|
|
111
|
+
raise IndexError(msg)
|
|
112
|
+
# If snear = True: Dist to nearest side < nearest vertex
|
|
113
|
+
# If snear = False: Dist to nearest vertex < nearest side
|
|
114
|
+
snear = np.ma.masked_all(xpoint.shape, dtype=bool)
|
|
115
|
+
# Initialize arrays
|
|
116
|
+
mindst = np.ones_like(xpoint, dtype=float) * np.inf
|
|
117
|
+
j = np.ma.masked_all(xpoint.shape, dtype=int)
|
|
118
|
+
x = self.x
|
|
119
|
+
y = self.y
|
|
120
|
+
n = len(x) - 1 # Number of sides/vertices defining the polygon
|
|
121
|
+
# Loop over each side defining polygon
|
|
122
|
+
for i in range(n):
|
|
123
|
+
d = np.ones_like(xpoint, dtype=float) * np.inf
|
|
124
|
+
# Start of side has coords (x1, y1)
|
|
125
|
+
# End of side has coords (x2, y2)
|
|
126
|
+
# Point has coords (xpoint, ypoint)
|
|
127
|
+
x1 = x[i]
|
|
128
|
+
y1 = y[i]
|
|
129
|
+
x21 = x[i + 1] - x1
|
|
130
|
+
y21 = y[i + 1] - y1
|
|
131
|
+
x1p = x1 - xpoint
|
|
132
|
+
y1p = y1 - ypoint
|
|
133
|
+
# Points on infinite line defined by
|
|
134
|
+
# x = x1 + t * (x1 - x2)
|
|
135
|
+
# y = y1 + t * (y1 - y2)
|
|
136
|
+
# where
|
|
137
|
+
# t = 0 at (x1, y1)
|
|
138
|
+
# t = 1 at (x2, y2)
|
|
139
|
+
# Find where normal passing through (xpoint, ypoint) intersects
|
|
140
|
+
# infinite line
|
|
141
|
+
t = -(x1p * x21 + y1p * y21) / (x21**2 + y21**2)
|
|
142
|
+
tlt0 = t < 0
|
|
143
|
+
tle1 = (t >= 0) & (t <= 1)
|
|
144
|
+
# Normal intersects side
|
|
145
|
+
d[tle1] = (x1p[tle1] + t[tle1] * x21) ** 2 + (y1p[tle1] + t[tle1] * y21) ** 2
|
|
146
|
+
# Normal does not intersects side
|
|
147
|
+
# Point is closest to vertex (x1, y1)
|
|
148
|
+
# Compute square of distance to this vertex
|
|
149
|
+
d[tlt0] = x1p[tlt0] ** 2 + y1p[tlt0] ** 2
|
|
150
|
+
# Store distances
|
|
151
|
+
mask = d < mindst
|
|
152
|
+
mindst[mask] = d[mask]
|
|
153
|
+
j[mask] = i
|
|
154
|
+
# Point is closer to (x1, y1) than any other vertex or side
|
|
155
|
+
snear[mask & tlt0] = False
|
|
156
|
+
# Point is closer to this side than to any other side or vertex
|
|
157
|
+
snear[mask & tle1] = True
|
|
158
|
+
if np.ma.count(snear) != snear.size:
|
|
159
|
+
msg = 'Error computing distances'
|
|
160
|
+
raise IndexError(msg)
|
|
161
|
+
mindst **= 0.5
|
|
162
|
+
# Point is closer to its nearest vertex than its nearest side, check if
|
|
163
|
+
# nearest vertex is concave.
|
|
164
|
+
# If the nearest vertex is concave then point is inside the polygon,
|
|
165
|
+
# else the point is outside the polygon.
|
|
166
|
+
jo = j.copy()
|
|
167
|
+
jo[j == 0] -= 1
|
|
168
|
+
area = _det([x[j + 1], x[j], x[jo - 1]], [y[j + 1], y[j], y[jo - 1]])
|
|
169
|
+
mindst[~snear] = np.copysign(mindst, area)[~snear]
|
|
170
|
+
# Point is closer to its nearest side than to its nearest vertex, check
|
|
171
|
+
# if point is to left or right of this side.
|
|
172
|
+
# If point is to left of side it is inside polygon, else point is
|
|
173
|
+
# outside polygon.
|
|
174
|
+
area = _det([x[j], x[j + 1], xpoint], [y[j], y[j + 1], ypoint])
|
|
175
|
+
mindst[snear] = np.copysign(mindst, area)[snear]
|
|
176
|
+
# Point is on side of polygon
|
|
177
|
+
mindst[np.fabs(mindst) < smalld] = 0
|
|
178
|
+
# If input values were scalar then the output should be too
|
|
179
|
+
if scalar:
|
|
180
|
+
mindst = float(mindst[0])
|
|
181
|
+
return mindst
|
|
182
|
+
|
|
183
|
+
|
|
16
184
|
def get_file_basename(file, alternative_basename='void'):
|
|
17
185
|
"""Return the basename of the file.
|
|
18
186
|
|