SignalProcessingTools 1.2.2__py3-none-any.whl → 1.2.4__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.
@@ -1,3 +1,3 @@
1
1
  __title__ = "SignalProcessingTools"
2
- __version__ = "1.2.2"
2
+ __version__ = "1.2.4"
3
3
  __author__ = "Bruno Zuada Coelho, Aron Noordam"
@@ -8,7 +8,11 @@ class SpaceSignalProcessing:
8
8
  """
9
9
  SignalProcessing class for processing signals in space.
10
10
  """
11
- def __init__(self, x: npt.NDArray[np.float64], values: npt.NDArray[np.float64], Fs: Optional[float] = None):
11
+
12
+ def __init__(self,
13
+ x: npt.NDArray[np.float64],
14
+ values: npt.NDArray[np.float64],
15
+ Fs: Optional[float] = None):
12
16
  """
13
17
  Initializes the ProcessSignal object.
14
18
 
@@ -40,7 +44,6 @@ class SpaceSignalProcessing:
40
44
  self.max_fast = None
41
45
  self.max_fast_Dx = None
42
46
 
43
-
44
47
  def compute_track_longitudinal_levels(self):
45
48
  """
46
49
  Computes the track longitudinal levels, following EN 13848-1:2006.
@@ -53,20 +56,34 @@ class SpaceSignalProcessing:
53
56
  - D3: 70m < lambda <= 150m (1/150 Hz < f <= 1/70 Hz)
54
57
  """
55
58
 
56
- sig = TimeSignalProcessing(self.coordinates, self.signal_raw, Fs=self.fs)
57
- sig.filter([1/5., 1.], 4, type_filter="bandpass", filter_design=FilterDesign.BUTTERWORTH)
59
+ sig = TimeSignalProcessing(self.coordinates,
60
+ self.signal_raw,
61
+ Fs=self.fs)
62
+ sig.filter([1 / 5., 1.],
63
+ 4,
64
+ type_filter="bandpass",
65
+ filter_design=FilterDesign.BUTTERWORTH)
58
66
  self.d0 = sig.signal
59
67
 
60
68
  sig.reset()
61
- sig.filter([1/25., 1/3.], 4, type_filter="bandpass", filter_design=FilterDesign.BUTTERWORTH)
69
+ sig.filter([1 / 25., 1 / 3.],
70
+ 4,
71
+ type_filter="bandpass",
72
+ filter_design=FilterDesign.BUTTERWORTH)
62
73
  self.d1 = sig.signal
63
74
  sig.reset()
64
75
 
65
- sig.filter([1/70., 1/25.], 4, type_filter="bandpass", filter_design=FilterDesign.BUTTERWORTH)
76
+ sig.filter([1 / 70., 1 / 25.],
77
+ 4,
78
+ type_filter="bandpass",
79
+ filter_design=FilterDesign.BUTTERWORTH)
66
80
  self.d2 = sig.signal
67
81
  sig.reset()
68
82
 
69
- sig.filter([1/150., 1/70.], 4, type_filter="bandpass", filter_design=FilterDesign.BUTTERWORTH)
83
+ sig.filter([1 / 150., 1 / 70.],
84
+ 4,
85
+ type_filter="bandpass",
86
+ filter_design=FilterDesign.BUTTERWORTH)
70
87
  self.d3 = sig.signal
71
88
  sig.reset()
72
89
 
@@ -82,22 +99,23 @@ class SpaceSignalProcessing:
82
99
  """
83
100
 
84
101
  # octave bands used for the processing
85
- one_third_octave_bands = [[.08, .10],
86
- [.10, .126],
87
- [.126, .16],
88
- [.16, .20],
89
- [.20, .253],
90
- [.253, .32],
91
- [.32, .40],
92
- [.40, .50],
93
- [.50, .63],
94
- ]
95
-
102
+ one_third_octave_bands = [
103
+ [.08, .10],
104
+ [.10, .126],
105
+ [.126, .16],
106
+ [.16, .20],
107
+ [.20, .253],
108
+ [.253, .32],
109
+ [.32, .40],
110
+ [.40, .50],
111
+ [.50, .63],
112
+ ]
96
113
 
97
114
  # setting for the processing
98
115
  self.DXmaxFast = 1
99
116
  nb_fft_min = 256 # minimum number of samples for the power spectral density
100
- derivative = [0, 0, 0, 2, 2, 2, 2, 2, 2] # number of times that each frequency band is derived
117
+ derivative = [0, 0, 0, 2, 2, 2, 2, 2,
118
+ 2] # number of times that each frequency band is derived
101
119
 
102
120
  # RMS of the square root of the power spectral density
103
121
  self.rms_bands = np.zeros(len(one_third_octave_bands))
@@ -111,26 +129,34 @@ class SpaceSignalProcessing:
111
129
  self.signal = self.signal_raw * 1000
112
130
 
113
131
  # compute the power spectral density
114
- n_fft = int(np.max([2 ** (np.ceil(np.log2(len(self.signal)))), nb_fft_min]))
132
+ n_fft = int(
133
+ np.max([2**(np.ceil(np.log2(len(self.signal)))), nb_fft_min]))
115
134
  # if signal is odd length, add a zero to make it even
116
135
  if len(self.signal) % 2 != 0:
117
136
  signal = np.append(self.signal, 0)
118
- coordinates = np.append(self.coordinates, self.coordinates[-1] + (self.coordinates[1] - self.coordinates[0]))
137
+ coordinates = np.append(
138
+ self.coordinates, self.coordinates[-1] +
139
+ (self.coordinates[1] - self.coordinates[0]))
119
140
  else:
120
141
  signal = self.signal
121
142
  coordinates = self.coordinates
122
- sig = TimeSignalProcessing(coordinates, signal, Fs=self.fs, window=Windows.HAMMING,
143
+ sig = TimeSignalProcessing(coordinates,
144
+ signal,
145
+ Fs=self.fs,
146
+ window=Windows.HAMMING,
123
147
  window_size=len(signal))
124
148
  sig.psd(nb_points=n_fft, detrend=False)
125
149
 
126
150
  # compute the rsm psd
127
- self.__rms_effective(sig.frequency_Pxx, sig.Pxx, one_third_octave_bands, derivative)
151
+ self.__rms_effective(sig.frequency_Pxx, sig.Pxx,
152
+ one_third_octave_bands, derivative)
128
153
  # compute the effective values
129
154
  self.__effective_values(one_third_octave_bands, derivative)
130
155
 
131
-
132
- def __rms_effective(self, frequency: npt.NDArray[np.float64], Pxx: npt.NDArray[np.float64],
133
- one_third_octave_bands: List[Tuple[float, float]], derivative: List[int]):
156
+ def __rms_effective(self, frequency: npt.NDArray[np.float64],
157
+ Pxx: npt.NDArray[np.float64],
158
+ one_third_octave_bands: List[Tuple[float, float]],
159
+ derivative: List[int]):
134
160
  """
135
161
  Computes RMS square root of power spectral density
136
162
 
@@ -148,10 +174,13 @@ class SpaceSignalProcessing:
148
174
  for i, band in enumerate(one_third_octave_bands):
149
175
  # find indexes where the bands exist
150
176
  idx = np.where((frequency >= band[0]) & (frequency < band[1]))[0]
151
- Pxx[idx] = (2 * np.pi * frequency[idx]) ** (2 * derivative[i]) * Pxx[idx]
177
+ Pxx[idx] = (2 * np.pi * frequency[idx])**(2 *
178
+ derivative[i]) * Pxx[idx]
152
179
  self.rms_bands[i] = np.sqrt(np.sum(Pxx[idx] * delta_f))
153
180
 
154
- def __effective_values(self, one_third_octave_bands: List[Tuple[float, float]], derivative: List[int]):
181
+ def __effective_values(self, one_third_octave_bands: List[Tuple[float,
182
+ float]],
183
+ derivative: List[int]):
155
184
  """
156
185
  Computes the effective values of the signal
157
186
 
@@ -170,8 +199,13 @@ class SpaceSignalProcessing:
170
199
 
171
200
  for i, band in enumerate(one_third_octave_bands):
172
201
  derivative_value = derivative[i]
173
- sig = TimeSignalProcessing(self.coordinates, self.signal, Fs=self.fs)
174
- sig.filter(np.array(band), N=3, type_filter="bandpass", filter_design=FilterDesign.BUTTERWORTH)
202
+ sig = TimeSignalProcessing(self.coordinates,
203
+ self.signal,
204
+ Fs=self.fs)
205
+ sig.filter(np.array(band),
206
+ N=3,
207
+ type_filter="bandpass",
208
+ filter_design=FilterDesign.BUTTERWORTH)
175
209
  new_signal = sig.signal
176
210
 
177
211
  while derivative_value != 0:
@@ -181,7 +215,8 @@ class SpaceSignalProcessing:
181
215
  ksi = np.linspace(0, n * tau, int(n * tau / dx + 1))
182
216
  g = fout * np.exp(-ksi / tau)
183
217
 
184
- convoluted_signal = np.sqrt(np.convolve(new_signal**2, g) * dx / tau)
218
+ convoluted_signal = np.sqrt(
219
+ np.convolve(new_signal**2, g) * dx / tau)
185
220
  self.max_fast[i] = np.max(convoluted_signal)
186
221
  idx = np.floor((len(self.signal) - np.floor(self.DXmaxFast / dx)) / 2) + \
187
222
  np.linspace(0, np.floor(self.DXmaxFast / dx)-1, int(np.floor(self.DXmaxFast / dx)))
@@ -14,6 +14,7 @@ class FilterDesign(Enum):
14
14
  CHEBYSHEV = 2
15
15
  ELLIPTIC = 3
16
16
 
17
+
17
18
  class IntegrationRules(Enum):
18
19
  """
19
20
  Integration rules
@@ -21,6 +22,7 @@ class IntegrationRules(Enum):
21
22
  TRAPEZOID = 1
22
23
  SIMPSON = 2
23
24
 
25
+
24
26
  class Windows(Enum):
25
27
  """
26
28
  Windows types
@@ -34,10 +36,12 @@ class Windows(Enum):
34
36
  RECTANGULAR = 'boxcar'
35
37
  TRIANG = 'triang'
36
38
 
39
+
37
40
  class TimeSignalProcessing:
38
41
  """
39
42
  Signal processing class for time signals
40
43
  """
44
+
41
45
  def __init__(self,
42
46
  time: npt.NDArray[np.float64],
43
47
  signal: npt.NDArray[np.float64],
@@ -71,8 +75,7 @@ class TimeSignalProcessing:
71
75
  self.Sxx = None
72
76
  self.frequency_Sxx = None
73
77
  self.time_Sxx = None
74
- self.fft_settings = {"nb_points": None,
75
- "half_representation": False}
78
+ self.fft_settings = {"nb_points": None, "half_representation": False}
76
79
  # Track operations performed on the signal
77
80
  self.operations = []
78
81
 
@@ -93,25 +96,38 @@ class TimeSignalProcessing:
93
96
  self.use_window = False
94
97
  else:
95
98
  if window_size == 0:
96
- raise ValueError("When using a window the `window_size` must be specified")
99
+ raise ValueError(
100
+ "When using a window the `window_size` must be specified")
97
101
  if window_size % 2 != 0:
98
102
  raise ValueError("Window length must be even")
99
103
  if window not in Windows:
100
- raise ValueError(f"Window type {window} not supported. Available types: {list(Windows)}")
104
+ raise ValueError(
105
+ f"Window type {window} not supported. Available types: {list(Windows)}"
106
+ )
101
107
  if window_size > signal_length:
102
- raise ValueError(f"Window length ({window_size}) cannot be greater than signal length ({signal_length}).")
108
+ raise ValueError(
109
+ f"Window length ({window_size}) cannot be greater than signal length ({signal_length})."
110
+ )
103
111
 
104
112
  self.window = self.__create_window(window, window_size)
105
113
  self.window_size = window_size
106
114
  self.window_type = window
107
- self.nb_windows = int(np.ceil((signal_length / window_size) * 2 - 2))
115
+ self.nb_windows = int(
116
+ np.ceil((signal_length / window_size) * 2 - 2))
108
117
  self.use_window = True
109
118
 
110
119
  # pad signal at the end if necessary to get full windows
111
120
  if signal_length % window_size != 0:
112
- self.signal = np.append(self.signal, np.zeros(window_size - (signal_length % window_size)))
113
- self.time = np.append(self.time, np.zeros(window_size - (signal_length % window_size)))
114
- self.operations.append(f"Signal padded with zeros (original length: {signal_length}, new length: {len(self.signal)})")
121
+ self.signal = np.append(
122
+ self.signal,
123
+ np.zeros(window_size - (signal_length % window_size)))
124
+ self.time = np.append(
125
+ self.time, self.time[-1] + np.cumsum(
126
+ np.ones(window_size - (signal_length % window_size)) *
127
+ (1 / Fs)))
128
+ self.operations.append(
129
+ f"Signal padded with zeros (original length: {signal_length}, new length: {len(self.signal)})"
130
+ )
115
131
 
116
132
  def __str__(self) -> str:
117
133
  """
@@ -148,7 +164,8 @@ class TimeSignalProcessing:
148
164
  return "\n".join(info)
149
165
 
150
166
  @staticmethod
151
- def __create_window(window_type: Windows, size: int) -> npt.NDArray[np.float64]:
167
+ def __create_window(window_type: Windows,
168
+ size: int) -> npt.NDArray[np.float64]:
152
169
  """
153
170
  Create a window array of specified type and size
154
171
 
@@ -234,10 +251,10 @@ class TimeSignalProcessing:
234
251
  # peak amplitude of stationary sinusoids.
235
252
  spectrum_w[:, w] = np.fft.fft(signal_w, nfft) / normalise_fct
236
253
 
237
-
238
254
  self.amplitude = np.mean(np.abs(spectrum_w), axis=1)
239
255
  # self.phase = np.unwrap(np.angle(np.mean(spectrum_w, axis=1)))
240
- self.phase = np.angle(np.mean(np.exp(1j * np.angle(spectrum_w)), axis=1))
256
+ self.phase = np.angle(
257
+ np.mean(np.exp(1j * np.angle(spectrum_w)), axis=1))
241
258
 
242
259
  # compute frequency
243
260
  self.frequency = np.linspace(0, 1, nfft) * self.Fs
@@ -249,9 +266,11 @@ class TimeSignalProcessing:
249
266
  self.phase = self.phase[:int(nfft / 2)]
250
267
 
251
268
  # FFT settings: needed to perform inverse FFT
252
- self.fft_settings = {"nb_points": nfft,
253
- "half_representation": half_representation,
254
- "odd_length": odd_length}
269
+ self.fft_settings = {
270
+ "nb_points": nfft,
271
+ "half_representation": half_representation,
272
+ "odd_length": odd_length
273
+ }
255
274
 
256
275
  # Add to operations list
257
276
  op_info = f"FFT (points: {nfft}, half representation: {half_representation})"
@@ -275,7 +294,8 @@ class TimeSignalProcessing:
275
294
  "Please compute FFT with full representation.")
276
295
 
277
296
  if self.use_window:
278
- raise ValueError("Cannot perform inverse FFT on the windowed signal.")
297
+ raise ValueError(
298
+ "Cannot perform inverse FFT on the windowed signal.")
279
299
 
280
300
  # get FFT settings
281
301
  odd_length = self.fft_settings["odd_length"]
@@ -290,7 +310,8 @@ class TimeSignalProcessing:
290
310
  # inverse of the FFT signal
291
311
  self.signal_inv = np.real(spectrum_inv) * len(spectrum)
292
312
  # time from frequency
293
- self.time_inv = np.cumsum(np.ones(len(spectrum)) * 1 / self.Fs) - 1 / self.Fs
313
+ self.time_inv = np.cumsum(
314
+ np.ones(len(spectrum)) * 1 / self.Fs) - 1 / self.Fs
294
315
 
295
316
  if odd_length:
296
317
  # remove last sample
@@ -298,11 +319,17 @@ class TimeSignalProcessing:
298
319
  self.time_inv = self.time_inv[:-1]
299
320
 
300
321
  # Add to operations list
301
- self.operations.append("Inverse FFT" + (" with windowing" if self.use_window else ""))
302
-
303
- def integrate(self, rule: IntegrationRules = IntegrationRules.TRAPEZOID,
304
- baseline: bool = False, moving: bool = False, hp: bool = False, ini_cond: float = 0.,
305
- fpass: float = 0.5, n: int = 6):
322
+ self.operations.append("Inverse FFT" +
323
+ (" with windowing" if self.use_window else ""))
324
+
325
+ def integrate(self,
326
+ rule: IntegrationRules = IntegrationRules.TRAPEZOID,
327
+ baseline: bool = False,
328
+ moving: bool = False,
329
+ hp: bool = False,
330
+ ini_cond: float = 0.,
331
+ fpass: float = 0.5,
332
+ n: int = 6):
306
333
  """
307
334
  Numerical integration of signal
308
335
 
@@ -322,9 +349,13 @@ class TimeSignalProcessing:
322
349
 
323
350
  # integration rule
324
351
  if rule == IntegrationRules.TRAPEZOID:
325
- self.signal = integrate.cumulative_trapezoid(self.signal, self.time, initial=ini_cond)
352
+ self.signal = integrate.cumulative_trapezoid(self.signal,
353
+ self.time,
354
+ initial=ini_cond)
326
355
  elif rule == IntegrationRules.SIMPSON:
327
- self.signal = integrate.cumulative_simpson(self.signal, x=self.time, initial=ini_cond)
356
+ self.signal = integrate.cumulative_simpson(self.signal,
357
+ x=self.time,
358
+ initial=ini_cond)
328
359
  else:
329
360
  sys.exit("Integration rule not supported")
330
361
 
@@ -346,13 +377,18 @@ class TimeSignalProcessing:
346
377
  if moving:
347
378
  op_details.append("moving average correction")
348
379
  if hp:
349
- op_details.append(f"highpass filter (cutoff: {fpass} Hz, order: {n})")
380
+ op_details.append(
381
+ f"highpass filter (cutoff: {fpass} Hz, order: {n})")
350
382
 
351
383
  self.operations.append(f"Integration ({', '.join(op_details)})")
352
384
 
353
-
354
- def filter(self, Fpass: float, N: int, filter_design: FilterDesign = FilterDesign.ELLIPTIC,
355
- type_filter: str = "lowpass", rp: float = 0.01, rs: int = 60):
385
+ def filter(self,
386
+ Fpass: float,
387
+ N: int,
388
+ filter_design: FilterDesign = FilterDesign.ELLIPTIC,
389
+ type_filter: str = "lowpass",
390
+ rp: float = 0.01,
391
+ rs: int = 60):
356
392
  """
357
393
  Filter signal
358
394
 
@@ -377,11 +413,23 @@ class TimeSignalProcessing:
377
413
 
378
414
  # design filter
379
415
  if filter_design == FilterDesign.ELLIPTIC:
380
- z, p, k = signal.ellip(N, rp, rs, np.array(Fpass) / (self.Fs / 2), btype=type_filter, output='zpk')
416
+ z, p, k = signal.ellip(N,
417
+ rp,
418
+ rs,
419
+ np.array(Fpass) / (self.Fs / 2),
420
+ btype=type_filter,
421
+ output='zpk')
381
422
  elif filter_design == FilterDesign.BUTTERWORTH:
382
- z, p, k = signal.butter(N, np.array(Fpass) / (self.Fs / 2), btype=type_filter, output='zpk')
423
+ z, p, k = signal.butter(N,
424
+ np.array(Fpass) / (self.Fs / 2),
425
+ btype=type_filter,
426
+ output='zpk')
383
427
  elif filter_design == FilterDesign.CHEBYSHEV:
384
- z, p, k = signal.cheby1(N, rp, np.array(Fpass) / (self.Fs / 2), btype=type_filter, output='zpk')
428
+ z, p, k = signal.cheby1(N,
429
+ rp,
430
+ np.array(Fpass) / (self.Fs / 2),
431
+ btype=type_filter,
432
+ output='zpk')
385
433
 
386
434
  sos = signal.zpk2sos(z, p, k)
387
435
 
@@ -389,8 +437,8 @@ class TimeSignalProcessing:
389
437
  self.signal = signal.sosfiltfilt(sos, self.signal)
390
438
 
391
439
  # Add to operations list
392
- self.operations.append(f"Filter ({type_filter}, cutoff: {Fpass} Hz, order: {N})")
393
-
440
+ self.operations.append(
441
+ f"Filter ({type_filter}, cutoff: {Fpass} Hz, order: {N})")
394
442
 
395
443
  def psd(self, detrend: str = "linear", nb_points: Optional[int] = None):
396
444
  """
@@ -403,11 +451,15 @@ class TimeSignalProcessing:
403
451
  """
404
452
 
405
453
  if detrend not in ["linear", False]:
406
- raise ValueError("Detrend method not supported. Available methods: ['linear', False]")
454
+ raise ValueError(
455
+ "Detrend method not supported. Available methods: ['linear', False]"
456
+ )
407
457
 
408
458
  # check if window is initialized
409
459
  if not self.use_window:
410
- raise ValueError("No window defined. Please define a window when initialising SignalProcessing.")
460
+ raise ValueError(
461
+ "No window defined. Please define a window when initialising SignalProcessing."
462
+ )
411
463
 
412
464
  # if nb_points is None: nb_points is window length
413
465
  if nb_points is None:
@@ -416,12 +468,18 @@ class TimeSignalProcessing:
416
468
  nfft = nb_points
417
469
 
418
470
  # compute PSD using Welch method
419
- self.frequency_Pxx, self.Pxx = signal.welch(self.signal, fs=self.Fs, nperseg=self.window_size, nfft=nfft,
420
- window=self.window_type.value, scaling='density', detrend=detrend)
471
+ self.frequency_Pxx, self.Pxx = signal.welch(
472
+ self.signal,
473
+ fs=self.Fs,
474
+ nperseg=self.window_size,
475
+ nfft=nfft,
476
+ window=self.window_type.value,
477
+ scaling='density',
478
+ detrend=detrend)
421
479
 
422
480
  # Add to operations list
423
- self.operations.append(f"PSD (window: {self.window_type.name}, size: {self.window_size})")
424
-
481
+ self.operations.append(
482
+ f"PSD (window: {self.window_type.name}, size: {self.window_size})")
425
483
 
426
484
  def v_eff_SBR(self, n: int = 4, tau: float = 0.125):
427
485
  """
@@ -438,13 +496,12 @@ class TimeSignalProcessing:
438
496
  qsi = np.linspace(0, n * tau, int(n * tau * self.Fs + 1))
439
497
  g = fout * np.exp(-qsi / tau)
440
498
 
441
-
442
499
  # Frequency weighting parameters
443
500
  v0 = 1 / 1000 # Reference velocity [m/s]
444
- f0 = 5.6 # Reference frequency [Hz]
501
+ f0 = 5.6 # Reference frequency [Hz]
445
502
 
446
503
  # Handle even/odd signal length for FFT
447
- if self.signal.shape[0] % 2 != 0:
504
+ if self.signal.shape[0] % 2 != 0:
448
505
  nv1 = int(self.signal.shape[0] / 2 + 0.5)
449
506
  nv2 = int(self.signal.shape[0] / 2 - 0.5)
450
507
  else:
@@ -456,25 +513,25 @@ class TimeSignalProcessing:
456
513
  freq = np.arange(df, (nv1 + 1) * df, df)
457
514
 
458
515
  # Create high-pass weighting filter (human perception curve)
459
- Hv = (1 / v0) * 1 / (np.sqrt(1 + (f0 / freq) ** 2))
516
+ Hv = (1 / v0) * 1 / (np.sqrt(1 + (f0 / freq)**2))
460
517
  Hv = np.append(0, Hv) # Add DC component
461
518
 
462
519
  # Create low-pass filter with 50 Hz cutoff
463
520
  cut_off_number = int(np.ceil(50 / df))
464
521
  if cut_off_number < nv1:
465
522
  Hv2 = np.zeros(Hv.shape[0])
466
- Hv2[:cut_off_number+1] = 1
523
+ Hv2[:cut_off_number + 1] = 1
467
524
  else:
468
525
  Hv2 = np.ones(Hv.shape[0])
469
526
 
470
527
  # Applies the frequency weighting functions
471
528
  Fv = np.fft.fft(self.signal)
472
- Fhv = Hv2 * Hv * Fv[:nv1+1]
529
+ Fhv = Hv2 * Hv * Fv[:nv1 + 1]
473
530
  Fv = np.append(Fhv, np.flipud(np.conj(Fhv[1:nv2])))
474
531
  v_eff = np.real(np.fft.ifft(Fv))
475
532
 
476
533
  # moving root-mean-square through convolution with the exponential decay function `g`
477
- v_eff = np.sqrt( np.convolve(v_eff**2, g) * (1 / self.Fs) /tau)
534
+ v_eff = np.sqrt(np.convolve(v_eff**2, g) * (1 / self.Fs) / tau)
478
535
 
479
536
  self.v_eff = v_eff[:self.signal.shape[0]]
480
537
 
@@ -514,14 +571,19 @@ class TimeSignalProcessing:
514
571
  Compute spectrogram of signal
515
572
  """
516
573
  # compute spectrogram
517
- f, t, Sxx = signal.spectrogram(self.signal, fs=self.Fs, window=self.window_type.value,
518
- nperseg=self.window_size, noverlap=self.window_size // 8)
574
+ f, t, Sxx = signal.spectrogram(self.signal,
575
+ fs=self.Fs,
576
+ window=self.window_type.value,
577
+ nperseg=self.window_size,
578
+ noverlap=self.window_size // 8)
519
579
  self.Sxx = Sxx
520
580
  self.frequency_Sxx = f
521
581
  self.time_Sxx = t
522
582
 
523
583
  # Add to operations list
524
- self.operations.append(f"Spectrogram (nperseg: {self.window_size}, noverlap: {self.window_size // 8})")
584
+ self.operations.append(
585
+ f"Spectrogram (nperseg: {self.window_size}, noverlap: {self.window_size // 8})"
586
+ )
525
587
 
526
588
  def one_third_octave_bands(self):
527
589
  """
@@ -534,24 +596,31 @@ class TimeSignalProcessing:
534
596
  # https://en.wikipedia.org/wiki/Octave_band
535
597
  initial_frequency_band_number = -20
536
598
  final_frequency_band_number = 33
537
- names = ("10", "12.5", "16", "20", "25", "31.5", "40", "50", "63", "80", "100", "125", "160", "200", "250", "315", "400",
538
- "500", "630", "800", "1000", "1250", "1600", "2000", "2500", "3.150", "4000", "5000", "6300", "8000",
539
- "10000", "12500", "16000", "20000")
599
+ names = ("10", "12.5", "16", "20", "25", "31.5", "40", "50", "63",
600
+ "80", "100", "125", "160", "200", "250", "315", "400", "500",
601
+ "630", "800", "1000", "1250", "1600", "2000", "2500", "3.150",
602
+ "4000", "5000", "6300", "8000", "10000", "12500", "16000",
603
+ "20000")
540
604
 
541
605
  # compute centre frequencies of the bands
542
- f_centre = 1000 * (2 ** (np.arange(initial_frequency_band_number, final_frequency_band_number) / 3))
543
- f_upper = f_centre * (2 ** (1 / 6))
544
- f_lower = f_centre / (2 ** (1 / 6))
606
+ f_centre = 1000 * (2**(np.arange(initial_frequency_band_number,
607
+ final_frequency_band_number) / 3))
608
+ f_upper = f_centre * (2**(1 / 6))
609
+ f_lower = f_centre / (2**(1 / 6))
545
610
 
546
611
  # sum the signal for the bands
547
612
  if (self.Pxx is None) and (self.amplitude is None):
548
- raise ValueError("No PSD nor FFT computed. Please compute either first.")
613
+ raise ValueError(
614
+ "No PSD nor FFT computed. Please compute either first.")
549
615
 
550
616
  if self.Pxx is not None:
551
617
  # determine the frequency bands
552
- idx = np.where((f_lower < np.max(self.frequency_Pxx)) & (f_upper > np.min(self.frequency_Pxx)))[0]
618
+ idx = np.where((f_lower < np.max(self.frequency_Pxx))
619
+ & (f_upper > np.min(self.frequency_Pxx)))[0]
553
620
  if len(idx) == 0:
554
- raise ValueError("No frequency bands found in the PSD. Please check the frequency bands.")
621
+ raise ValueError(
622
+ "No frequency bands found in the PSD. Please check the frequency bands."
623
+ )
555
624
 
556
625
  delta_f = self.frequency_Pxx[1] - self.frequency_Pxx[0]
557
626
 
@@ -561,14 +630,19 @@ class TimeSignalProcessing:
561
630
 
562
631
  for i, val in enumerate(idx):
563
632
  self.octave_bands_Pxx[i] = float(names[val])
564
- mask = (self.frequency_Pxx >= f_lower[val]) & (self.frequency_Pxx < f_upper[val])
565
- self.octave_bands_Pxx_power[i] = np.sum(self.Pxx[mask] * delta_f)
633
+ mask = (self.frequency_Pxx
634
+ >= f_lower[val]) & (self.frequency_Pxx < f_upper[val])
635
+ self.octave_bands_Pxx_power[i] = np.sum(self.Pxx[mask] *
636
+ delta_f)
566
637
 
567
638
  if self.amplitude is not None:
568
639
  # determine the frequency bands
569
- idx = np.where((f_lower < np.max(self.frequency)) & (f_upper > np.min(self.frequency)))[0]
640
+ idx = np.where((f_lower < np.max(self.frequency))
641
+ & (f_upper > np.min(self.frequency)))[0]
570
642
  if len(idx) == 0:
571
- raise ValueError("No frequency bands found in the FFT. Please check the frequency bands.")
643
+ raise ValueError(
644
+ "No frequency bands found in the FFT. Please check the frequency bands."
645
+ )
572
646
 
573
647
  # compute the FFT for the bands
574
648
  self.octave_bands_fft = np.zeros(len(idx))
@@ -576,5 +650,7 @@ class TimeSignalProcessing:
576
650
 
577
651
  for i, val in enumerate(idx):
578
652
  self.octave_bands_fft[i] = float(names[val])
579
- mask = (self.frequency >= f_lower[val]) & (self.frequency < f_upper[val])
580
- self.octave_bands_fft_power[i] = np.sum(self.amplitude[mask]**2)
653
+ mask = (self.frequency >= f_lower[val]) & (self.frequency
654
+ < f_upper[val])
655
+ self.octave_bands_fft_power[i] = np.sum(
656
+ self.amplitude[mask]**2)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SignalProcessingTools
3
- Version: 1.2.2
3
+ Version: 1.2.4
4
4
  Summary: Signal processing tools
5
5
  Author: attr: SignalProcessingTools.__author__
6
6
  Author-email: bruno.zuadacoelho@deltares.nl, aron.noordam@deltares.nl
@@ -13,8 +13,9 @@ Requires-Dist: matplotlib>=3.10
13
13
  Requires-Dist: numpy>=2.2
14
14
  Requires-Dist: scipy>=1.15
15
15
  Provides-Extra: testing
16
- Requires-Dist: pytest>=8.3; extra == "testing"
17
- Requires-Dist: tox>=4.24; extra == "testing"
16
+ Requires-Dist: pytest>=8.0; extra == "testing"
17
+ Requires-Dist: tox>=4.13; extra == "testing"
18
+ Requires-Dist: pre_commit>=4.2.0; extra == "testing"
18
19
 
19
20
  # SignalProcessingTools
20
21
 
@@ -0,0 +1,8 @@
1
+ SignalProcessingTools/__init__.py,sha256=nMXg9GFsk8gCcyBnh1MAeUPvo4Zcuv3sdwGHZrxfpPE,36
2
+ SignalProcessingTools/__version__.py,sha256=V_BrxIpTLkU66higGwyPgbY3Y9GEmDzhELMqlxzfhW0,106
3
+ SignalProcessingTools/space_signal.py,sha256=GOg0gLkAZWDWqZiOMd9GJLF8pLuEvk21ub_PtD21AWs,8303
4
+ SignalProcessingTools/time_signal.py,sha256=yeWnCW6CGOZGfhE9vv1OSSeroa14vdVP1RCpwlWz4EU,24324
5
+ signalprocessingtools-1.2.4.dist-info/METADATA,sha256=sWUWxUJx3ETAvLfSJ0NHUIwXQhh6yh6uO34xga0efHg,4485
6
+ signalprocessingtools-1.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ signalprocessingtools-1.2.4.dist-info/top_level.txt,sha256=BzkLqG2CNDmcYLCI_-5cmnV91uDRfxNGotNulq6m4pk,22
8
+ signalprocessingtools-1.2.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- SignalProcessingTools/__init__.py,sha256=nMXg9GFsk8gCcyBnh1MAeUPvo4Zcuv3sdwGHZrxfpPE,36
2
- SignalProcessingTools/__version__.py,sha256=Lucz6_GwEnJ-LUvdGCXZC1n22nCjuvH9BJ1r985KRbI,106
3
- SignalProcessingTools/space_signal.py,sha256=Pq_S2EimxSIjBWdz7pkaNDypOFdI6YXZE7PrsVbwueg,7572
4
- SignalProcessingTools/time_signal.py,sha256=GAlA34B6qjkuvwsANixYS8rC7VR4jedpyg3IoMwIwXM,22505
5
- signalprocessingtools-1.2.2.dist-info/METADATA,sha256=oBGbc0RGlUcw1j0S2ZNVyygKjmZsdWgdkVQzrBLMVp0,4432
6
- signalprocessingtools-1.2.2.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
7
- signalprocessingtools-1.2.2.dist-info/top_level.txt,sha256=BzkLqG2CNDmcYLCI_-5cmnV91uDRfxNGotNulq6m4pk,22
8
- signalprocessingtools-1.2.2.dist-info/RECORD,,