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