acoular 25.7__py3-none-any.whl → 25.10__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/sources.py CHANGED
@@ -4,6 +4,12 @@
4
4
  """
5
5
  Measured multichannel data management and simulation of acoustic sources.
6
6
 
7
+ .. inheritance-diagram::
8
+ acoular.sources
9
+ :top-classes:
10
+ acoular.base.SamplesGenerator
11
+ :parts: 1
12
+
7
13
  .. autosummary::
8
14
  :toctree: generated/
9
15
 
@@ -31,32 +37,9 @@ from os import path
31
37
  from warnings import warn
32
38
 
33
39
  import numba as nb
34
- from numpy import any as npany
35
- from numpy import (
36
- arange,
37
- arctan2,
38
- array,
39
- ceil,
40
- complex128,
41
- cross,
42
- dot,
43
- empty,
44
- int64,
45
- mod,
46
- newaxis,
47
- ones,
48
- ones_like,
49
- pi,
50
- real,
51
- repeat,
52
- sqrt,
53
- tile,
54
- uint32,
55
- zeros,
56
- )
57
- from numpy import min as npmin
40
+ import numpy as np
41
+ import scipy.linalg as spla
58
42
  from numpy.fft import fft, ifft
59
- from scipy.linalg import norm
60
43
  from scipy.special import sph_harm, spherical_jn, spherical_yn
61
44
  from traits.api import (
62
45
  Any,
@@ -77,14 +60,11 @@ from traits.api import (
77
60
  Union,
78
61
  cached_property,
79
62
  observe,
80
- on_trait_change,
81
63
  )
82
64
 
83
65
  from .base import SamplesGenerator
84
66
 
85
67
  # acoular imports
86
- from .calib import Calib
87
- from .deprecation import deprecated_alias
88
68
  from .environments import Environment
89
69
  from .h5files import H5FileBase, _get_h5file_class
90
70
  from .internal import digest, ldigest
@@ -110,7 +90,6 @@ def _fill_mic_signal_block(out, signal, rm, ind, blocksize, num_channels, up, pr
110
90
  for m in range(num_channels):
111
91
  out[b, m] = signal[int(0.5 + ind[0, m])] / rm[0, m]
112
92
  ind += up
113
- return out
114
93
 
115
94
 
116
95
  def spherical_hn1(n, z):
@@ -202,7 +181,7 @@ def get_radiation_angles(direction, mpos, sourceposition):
202
181
  --------
203
182
  :func:`numpy.linalg.norm` :
204
183
  Computes the norm of a vector.
205
- :func:`numpy.arctan2` :
184
+ :obj:`numpy.arctan2` :
206
185
  Computes the arctangent of two variables, preserving quadrant information.
207
186
 
208
187
  Notes
@@ -228,21 +207,21 @@ def get_radiation_angles(direction, mpos, sourceposition):
228
207
  array([4.71238898, 4.71238898])
229
208
  """
230
209
  # direction of the Spherical Harmonics
231
- direc = array(direction, dtype=float)
232
- direc = direc / norm(direc)
210
+ direc = np.array(direction, dtype=float)
211
+ direc = direc / spla.norm(direc)
233
212
  # distances
234
- source_to_mic_vecs = mpos - array(sourceposition).reshape((3, 1))
213
+ source_to_mic_vecs = mpos - np.array(sourceposition).reshape((3, 1))
235
214
  source_to_mic_vecs[2] *= -1 # invert z-axis (acoular) #-1
236
215
  # z-axis (acoular) -> y-axis (spherical)
237
216
  # y-axis (acoular) -> z-axis (spherical)
238
217
  # theta
239
- ele = arctan2(sqrt(source_to_mic_vecs[0] ** 2 + source_to_mic_vecs[2] ** 2), source_to_mic_vecs[1])
240
- ele += arctan2(sqrt(direc[0] ** 2 + direc[2] ** 2), direc[1])
241
- ele += pi * 0.5 # convert from [-pi/2, pi/2] to [0,pi] range
218
+ ele = np.arctan2(np.sqrt(source_to_mic_vecs[0] ** 2 + source_to_mic_vecs[2] ** 2), source_to_mic_vecs[1])
219
+ ele += np.arctan2(np.sqrt(direc[0] ** 2 + direc[2] ** 2), direc[1])
220
+ ele += np.pi * 0.5 # convert from [-pi/2, pi/2] to [0,pi] range
242
221
  # phi
243
- azi = arctan2(source_to_mic_vecs[2], source_to_mic_vecs[0])
244
- azi += arctan2(direc[2], direc[0])
245
- azi = mod(azi, 2 * pi)
222
+ azi = np.arctan2(source_to_mic_vecs[2], source_to_mic_vecs[0])
223
+ azi += np.arctan2(direc[2], direc[0])
224
+ azi = np.mod(azi, 2 * np.pi)
246
225
  return azi, ele
247
226
 
248
227
 
@@ -305,9 +284,9 @@ def get_modes(lOrder, direction, mpos, sourceposition=None): # noqa: N803
305
284
  >>> modes.shape
306
285
  (2, 9)
307
286
  """
308
- sourceposition = sourceposition if sourceposition is not None else array([0, 0, 0])
287
+ sourceposition = sourceposition if sourceposition is not None else np.array([0, 0, 0])
309
288
  azi, ele = get_radiation_angles(direction, mpos, sourceposition) # angles between source and mics
310
- modes = zeros((azi.shape[0], (lOrder + 1) ** 2), dtype=complex128)
289
+ modes = np.zeros((azi.shape[0], (lOrder + 1) ** 2), dtype=np.complex128)
311
290
  i = 0
312
291
  for lidx in range(lOrder + 1):
313
292
  for m in range(-lidx, lidx + 1):
@@ -318,7 +297,6 @@ def get_modes(lOrder, direction, mpos, sourceposition=None): # noqa: N803
318
297
  return modes
319
298
 
320
299
 
321
- @deprecated_alias({'name': 'file'}, removal_version='25.10')
322
300
  class TimeSamples(SamplesGenerator):
323
301
  """
324
302
  Container for processing time data in ``*.h5`` or NumPy array format.
@@ -330,13 +308,12 @@ class TimeSamples(SamplesGenerator):
330
308
 
331
309
  See Also
332
310
  --------
333
- :class:`acoular.sources.MaskedTimeSamples` :
311
+ :class:`~acoular.sources.MaskedTimeSamples` :
334
312
  Extends the functionality of class :class:`TimeSamples` by enabling the definition of start
335
313
  and stop samples as well as the specification of invalid channels.
336
314
 
337
315
  Notes
338
316
  -----
339
- - If a calibration object is provided, calibrated time-domain data will be returned.
340
317
  - Metadata from the :attr:`HDF5 file<file>` can be accessed through the :attr:`metadata`
341
318
  attribute.
342
319
 
@@ -375,10 +352,6 @@ class TimeSamples(SamplesGenerator):
375
352
  #: Basename of the ``.h5`` file, set automatically from the :attr:`file` attribute.
376
353
  basename = Property(depends_on=['file'], desc='basename of data file')
377
354
 
378
- #: Calibration data, an instance of the :class:`~acoular.calib.Calib` class.
379
- #: (optional; if provided, the time data will be calibrated.)
380
- calib = Instance(Calib, desc='Calibration data')
381
-
382
355
  #: Number of input channels in the time data, set automatically based on the
383
356
  #: :attr:`loaded data<file>` or :attr:`specified array<data>`.
384
357
  num_channels = CInt(0, desc='number of input channels')
@@ -398,12 +371,10 @@ class TimeSamples(SamplesGenerator):
398
371
  metadata = Dict(desc='metadata contained in .h5 file')
399
372
 
400
373
  # Checksum over first data entries of all channels
401
- _datachecksum = Property()
374
+ _datachecksum = Property(depends_on=['data'])
402
375
 
403
376
  #: A unique identifier for the samples, based on its properties. (read-only)
404
- digest = Property(
405
- depends_on=['basename', 'calib.digest', '_datachecksum', 'sample_freq', 'num_channels', 'num_samples']
406
- )
377
+ digest = Property(depends_on=['basename', '_datachecksum', 'sample_freq', 'num_channels', 'num_samples'])
407
378
 
408
379
  def _get__datachecksum(self):
409
380
  return self.data[0, :].sum()
@@ -416,8 +387,8 @@ class TimeSamples(SamplesGenerator):
416
387
  def _get_basename(self):
417
388
  return get_file_basename(self.file)
418
389
 
419
- @on_trait_change('basename')
420
- def _load_data(self):
390
+ @observe('basename')
391
+ def _load_data(self, event): # noqa ARG002
421
392
  # Open the .h5 file and set attributes.
422
393
  if self.h5f is not None:
423
394
  with contextlib.suppress(OSError):
@@ -427,8 +398,8 @@ class TimeSamples(SamplesGenerator):
427
398
  self._load_timedata()
428
399
  self._load_metadata()
429
400
 
430
- @on_trait_change('data')
431
- def _load_shapes(self):
401
+ @observe('data')
402
+ def _load_shapes(self, event): # noqa ARG002
432
403
  # Set :attr:`num_channels` and :attr:`num_samples` from data.
433
404
  if self.data is not None:
434
405
  self.num_samples, self.num_channels = self.data.shape
@@ -450,8 +421,7 @@ class TimeSamples(SamplesGenerator):
450
421
 
451
422
  The :meth:`result` method is a Python generator that yields blocks of time-domain data
452
423
  of the specified size. Data is either read from an HDF5 file (if :attr:`file` is set)
453
- or from a NumPy array (if :attr:`data` is directly provided). If a calibration object
454
- is specified, the returned data is calibrated.
424
+ or from a NumPy array (if :attr:`data` is directly provided).
455
425
 
456
426
  Parameters
457
427
  ----------
@@ -470,14 +440,6 @@ class TimeSamples(SamplesGenerator):
470
440
  ------
471
441
  :obj:`OSError`
472
442
  If no samples are available (i.e., :attr:`num_samples` is ``0``).
473
- :obj:`ValueError`
474
- If the calibration data does not match the number of channels.
475
-
476
- Warnings
477
- --------
478
- A deprecation warning is raised if the calibration functionality is used directly in
479
- :class:`TimeSamples`. Instead, the :class:`~acoular.calib.Calib` class should be used as a
480
- separate processing block.
481
443
 
482
444
  Examples
483
445
  --------
@@ -501,36 +463,11 @@ class TimeSamples(SamplesGenerator):
501
463
  raise OSError(msg)
502
464
  self._datachecksum # trigger checksum calculation # noqa: B018
503
465
  i = 0
504
- if self.calib:
505
- warn(
506
- 'The use of the calibration functionality in TimeSamples is deprecated and will be removed in \
507
- Acoular 25.10. Use the Calib class as an additional processing block instead.',
508
- DeprecationWarning,
509
- stacklevel=2,
510
- )
511
- if self.calib.num_mics == self.num_channels:
512
- cal_factor = self.calib.data[newaxis]
513
- else:
514
- msg = f'calibration data not compatible: {self.calib.num_mics:d}, {self.num_channels:d}'
515
- raise ValueError(msg)
516
- while i < self.num_samples:
517
- yield self.data[i : i + num] * cal_factor
518
- i += num
519
- else:
520
- while i < self.num_samples:
521
- yield self.data[i : i + num]
522
- i += num
523
-
524
-
525
- @deprecated_alias(
526
- {
527
- 'numchannels_total': 'num_channels_total',
528
- 'numsamples_total': 'num_samples_total',
529
- 'numchannels': 'num_channels',
530
- 'numsamples': 'num_samples',
531
- },
532
- read_only=['numchannels', 'numsamples'],
533
- )
466
+ while i < self.num_samples:
467
+ yield self.data[i : num + i]
468
+ i += num
469
+
470
+
534
471
  class MaskedTimeSamples(TimeSamples):
535
472
  """
536
473
  Container to process and manage time-domain data with support for masking samples and channels.
@@ -543,7 +480,7 @@ class MaskedTimeSamples(TimeSamples):
543
480
 
544
481
  See Also
545
482
  --------
546
- :class:`acoular.sources.TimeSamples` : The parent class for managing unmasked time-domain data.
483
+ :class:`~acoular.sources.TimeSamples` : The parent class for managing unmasked time-domain data.
547
484
 
548
485
  Notes
549
486
  -----
@@ -610,7 +547,7 @@ class MaskedTimeSamples(TimeSamples):
610
547
  )
611
548
 
612
549
  #: A unique identifier for the samples, based on its properties. (read-only)
613
- digest = Property(depends_on=['basename', 'start', 'stop', 'calib.digest', 'invalid_channels', '_datachecksum'])
550
+ digest = Property(depends_on=['basename', 'start', 'stop', 'invalid_channels', '_datachecksum'])
614
551
 
615
552
  @cached_property
616
553
  def _get_digest(self):
@@ -621,7 +558,7 @@ class MaskedTimeSamples(TimeSamples):
621
558
  if len(self.invalid_channels) == 0:
622
559
  return slice(0, None, None)
623
560
  allr = [i for i in range(self.num_channels_total) if i not in self.invalid_channels]
624
- return array(allr)
561
+ return np.array(allr)
625
562
 
626
563
  @cached_property
627
564
  def _get_num_channels(self):
@@ -634,8 +571,8 @@ class MaskedTimeSamples(TimeSamples):
634
571
  sli = slice(self.start, self.stop).indices(self.num_samples_total)
635
572
  return sli[1] - sli[0]
636
573
 
637
- @on_trait_change('basename')
638
- def _load_data(self):
574
+ @observe('basename')
575
+ def _load_data(self, event): # noqa ARG002
639
576
  # Open the .h5 file and set attributes.
640
577
  if not path.isfile(self.file):
641
578
  # no file there
@@ -650,8 +587,8 @@ class MaskedTimeSamples(TimeSamples):
650
587
  self._load_timedata()
651
588
  self._load_metadata()
652
589
 
653
- @on_trait_change('data')
654
- def _load_shapes(self):
590
+ @observe('data')
591
+ def _load_shapes(self, event): # noqa ARG002
655
592
  # Set :attr:`num_channels` and num_samples from :attr:`~acoular.sources.TimeSamples.data`.
656
593
  if self.data is not None:
657
594
  self.num_samples_total, self.num_channels_total = self.data.shape
@@ -667,8 +604,7 @@ class MaskedTimeSamples(TimeSamples):
667
604
  Generate blocks of valid time-domain data iteratively.
668
605
 
669
606
  The :meth:`result` method is a Python generator that yields blocks of valid time-domain data
670
- based on the specified :attr:`start` and :attr:`stop` indices and the valid channels. Data
671
- can be calibrated if a calibration object, given by :attr:`calib`, is provided.
607
+ based on the specified :attr:`start` and :attr:`stop` indices and the valid channels.
672
608
 
673
609
  Parameters
674
610
  ----------
@@ -688,15 +624,6 @@ class MaskedTimeSamples(TimeSamples):
688
624
  :obj:`OSError`
689
625
  If no valid samples are available (i.e., :attr:`start` and :attr:`stop` indices result
690
626
  in an empty range).
691
- :obj:`ValueError`
692
- If the :attr:`calibration data<calib>` is incompatible with the
693
- :attr:`number of valid channels<num_channels>`.
694
-
695
- Warnings
696
- --------
697
- A deprecation warning is raised if the calibration functionality is used directly in
698
- :class:`MaskedTimeSamples`. Instead, the :class:`acoular.calib.Calib` class should be used
699
- as a separate processing block.
700
627
 
701
628
  Examples
702
629
  --------
@@ -721,33 +648,15 @@ class MaskedTimeSamples(TimeSamples):
721
648
  sli = slice(self.start, self.stop).indices(self.num_samples_total)
722
649
  i = sli[0]
723
650
  stop = sli[1]
724
- cal_factor = 1.0
725
651
  if i >= stop:
726
652
  msg = 'no samples available'
727
653
  raise OSError(msg)
728
654
  self._datachecksum # trigger checksum calculation # noqa: B018
729
- if self.calib:
730
- warn(
731
- 'The use of the calibration functionality in MaskedTimeSamples is deprecated and will be removed in \
732
- Acoular 25.10. Use the Calib class as an additional processing block instead.',
733
- DeprecationWarning,
734
- stacklevel=2,
735
- )
736
- if self.calib.num_mics == self.num_channels_total:
737
- cal_factor = self.calib.data[self.channels][newaxis]
738
- elif self.calib.num_mics == self.num_channels:
739
- cal_factor = self.calib.data[newaxis]
740
- elif self.calib.num_mics == 0:
741
- warn('No calibration data used.', Warning, stacklevel=2)
742
- else:
743
- msg = f'calibration data not compatible: {self.calib.num_mics:d}, {self.num_channels:d}'
744
- raise ValueError(msg)
745
655
  while i < stop:
746
- yield self.data[i : min(i + num, stop)][:, self.channels] * cal_factor
656
+ yield self.data[i : min(i + num, stop)][:, self.channels]
747
657
  i += num
748
658
 
749
659
 
750
- @deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples'}, read_only=True, removal_version='25.10')
751
660
  class PointSource(SamplesGenerator):
752
661
  """
753
662
  Define a fixed point source emitting a signal, intended for simulations.
@@ -759,9 +668,9 @@ class PointSource(SamplesGenerator):
759
668
 
760
669
  See Also
761
670
  --------
762
- :class:`acoular.signals.SignalGenerator` : For defining custom emitted signals.
763
- :class:`acoular.microphones.MicGeom` : For specifying microphone geometries.
764
- :class:`acoular.environments.Environment` : For modeling sound propagation effects.
671
+ :class:`~acoular.signals.SignalGenerator` : For defining custom emitted signals.
672
+ :class:`~acoular.microphones.MicGeom` : For specifying microphone geometries.
673
+ :class:`~acoular.environments.Environment` : For modeling sound propagation effects.
765
674
 
766
675
  Notes
767
676
  -----
@@ -826,8 +735,8 @@ class PointSource(SamplesGenerator):
826
735
  mics = Instance(MicGeom, desc='microphone geometry')
827
736
 
828
737
  def _validate_locations(self):
829
- dist = self.env._r(array(self.loc).reshape((3, 1)), self.mics.pos)
830
- if npany(dist < 1e-7):
738
+ dist = self.env._r(np.array(self.loc).reshape((3, 1)), self.mics.pos)
739
+ if np.any(dist < 1e-7):
831
740
  warn('Source and microphone locations are identical.', Warning, stacklevel=2)
832
741
 
833
742
  #: An :class:`~acoular.environments.Environment` or derived object providing sound propagation
@@ -905,31 +814,32 @@ class PointSource(SamplesGenerator):
905
814
  If signal processing or propagation cannot be performed.
906
815
  """
907
816
  self._validate_locations()
908
- N = int(ceil(self.num_samples / num)) # number of output blocks
817
+ num_samples_estimate = self.num_samples + (self.start_t - self.start) * self.sample_freq
818
+ N = int(np.ceil(num_samples_estimate / num)) # number of output blocks
909
819
  signal = self.signal.usignal(self.up)
910
- out = empty((num, self.num_channels))
820
+ out = np.empty((num, self.num_channels))
911
821
  # distances
912
- rm = self.env._r(array(self.loc).reshape((3, 1)), self.mics.pos).reshape(1, -1)
822
+ rm = self.env._r(np.array(self.loc).reshape((3, 1)), self.mics.pos).reshape(1, -1)
913
823
  # emission time relative to start_t (in samples) for first sample
914
824
  ind = (-rm / self.env.c - self.start_t + self.start) * self.sample_freq * self.up
915
825
 
916
826
  if self.prepadding == 'zeros':
917
827
  # number of blocks where signal behaviour is amended
918
- pre = -int(npmin(ind[0]) // (self.up * num))
828
+ pre = -int(np.min(ind[0]) // (self.up * num))
919
829
  # amend signal for first blocks
920
830
  # if signal stops during prepadding, terminate
921
831
  if pre >= N:
922
832
  for _nb in range(N - 1):
923
- out = _fill_mic_signal_block(out, signal, rm, ind, num, self.num_channels, self.up, True)
833
+ _fill_mic_signal_block(out, signal, rm, ind, num, self.num_channels, self.up, True)
924
834
  yield out
925
835
 
926
836
  blocksize = self.num_samples % num or num
927
- out = _fill_mic_signal_block(out, signal, rm, ind, blocksize, self.num_channels, self.up, True)
837
+ _fill_mic_signal_block(out, signal, rm, ind, blocksize, self.num_channels, self.up, True)
928
838
  yield out[:blocksize]
929
839
  return
930
840
  else:
931
841
  for _nb in range(pre):
932
- out = _fill_mic_signal_block(out, signal, rm, ind, num, self.num_channels, self.up, True)
842
+ _fill_mic_signal_block(out, signal, rm, ind, num, self.num_channels, self.up, True)
933
843
  yield out
934
844
 
935
845
  else:
@@ -937,12 +847,12 @@ class PointSource(SamplesGenerator):
937
847
 
938
848
  # main generator
939
849
  for _nb in range(N - pre - 1):
940
- out = _fill_mic_signal_block(out, signal, rm, ind, num, self.num_channels, self.up, False)
850
+ _fill_mic_signal_block(out, signal, rm, ind, num, self.num_channels, self.up, False)
941
851
  yield out
942
852
 
943
853
  # last block of variable size
944
854
  blocksize = self.num_samples % num or num
945
- out = _fill_mic_signal_block(out, signal, rm, ind, blocksize, self.num_channels, self.up, False)
855
+ _fill_mic_signal_block(out, signal, rm, ind, blocksize, self.num_channels, self.up, False)
946
856
  yield out[:blocksize]
947
857
 
948
858
 
@@ -1026,9 +936,9 @@ class SphericalHarmonicSource(PointSource):
1026
936
  lOrder=self.lOrder,
1027
937
  direction=self.direction,
1028
938
  mpos=self.mics.pos,
1029
- sourceposition=array(self.loc),
939
+ sourceposition=np.array(self.loc),
1030
940
  )
1031
- return real(ifft(fft(signals, axis=0) * (Y_lm @ self.alpha), axis=0))
941
+ return np.real(ifft(fft(signals, axis=0) * (Y_lm @ self.alpha), axis=0))
1032
942
 
1033
943
  def result(self, num=128):
1034
944
  """
@@ -1060,15 +970,15 @@ class SphericalHarmonicSource(PointSource):
1060
970
 
1061
971
  signal = self.signal.usignal(self.up)
1062
972
  # emission time relative to start_t (in samples) for first sample
1063
- rm = self.env._r(array(self.loc).reshape((3, 1)), self.mics.pos)
1064
- ind = (-rm / self.env.c - self.start_t + self.start) * self.sample_freq + pi / 30
973
+ rm = self.env._r(np.array(self.loc).reshape((3, 1)), self.mics.pos)
974
+ ind = (-rm / self.env.c - self.start_t + self.start) * self.sample_freq + np.pi / 30
1065
975
  i = 0
1066
976
  n = self.num_samples
1067
- out = empty((num, self.num_channels))
977
+ out = np.empty((num, self.num_channels))
1068
978
  while n:
1069
979
  n -= 1
1070
980
  try:
1071
- out[i] = signal[array(0.5 + ind * self.up, dtype=int64)] / rm
981
+ out[i] = signal[np.array(0.5 + ind * self.up, dtype=np.int64)] / rm
1072
982
  ind += 1
1073
983
  i += 1
1074
984
  if i == num:
@@ -1091,8 +1001,8 @@ class MovingPointSource(PointSource):
1091
1001
 
1092
1002
  See Also
1093
1003
  --------
1094
- :class:`acoular.sources.PointSource` : For modeling stationary point sources.
1095
- :class:`acoular.trajectory.Trajectory` : For specifying source motion paths.
1004
+ :class:`~acoular.sources.PointSource` : For modeling stationary point sources.
1005
+ :class:`~acoular.trajectory.Trajectory` : For specifying source motion paths.
1096
1006
  """
1097
1007
 
1098
1008
  #: Determines whether convective amplification is considered. When ``True``, the amplitude of
@@ -1127,6 +1037,51 @@ class MovingPointSource(PointSource):
1127
1037
  def _get_digest(self):
1128
1038
  return digest(self)
1129
1039
 
1040
+ def get_moving_direction(self, direction, time=0):
1041
+ """
1042
+ Calculate the moving direction of the source along its trajectory.
1043
+
1044
+ This method computes the updated direction vector for the moving source, considering both
1045
+ translation along the :attr:`~MovingPointSource.trajectory` and rotation defined by the
1046
+ :attr:`reference vector<rvec>`. If the :attr:`reference vector<rvec>` is `(0, 0, 0)`, only
1047
+ translation is applied. Otherwise, the method incorporates rotation into the calculation.
1048
+
1049
+ Parameters
1050
+ ----------
1051
+ direction : :class:`numpy.ndarray`
1052
+ The initial orientation of the source, specified as a three-dimensional array.
1053
+ time : :class:`float`, optional
1054
+ The time at which the :attr:`~MovingPointSource.trajectory` position and velocity are
1055
+ evaluated. Defaults to ``0``.
1056
+
1057
+ Returns
1058
+ -------
1059
+ :class:`numpy.ndarray`
1060
+ The updated direction vector of the moving source after translation and, if applicable,
1061
+ rotation. The output is a three-dimensional array.
1062
+
1063
+ Notes
1064
+ -----
1065
+ - The method computes the translation direction vector based on the
1066
+ :attr:`~MovingPointSource.trajectory`'s velocity at the specified time.
1067
+ - If the :attr:`reference vector<rvec>` is non-zero, the method constructs a rotation matrix
1068
+ to compute the new source direction based on the
1069
+ :attr:`~MovingPointSource.trajectory`'s motion and the :attr:`reference vector<rvec>`.
1070
+ - The rotation matrix ensures that the new orientation adheres to the right-hand rule and
1071
+ remains orthogonal.
1072
+ """
1073
+ trajg1 = np.array(self.trajectory.location(time, der=1))[:, 0][:, np.newaxis]
1074
+ rflag = (self.rvec == 0).all() # flag translation vs. rotation
1075
+ if rflag:
1076
+ return direction
1077
+ dx = np.array(trajg1.T) # direction vector (new x-axis)
1078
+ dy = np.cross(self.rvec, dx) # new y-axis
1079
+ dz = np.cross(dx, dy) # new z-axis
1080
+ RM = np.array((dx, dy, dz)).T # rotation matrix
1081
+ RM /= np.sqrt((RM * RM).sum(0)) # column normalized
1082
+ newdir = np.dot(RM, direction)
1083
+ return np.cross(newdir[:, 0].T, self.rvec.T).T
1084
+
1130
1085
  def result(self, num=128):
1131
1086
  """
1132
1087
  Generate the output signal at microphones in blocks, accounting for source motion.
@@ -1167,23 +1122,23 @@ class MovingPointSource(PointSource):
1167
1122
  signal = self.signal.usignal(self.up)
1168
1123
  # shortcuts and initial values
1169
1124
  num_mics = self.num_channels
1170
- mpos = self.mics.pos[:, :, newaxis]
1171
- t = self.start + ones(num_mics)[:, newaxis] * arange(num) / self.sample_freq
1125
+ mpos = self.mics.pos[:, :, np.newaxis]
1126
+ t = self.start + np.ones(num_mics)[:, np.newaxis] * np.arange(num) / self.sample_freq
1172
1127
  epslim = 0.1 / self.up / self.sample_freq
1173
1128
  c0 = self.env.c
1174
1129
  tr = self.trajectory
1175
1130
  n = self.num_samples
1176
1131
  while n > 0:
1177
- eps = ones_like(t) # init discrepancy in time
1132
+ eps = np.ones_like(t) # init discrepancy in time
1178
1133
  te = t.copy() # init emission time = receiving time
1179
1134
  j = 0
1180
1135
  # Newton-Rhapson iteration
1181
1136
  while abs(eps).max() > epslim and j < 100:
1182
- loc = array(tr.location(te.flatten())).reshape((3, num_mics, -1))
1137
+ loc = np.array(tr.location(te.flatten())).reshape((3, num_mics, -1))
1183
1138
  rm = loc - mpos # distance vectors to microphones
1184
- rm = sqrt((rm * rm).sum(0)) # absolute distance
1185
- loc /= sqrt((loc * loc).sum(0)) # distance unit vector
1186
- der = array(tr.location(te.flatten(), der=1)).reshape((3, num_mics, -1))
1139
+ rm = np.sqrt((rm * rm).sum(0)) # absolute distance
1140
+ loc /= np.sqrt((loc * loc).sum(0)) # distance unit vector
1141
+ der = np.array(tr.location(te.flatten(), der=1)).reshape((3, num_mics, -1))
1187
1142
  Mr = (der * loc).sum(0) / c0 # radial Mach number
1188
1143
  eps[:] = (te + rm / c0 - t) / (1 + Mr) # discrepancy in time
1189
1144
  te -= eps
@@ -1194,7 +1149,7 @@ class MovingPointSource(PointSource):
1194
1149
  if self.conv_amp:
1195
1150
  rm *= (1 - Mr) ** 2
1196
1151
  try:
1197
- ind = array(0.5 + ind * self.up, dtype=int64)
1152
+ ind = np.array(0.5 + ind * self.up, dtype=np.int64)
1198
1153
  out = (signal[ind] / rm).T
1199
1154
  yield out[:n]
1200
1155
  except IndexError: # last incomplete frame
@@ -1221,7 +1176,7 @@ class PointSourceDipole(PointSource):
1221
1176
 
1222
1177
  See Also
1223
1178
  --------
1224
- :class:`acoular.sources.PointSource` : For modeling stationary point sources.
1179
+ :class:`~acoular.sources.PointSource` : For modeling stationary point sources.
1225
1180
 
1226
1181
  Notes
1227
1182
  -----
@@ -1296,10 +1251,10 @@ class PointSourceDipole(PointSource):
1296
1251
 
1297
1252
  mpos = self.mics.pos
1298
1253
  # position of the dipole as (3,1) vector
1299
- loc = array(self.loc, dtype=float).reshape((3, 1))
1254
+ loc = np.array(self.loc, dtype=float).reshape((3, 1))
1300
1255
  # direction vector from tuple
1301
- direc = array(self.direction, dtype=float) * 1e-5
1302
- direc_mag = sqrt(dot(direc, direc))
1256
+ direc = np.array(self.direction, dtype=float) * 1e-5
1257
+ direc_mag = np.sqrt(np.dot(direc, direc))
1303
1258
 
1304
1259
  # normed direction vector
1305
1260
  direc_n = direc / direc_mag
@@ -1313,7 +1268,7 @@ class PointSourceDipole(PointSource):
1313
1268
  dir2 = (direc_n * dist / 2.0).reshape((3, 1))
1314
1269
 
1315
1270
  signal = self.signal.usignal(self.up)
1316
- out = empty((num, self.num_channels))
1271
+ out = np.empty((num, self.num_channels))
1317
1272
 
1318
1273
  # distance from dipole center to microphones
1319
1274
  rm = self.env._r(loc, mpos)
@@ -1336,8 +1291,8 @@ class PointSourceDipole(PointSource):
1336
1291
  rm
1337
1292
  / dist
1338
1293
  * (
1339
- signal[array(0.5 + ind1 * self.up, dtype=int64)] / rm1
1340
- - signal[array(0.5 + ind2 * self.up, dtype=int64)] / rm2
1294
+ signal[np.array(0.5 + ind1 * self.up, dtype=np.int64)] / rm1
1295
+ - signal[np.array(0.5 + ind2 * self.up, dtype=np.int64)] / rm2
1341
1296
  )
1342
1297
  )
1343
1298
  ind1 += 1.0
@@ -1350,7 +1305,8 @@ class PointSourceDipole(PointSource):
1350
1305
  except IndexError:
1351
1306
  break
1352
1307
 
1353
- yield out[:i]
1308
+ if i > 0:
1309
+ yield out[:i]
1354
1310
 
1355
1311
 
1356
1312
  class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
@@ -1370,8 +1326,8 @@ class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
1370
1326
 
1371
1327
  See Also
1372
1328
  --------
1373
- :class:`acoular.sources.PointSourceDipole` : For stationary dipole sources.
1374
- :class:`acoular.sources.MovingPointSource` :
1329
+ :class:`~acoular.sources.PointSourceDipole` : For stationary dipole sources.
1330
+ :class:`~acoular.sources.MovingPointSource` :
1375
1331
  For moving point sources without dipole characteristics.
1376
1332
  """
1377
1333
 
@@ -1392,7 +1348,7 @@ class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
1392
1348
  #: A reference vector, perpendicular to the x and y-axis of moving source, defining the axis of
1393
1349
  #: rotation for the dipole directivity. If set to ``(0, 0, 0)``, the dipole is only translated
1394
1350
  #: along the :attr:`~MovingPointSource.trajectory` without rotation. Default is ``(0, 0, 0)``.
1395
- rvec = CArray(dtype=float, shape=(3,), value=array((0, 0, 0)), desc='reference vector')
1351
+ rvec = CArray(dtype=float, shape=(3,), value=np.array((0, 0, 0)), desc='reference vector')
1396
1352
 
1397
1353
  @cached_property
1398
1354
  def _get_digest(self):
@@ -1434,70 +1390,25 @@ class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
1434
1390
  terminates when the time discrepancy (``eps``) is below a threshold (``epslim``)
1435
1391
  or after 100 iterations.
1436
1392
  """
1437
- eps = ones(self.mics.num_mics)
1393
+ eps = np.ones(self.mics.num_mics)
1438
1394
  epslim = 0.1 / self.up / self.sample_freq
1439
1395
  te = t.copy() # init emission time = receiving time
1440
1396
  j = 0
1441
1397
  # Newton-Rhapson iteration
1442
1398
  while abs(eps).max() > epslim and j < 100:
1443
- xs = array(self.trajectory.location(te))
1399
+ xs = np.array(self.trajectory.location(te))
1444
1400
  loc = xs.copy()
1445
1401
  loc += direction
1446
1402
  rm = loc - self.mics.pos # distance vectors to microphones
1447
- rm = sqrt((rm * rm).sum(0)) # absolute distance
1448
- loc /= sqrt((loc * loc).sum(0)) # distance unit vector
1449
- der = array(self.trajectory.location(te, der=1))
1403
+ rm = np.sqrt((rm * rm).sum(0)) # absolute distance
1404
+ loc /= np.sqrt((loc * loc).sum(0)) # distance unit vector
1405
+ der = np.array(self.trajectory.location(te, der=1))
1450
1406
  Mr = (der * loc).sum(0) / self.env.c # radial Mach number
1451
1407
  eps = (te + rm / self.env.c - t) / (1 + Mr) # discrepancy in time
1452
1408
  te -= eps
1453
1409
  j += 1 # iteration count
1454
1410
  return te, rm, Mr, xs
1455
1411
 
1456
- def get_moving_direction(self, direction, time=0):
1457
- """
1458
- Calculate the moving direction of the dipole source along its trajectory.
1459
-
1460
- This method computes the updated direction vector for the dipole source, considering both
1461
- translation along the trajectory and rotation defined by the :attr:`reference vector<rvec>`.
1462
- If the reference vector is ``(0, 0, 0)``, only translation is applied. Otherwise, the method
1463
- incorporates rotation into the calculation.
1464
-
1465
- Parameters
1466
- ----------
1467
- direction : :class:`numpy.ndarray`
1468
- The initial direction vector of the dipole, specified as a 3-element
1469
- array representing the orientation of the dipole lobes.
1470
- time : :class:`float`, optional
1471
- The time at which the trajectory position and velocity are evaluated. Defaults to ``0``.
1472
-
1473
- Returns
1474
- -------
1475
- :class:`numpy.ndarray`
1476
- The updated direction vector of the dipole source after translation
1477
- and, if applicable, rotation. The output is a 3-element array.
1478
-
1479
- Notes
1480
- -----
1481
- - The method computes the translation direction vector based on the trajectory's velocity at
1482
- the specified time.
1483
- - If the :attr:`reference vector<rvec>` is non-zero, the method constructs a rotation matrix
1484
- to compute the new dipole direction based on the trajectory's motion and the
1485
- reference vector.
1486
- - The rotation matrix ensures that the new dipole orientation adheres
1487
- to the right-hand rule and remains orthogonal.
1488
- """
1489
- trajg1 = array(self.trajectory.location(time, der=1))[:, 0][:, newaxis]
1490
- rflag = (self.rvec == 0).all() # flag translation vs. rotation
1491
- if rflag:
1492
- return direction
1493
- dx = array(trajg1.T) # direction vector (new x-axis)
1494
- dy = cross(self.rvec, dx) # new y-axis
1495
- dz = cross(dx, dy) # new z-axis
1496
- RM = array((dx, dy, dz)).T # rotation matrix
1497
- RM /= sqrt((RM * RM).sum(0)) # column normalized
1498
- newdir = dot(RM, direction)
1499
- return cross(newdir[:, 0].T, self.rvec.T).T
1500
-
1501
1412
  def result(self, num=128):
1502
1413
  """
1503
1414
  Generate the output signal at microphones in blocks.
@@ -1524,8 +1435,8 @@ class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
1524
1435
  mpos = self.mics.pos
1525
1436
 
1526
1437
  # direction vector from tuple
1527
- direc = array(self.direction, dtype=float) * 1e-5
1528
- direc_mag = sqrt(dot(direc, direc))
1438
+ direc = np.array(self.direction, dtype=float) * 1e-5
1439
+ direc_mag = np.sqrt(np.dot(direc, direc))
1529
1440
  # normed direction vector
1530
1441
  direc_n = direc / direc_mag
1531
1442
  c = self.env.c
@@ -1536,10 +1447,10 @@ class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
1536
1447
  dir2 = (direc_n * dist / 2.0).reshape((3, 1))
1537
1448
 
1538
1449
  signal = self.signal.usignal(self.up)
1539
- out = empty((num, self.num_channels))
1450
+ out = np.empty((num, self.num_channels))
1540
1451
  # shortcuts and initial values
1541
1452
  m = self.mics
1542
- t = self.start * ones(m.num_mics)
1453
+ t = self.start * np.ones(m.num_mics)
1543
1454
 
1544
1455
  i = 0
1545
1456
  n = self.num_samples
@@ -1548,7 +1459,7 @@ class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
1548
1459
  te, rm, Mr, locs = self.get_emission_time(t, 0)
1549
1460
  t += 1.0 / self.sample_freq
1550
1461
  # location of the center
1551
- loc = array(self.trajectory.location(te), dtype=float)[:, 0][:, newaxis]
1462
+ loc = np.array(self.trajectory.location(te), dtype=float)[:, 0][:, np.newaxis]
1552
1463
  # distance of the dipoles from the center
1553
1464
  diff = self.get_moving_direction(dir2, te)
1554
1465
 
@@ -1567,8 +1478,8 @@ class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
1567
1478
  rm
1568
1479
  / dist
1569
1480
  * (
1570
- signal[array(0.5 + ind * self.up, dtype=int64)] / rm1
1571
- - signal[array(0.5 + ind * self.up, dtype=int64)] / rm2
1481
+ signal[np.array(0.5 + ind * self.up, dtype=np.int64)] / rm1
1482
+ - signal[np.array(0.5 + ind * self.up, dtype=np.int64)] / rm2
1572
1483
  )
1573
1484
  )
1574
1485
  i += 1
@@ -1577,7 +1488,9 @@ class MovingPointSourceDipole(PointSourceDipole, MovingPointSource):
1577
1488
  i = 0
1578
1489
  except IndexError:
1579
1490
  break
1580
- yield out[:i]
1491
+
1492
+ if i > 0:
1493
+ yield out[:i]
1581
1494
 
1582
1495
 
1583
1496
  class LineSource(PointSource):
@@ -1598,7 +1511,7 @@ class LineSource(PointSource):
1598
1511
 
1599
1512
  See Also
1600
1513
  --------
1601
- :class:`acoular.sources.PointSource` : For modeling stationary point sources.
1514
+ :class:`~acoular.sources.PointSource` : For modeling stationary point sources.
1602
1515
 
1603
1516
  Notes
1604
1517
  -----
@@ -1662,24 +1575,24 @@ class LineSource(PointSource):
1662
1575
  mpos = self.mics.pos
1663
1576
 
1664
1577
  # direction vector from tuple
1665
- direc = array(self.direction, dtype=float)
1578
+ direc = np.array(self.direction, dtype=float)
1666
1579
  # normed direction vector
1667
- direc_n = direc / norm(direc)
1580
+ direc_n = direc / spla.norm(direc)
1668
1581
  c = self.env.c
1669
1582
 
1670
1583
  # distance between monopoles in the line
1671
1584
  dist = self.length / self.num_sources
1672
1585
 
1673
1586
  # blocwise output
1674
- out = zeros((num, self.num_channels))
1587
+ out = np.zeros((num, self.num_channels))
1675
1588
 
1676
1589
  # distance from line start position to microphones
1677
- loc = array(self.loc, dtype=float).reshape((3, 1))
1590
+ loc = np.array(self.loc, dtype=float).reshape((3, 1))
1678
1591
 
1679
1592
  # distances from monopoles in the line to microphones
1680
- rms = empty((self.num_channels, self.num_sources))
1681
- inds = empty((self.num_channels, self.num_sources))
1682
- signals = empty((self.num_sources, len(self.signal.usignal(self.up))))
1593
+ rms = np.empty((self.num_channels, self.num_sources))
1594
+ inds = np.empty((self.num_channels, self.num_sources))
1595
+ signals = np.empty((self.num_sources, len(self.signal.usignal(self.up))))
1683
1596
  # for every source - distances
1684
1597
  for s in range(self.num_sources):
1685
1598
  rms[:, s] = self.env._r((loc.T + direc_n * dist * s).T, mpos)
@@ -1696,18 +1609,19 @@ class LineSource(PointSource):
1696
1609
  try:
1697
1610
  for s in range(self.num_sources):
1698
1611
  # sum sources
1699
- out[i] += signals[s, array(0.5 + inds[:, s].T * self.up, dtype=int64)] / rms[:, s]
1612
+ out[i] += signals[s, np.array(0.5 + inds[:, s].T * self.up, dtype=np.int64)] / rms[:, s]
1700
1613
 
1701
1614
  inds += 1.0
1702
1615
  i += 1
1703
1616
  if i == num:
1704
1617
  yield out
1705
- out = zeros((num, self.num_channels))
1618
+ out = np.zeros((num, self.num_channels))
1706
1619
  i = 0
1707
1620
  except IndexError:
1708
1621
  break
1709
1622
 
1710
- yield out[:i]
1623
+ if i > 0:
1624
+ yield out[:i]
1711
1625
 
1712
1626
 
1713
1627
  class MovingLineSource(LineSource, MovingPointSource):
@@ -1727,10 +1641,10 @@ class MovingLineSource(LineSource, MovingPointSource):
1727
1641
 
1728
1642
  See Also
1729
1643
  --------
1730
- :class:`acoular.sources.LineSource` :
1644
+ :class:`~acoular.sources.LineSource` :
1731
1645
  For :class:`line sources<LineSource>` consisting of
1732
1646
  :attr:`coherent or incoherent<LineSource.coherence>` monopoles.
1733
- :class:`acoular.sources.MovingPointSource` :
1647
+ :class:`~acoular.sources.MovingPointSource` :
1734
1648
  For moving point sources without dipole characteristics.
1735
1649
  """
1736
1650
 
@@ -1752,59 +1666,12 @@ class MovingLineSource(LineSource, MovingPointSource):
1752
1666
  #: rotation for the line source directivity. If set to ``(0, 0, 0)``, the line source is only
1753
1667
  #: translated along the :attr:`~MovingPointSource.trajectory` without rotation. Default is
1754
1668
  #: ``(0, 0, 0)``.
1755
- rvec = CArray(dtype=float, shape=(3,), value=array((0, 0, 0)), desc='reference vector')
1669
+ rvec = CArray(dtype=float, shape=(3,), value=np.array((0, 0, 0)), desc='reference vector')
1756
1670
 
1757
1671
  @cached_property
1758
1672
  def _get_digest(self):
1759
1673
  return digest(self)
1760
1674
 
1761
- def get_moving_direction(self, direction, time=0):
1762
- """
1763
- Calculate the moving direction of the line source along its trajectory.
1764
-
1765
- This method computes the updated direction vector for the line source,
1766
- considering both translation along the :attr:`~MovingPointSource.trajectory` and rotation
1767
- defined by the :attr:`reference vector<rvec>`. If the :attr:`reference vector<rvec>` is
1768
- `(0, 0, 0)`, only translation is applied. Otherwise, the method incorporates rotation
1769
- into the calculation.
1770
-
1771
- Parameters
1772
- ----------
1773
- direction : :class:`numpy.ndarray`
1774
- The initial direction vector of the line source, specified as a
1775
- 3-element array representing the orientation of the line.
1776
- time : :class:`float`, optional
1777
- The time at which the :attr:`~MovingPointSource.trajectory` position and velocity
1778
- are evaluated. Defaults to ``0``.
1779
-
1780
- Returns
1781
- -------
1782
- :class:`numpy.ndarray`
1783
- The updated direction vector of the line source after translation and,
1784
- if applicable, rotation. The output is a 3-element array.
1785
-
1786
- Notes
1787
- -----
1788
- - The method computes the translation direction vector based on the
1789
- :attr:`~MovingPointSource.trajectory`'s velocity at the specified time.
1790
- - If the :attr:`reference vector<rvec>` is non-zero, the method constructs a
1791
- rotation matrix to compute the new line source direction based on the
1792
- :attr:`~MovingPointSource.trajectory`'s motion and the :attr:`reference vector<rvec>`.
1793
- - The rotation matrix ensures that the new orientation adheres to the
1794
- right-hand rule and remains orthogonal.
1795
- """
1796
- trajg1 = array(self.trajectory.location(time, der=1))[:, 0][:, newaxis]
1797
- rflag = (self.rvec == 0).all() # flag translation vs. rotation
1798
- if rflag:
1799
- return direction
1800
- dx = array(trajg1.T) # direction vector (new x-axis)
1801
- dy = cross(self.rvec, dx) # new y-axis
1802
- dz = cross(dx, dy) # new z-axis
1803
- RM = array((dx, dy, dz)).T # rotation matrix
1804
- RM /= sqrt((RM * RM).sum(0)) # column normalized
1805
- newdir = dot(RM, direction)
1806
- return cross(newdir[:, 0].T, self.rvec.T).T
1807
-
1808
1675
  def get_emission_time(self, t, direction):
1809
1676
  """
1810
1677
  Calculate the emission time for a moving line source based on its trajectory.
@@ -1847,19 +1714,19 @@ class MovingLineSource(LineSource, MovingPointSource):
1847
1714
  - The method iterates until the difference between the computed emission time and
1848
1715
  the current time is sufficiently small (within a defined threshold).
1849
1716
  """
1850
- eps = ones(self.mics.num_mics)
1717
+ eps = np.ones(self.mics.num_mics)
1851
1718
  epslim = 0.1 / self.up / self.sample_freq
1852
1719
  te = t.copy() # init emission time = receiving time
1853
1720
  j = 0
1854
1721
  # Newton-Rhapson iteration
1855
1722
  while abs(eps).max() > epslim and j < 100:
1856
- xs = array(self.trajectory.location(te))
1723
+ xs = np.array(self.trajectory.location(te))
1857
1724
  loc = xs.copy()
1858
1725
  loc += direction
1859
1726
  rm = loc - self.mics.pos # distance vectors to microphones
1860
- rm = sqrt((rm * rm).sum(0)) # absolute distance
1861
- loc /= sqrt((loc * loc).sum(0)) # distance unit vector
1862
- der = array(self.trajectory.location(te, der=1))
1727
+ rm = np.sqrt((rm * rm).sum(0)) # absolute distance
1728
+ loc /= np.sqrt((loc * loc).sum(0)) # distance unit vector
1729
+ der = np.array(self.trajectory.location(te, der=1))
1863
1730
  Mr = (der * loc).sum(0) / self.env.c # radial Mach number
1864
1731
  eps = (te + rm / self.env.c - t) / (1 + Mr) # discrepancy in time
1865
1732
  te -= eps
@@ -1887,21 +1754,21 @@ class MovingLineSource(LineSource, MovingPointSource):
1887
1754
  mpos = self.mics.pos
1888
1755
 
1889
1756
  # direction vector from tuple
1890
- direc = array(self.direction, dtype=float)
1757
+ direc = np.array(self.direction, dtype=float)
1891
1758
  # normed direction vector
1892
- direc_n = direc / norm(direc)
1759
+ direc_n = direc / spla.norm(direc)
1893
1760
 
1894
1761
  # distance between monopoles in the line
1895
1762
  dist = self.length / self.num_sources
1896
1763
  dir2 = (direc_n * dist).reshape((3, 1))
1897
1764
 
1898
1765
  # blocwise output
1899
- out = zeros((num, self.num_channels))
1766
+ out = np.zeros((num, self.num_channels))
1900
1767
 
1901
1768
  # distances from monopoles in the line to microphones
1902
- rms = empty((self.num_channels, self.num_sources))
1903
- inds = empty((self.num_channels, self.num_sources))
1904
- signals = empty((self.num_sources, len(self.signal.usignal(self.up))))
1769
+ rms = np.empty((self.num_channels, self.num_sources))
1770
+ inds = np.empty((self.num_channels, self.num_sources))
1771
+ signals = np.empty((self.num_sources, len(self.signal.usignal(self.up))))
1905
1772
  # coherence
1906
1773
  for s in range(self.num_sources):
1907
1774
  # new seed for every source
@@ -1913,20 +1780,20 @@ class MovingLineSource(LineSource, MovingPointSource):
1913
1780
 
1914
1781
  # shortcuts and initial values
1915
1782
  m = self.mics
1916
- t = self.start * ones(m.num_mics)
1783
+ t = self.start * np.ones(m.num_mics)
1917
1784
  i = 0
1918
1785
  n = self.num_samples
1919
1786
  while n:
1920
1787
  n -= 1
1921
1788
  t += 1.0 / self.sample_freq
1922
1789
  te1, rm1, Mr1, locs1 = self.get_emission_time(t, 0)
1923
- # trajg1 = array(self.trajectory.location( te1, der=1))[:,0][:,newaxis]
1790
+ # trajg1 = np.array(self.trajectory.location( te1, der=1))[:,0][:,np.newaxis]
1924
1791
 
1925
1792
  # get distance and ind for every source in the line
1926
1793
  for s in range(self.num_sources):
1927
1794
  diff = self.get_moving_direction(dir2, te1)
1928
- te, rm, Mr, locs = self.get_emission_time(t, tile((diff * s).T, (self.num_channels, 1)).T)
1929
- loc = array(self.trajectory.location(te), dtype=float)[:, 0][:, newaxis]
1795
+ te, rm, Mr, locs = self.get_emission_time(t, np.tile((diff * s).T, (self.num_channels, 1)).T)
1796
+ loc = np.array(self.trajectory.location(te), dtype=float)[:, 0][:, np.newaxis]
1930
1797
  diff = self.get_moving_direction(dir2, te)
1931
1798
  rms[:, s] = self.env._r((loc + diff * s), mpos)
1932
1799
  inds[:, s] = (te - self.start_t + self.start) * self.sample_freq
@@ -1938,19 +1805,20 @@ class MovingLineSource(LineSource, MovingPointSource):
1938
1805
  # subtract the second signal b/c of phase inversion
1939
1806
  for s in range(self.num_sources):
1940
1807
  # sum sources
1941
- out[i] += signals[s, array(0.5 + inds[:, s].T * self.up, dtype=int64)] / rms[:, s]
1808
+ out[i] += signals[s, np.array(0.5 + inds[:, s].T * self.up, dtype=np.int64)] / rms[:, s]
1942
1809
 
1943
1810
  i += 1
1944
1811
  if i == num:
1945
1812
  yield out
1946
- out = zeros((num, self.num_channels))
1813
+ out = np.zeros((num, self.num_channels))
1947
1814
  i = 0
1948
1815
  except IndexError:
1949
1816
  break
1950
- yield out[:i]
1817
+
1818
+ if i > 0:
1819
+ yield out[:i]
1951
1820
 
1952
1821
 
1953
- @deprecated_alias({'numchannels': 'num_channels'}, read_only=True, removal_version='25.10')
1954
1822
  class UncorrelatedNoiseSource(SamplesGenerator):
1955
1823
  """
1956
1824
  Simulate uncorrelated white or pink noise signals at multiple channels.
@@ -1962,8 +1830,8 @@ class UncorrelatedNoiseSource(SamplesGenerator):
1962
1830
 
1963
1831
  See Also
1964
1832
  --------
1965
- :class:`acoular.signals.SignalGenerator` : For defining noise types and properties.
1966
- :class:`acoular.microphones.MicGeom` : For specifying microphone geometries.
1833
+ :class:`~acoular.signals.SignalGenerator` : For defining noise types and properties.
1834
+ :class:`~acoular.microphones.MicGeom` : For specifying microphone geometries.
1967
1835
 
1968
1836
  Notes
1969
1837
  -----
@@ -2011,7 +1879,7 @@ class UncorrelatedNoiseSource(SamplesGenerator):
2011
1879
  #: Array of random seed values for generating uncorrelated noise at each channel. If left empty,
2012
1880
  #: seeds will be automatically generated as ``np.arange(self.num_channels) + signal.seed``. The
2013
1881
  #: size of the array must match the :attr:`number of output channels<num_channels>`.
2014
- seed = CArray(dtype=uint32, desc='random seed values')
1882
+ seed = CArray(dtype=np.uint32, desc='random seed values')
2015
1883
 
2016
1884
  #: Number of output channels, automatically determined by the number of microphones
2017
1885
  #: defined in the :attr:`mics` attribute. Corresponds to the number of uncorrelated noise
@@ -2089,14 +1957,14 @@ class UncorrelatedNoiseSource(SamplesGenerator):
2089
1957
  Noise = self.signal.__class__
2090
1958
  # create or get the array of random seeds
2091
1959
  if not self.seed.size > 0:
2092
- seed = arange(self.num_channels) + self.signal.seed
1960
+ seed = np.arange(self.num_channels) + self.signal.seed
2093
1961
  elif self.seed.shape == (self.num_channels,):
2094
1962
  seed = self.seed
2095
1963
  else:
2096
1964
  msg = f'Seed array expected to be of shape ({self.num_channels:d},), but has shape {self.seed.shape}.'
2097
1965
  raise ValueError(msg)
2098
1966
  # create array with [num_channels] noise signal tracks
2099
- signal = array(
1967
+ signal = np.array(
2100
1968
  [
2101
1969
  Noise(seed=s, num_samples=self.num_samples, sample_freq=self.sample_freq, rms=self.signal.rms).signal()
2102
1970
  for s in seed
@@ -2114,7 +1982,6 @@ class UncorrelatedNoiseSource(SamplesGenerator):
2114
1982
  return
2115
1983
 
2116
1984
 
2117
- @deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples'}, read_only=True, removal_version='25.10')
2118
1985
  class SourceMixer(SamplesGenerator):
2119
1986
  """
2120
1987
  Combine signals from multiple sources by mixing their outputs.
@@ -2126,7 +1993,7 @@ class SourceMixer(SamplesGenerator):
2126
1993
 
2127
1994
  See Also
2128
1995
  --------
2129
- :class:`acoular.base.SamplesGenerator` : Base class for signal generators.
1996
+ :class:`~acoular.base.SamplesGenerator` : Base class for signal generators.
2130
1997
 
2131
1998
  Notes
2132
1999
  -----
@@ -2316,7 +2183,7 @@ class SourceMixer(SamplesGenerator):
2316
2183
  gens = [i.result(num) for i in self.sources[1:]]
2317
2184
  weights = self.weights.copy()
2318
2185
  if weights.size == 0:
2319
- weights = array([1.0 for j in range(len(self.sources))])
2186
+ weights = np.array([1.0 for j in range(len(self.sources))])
2320
2187
  assert weights.shape[0] == len(self.sources)
2321
2188
  for temp in self.sources[0].result(num):
2322
2189
  temp *= weights[0]
@@ -2345,7 +2212,7 @@ class PointSourceConvolve(PointSource):
2345
2212
  See Also
2346
2213
  --------
2347
2214
  :class:`PointSource` : Base class for point sources.
2348
- :class:`acoular.tprocess.TimeConvolve` : Class used for performing time-domain convolution.
2215
+ :class:`~acoular.tprocess.TimeConvolve` : Class used for performing time-domain convolution.
2349
2216
 
2350
2217
  Notes
2351
2218
  -----
@@ -2444,7 +2311,7 @@ class PointSourceConvolve(PointSource):
2444
2311
  - Convolution is performed using the :class:`~acoular.tprocess.TimeConvolve` class
2445
2312
  to ensure efficiency.
2446
2313
  """
2447
- data = repeat(self.signal.signal()[:, newaxis], self.mics.num_mics, axis=1)
2314
+ data = np.repeat(self.signal.signal()[:, np.newaxis], self.mics.num_mics, axis=1)
2448
2315
  source = TimeSamples(
2449
2316
  data=data,
2450
2317
  sample_freq=self.sample_freq,