0
# Signal Processing
1
2
Comprehensive signal processing toolkit including digital filters, triggering algorithms, spectral analysis, array processing, and coordinate transformations specifically designed for seismological data analysis. These functions integrate seamlessly with ObsPy's core data structures and provide the foundation for advanced seismological analysis workflows.
3
4
## Capabilities
5
6
### Digital Filtering
7
8
High-performance digital filters optimized for seismological data including Butterworth, Chebyshev, and FIR implementations with zero-phase options.
9
10
```python { .api }
11
# Import from obspy.signal.filter
12
def bandpass(data, freqmin: float, freqmax: float, df: float, corners: int = 4,
13
zerophase: bool = False, **kwargs):
14
"""
15
Butterworth bandpass filter.
16
17
Args:
18
data: Input data array
19
freqmin: Low frequency corner in Hz
20
freqmax: High frequency corner in Hz
21
df: Sampling frequency in Hz
22
corners: Filter order (number of poles)
23
zerophase: Apply zero-phase filter
24
**kwargs: Additional filter options
25
26
Returns:
27
Filtered data array
28
"""
29
30
def lowpass(data, freq: float, df: float, corners: int = 4,
31
zerophase: bool = False, **kwargs):
32
"""
33
Butterworth lowpass filter.
34
35
Args:
36
data: Input data array
37
freq: Corner frequency in Hz
38
df: Sampling frequency in Hz
39
corners: Filter order
40
zerophase: Apply zero-phase filter
41
**kwargs: Additional options
42
43
Returns:
44
Filtered data array
45
"""
46
47
def highpass(data, freq: float, df: float, corners: int = 4,
48
zerophase: bool = False, **kwargs):
49
"""
50
Butterworth highpass filter.
51
52
Args:
53
data: Input data array
54
freq: Corner frequency in Hz
55
df: Sampling frequency in Hz
56
corners: Filter order
57
zerophase: Apply zero-phase filter
58
**kwargs: Additional options
59
60
Returns:
61
Filtered data array
62
"""
63
64
def bandstop(data, freqmin: float, freqmax: float, df: float, corners: int = 4,
65
zerophase: bool = False, **kwargs):
66
"""
67
Butterworth bandstop (notch) filter.
68
69
Args:
70
data: Input data array
71
freqmin: Low frequency corner in Hz
72
freqmax: High frequency corner in Hz
73
df: Sampling frequency in Hz
74
corners: Filter order
75
zerophase: Apply zero-phase filter
76
**kwargs: Additional options
77
78
Returns:
79
Filtered data array
80
"""
81
82
def lowpass_cheby_2(data, freq: float, df: float, maxorder: int = 12,
83
ba: bool = False, freq_passband: bool = False):
84
"""
85
Chebyshev type 2 lowpass filter.
86
87
Args:
88
data: Input data array
89
freq: Corner frequency in Hz
90
df: Sampling frequency in Hz
91
maxorder: Maximum filter order
92
ba: Return filter coefficients if True
93
freq_passband: Frequency is passband edge if True
94
95
Returns:
96
Filtered data array or filter coefficients
97
"""
98
99
def lowpass_fir(data, freq: float, df: float, ntaps: int = None, **kwargs):
100
"""
101
FIR lowpass filter using Parks-McClellan optimal design.
102
103
Args:
104
data: Input data array
105
freq: Corner frequency in Hz
106
df: Sampling frequency in Hz
107
ntaps: Number of filter taps (auto if None)
108
**kwargs: FIR design options
109
110
Returns:
111
Filtered data array
112
"""
113
114
def remez_fir(data, freqs: list, desired: list, df: float, **kwargs):
115
"""
116
FIR filter using Remez exchange algorithm.
117
118
Args:
119
data: Input data array
120
freqs: List of frequency band edges in Hz
121
desired: List of desired gains for each band
122
df: Sampling frequency in Hz
123
**kwargs: Remez algorithm options
124
125
Returns:
126
Filtered data array
127
"""
128
```
129
130
### Event Detection and Triggering
131
132
Automated event detection algorithms for identifying seismic arrivals and determining onset times with sub-sample precision.
133
134
```python { .api }
135
# Import from obspy.signal.trigger
136
def classic_sta_lta(a, nsta: int, nlta: int):
137
"""
138
Classic STA/LTA trigger algorithm.
139
140
Args:
141
a: Input data array
142
nsta: Short time average window length in samples
143
nlta: Long time average window length in samples
144
145
Returns:
146
STA/LTA characteristic function array
147
"""
148
149
def recursive_sta_lta(a, nsta: int, nlta: int):
150
"""
151
Recursive STA/LTA trigger algorithm (faster implementation).
152
153
Args:
154
a: Input data array
155
nsta: Short time average window length in samples
156
nlta: Long time average window length in samples
157
158
Returns:
159
STA/LTA characteristic function array
160
"""
161
162
def delayed_sta_lta(a, nsta: int, nlta: int):
163
"""
164
Delayed STA/LTA trigger algorithm.
165
166
Args:
167
a: Input data array
168
nsta: Short time average window length in samples
169
nlta: Long time average window length in samples
170
171
Returns:
172
STA/LTA characteristic function array
173
"""
174
175
def carl_sta_trig(a, nsta: int, nlta: int, ratio: float, quiet: float):
176
"""
177
Carl-STA-Trig algorithm for coincidence triggering.
178
179
Args:
180
a: Input data array
181
nsta: Short time average window length in samples
182
nlta: Long time average window length in samples
183
ratio: Trigger ratio threshold
184
quiet: Quiet ratio threshold
185
186
Returns:
187
Trigger times array
188
"""
189
190
def z_detect(a, nsta: int):
191
"""
192
Z-detector algorithm for event detection.
193
194
Args:
195
a: Input data array
196
nsta: Analysis window length in samples
197
198
Returns:
199
Z-detector characteristic function
200
"""
201
202
def trigger_onset(charfct, thres1: float, thres2: float, max_len: int = None,
203
max_len_delete: bool = False):
204
"""
205
Determine trigger on and off times from characteristic function.
206
207
Args:
208
charfct: Characteristic function array
209
thres1: Trigger on threshold
210
thres2: Trigger off threshold
211
max_len: Maximum trigger length in samples
212
max_len_delete: Delete long triggers if True
213
214
Returns:
215
Array of (trigger_on, trigger_off) sample indices
216
"""
217
218
def coincidence_trigger(trigger_type: str, thr_on: float, thr_off: float,
219
stream, thr_coincidence_sum: int, **kwargs):
220
"""
221
Multi-station coincidence triggering.
222
223
Args:
224
trigger_type: Trigger algorithm ('recstalta', 'classicstalta', etc.)
225
thr_on: Trigger on threshold
226
thr_off: Trigger off threshold
227
stream: Stream with multiple traces
228
thr_coincidence_sum: Minimum stations for coincidence
229
**kwargs: Algorithm-specific parameters
230
231
Returns:
232
List of coincident trigger dictionaries
233
"""
234
```
235
236
### Probabilistic Power Spectral Density
237
238
Advanced spectral analysis class for long-term noise analysis and instrument performance monitoring following McNamara and Buland (2004) methodology.
239
240
```python { .api }
241
class PPSD:
242
def __init__(self, stats, paz_or_resp, dtiny: float = 1e-6,
243
dmedian: float = None, period_smoothing_width_octaves: float = 1.0,
244
period_step_octaves: float = 0.125, **kwargs):
245
"""
246
Probabilistic power spectral density analysis.
247
248
Args:
249
stats: Trace.stats object with metadata
250
paz_or_resp: Poles and zeros dict or response object
251
dtiny: Tiny float for numerical stability
252
dmedian: Median smoothing parameter
253
period_smoothing_width_octaves: Smoothing width in octaves
254
period_step_octaves: Period step size in octaves
255
**kwargs: Additional PPSD parameters
256
"""
257
258
def add(self, stream, verbose: bool = False):
259
"""
260
Add stream data to PPSD analysis.
261
262
Args:
263
stream: Stream object with seismic data
264
verbose: Print processing information
265
"""
266
267
def plot(self, filename: str = None, show_coverage: bool = True,
268
show_histogram: bool = True, show_percentiles: bool = False,
269
percentiles: list = [0, 25, 50, 75, 100], **kwargs):
270
"""
271
Plot PPSD results.
272
273
Args:
274
filename: Save plot to file if specified
275
show_coverage: Show data coverage timeline
276
show_histogram: Show probability density histogram
277
show_percentiles: Show percentile lines
278
percentiles: List of percentiles to show
279
**kwargs: Additional plotting options
280
"""
281
282
def plot_temporal(self, starttime=None, endtime=None, **kwargs):
283
"""
284
Plot temporal evolution of PSDs.
285
286
Args:
287
starttime: Start time for plot window
288
endtime: End time for plot window
289
**kwargs: Plotting options
290
"""
291
292
def plot_spectrogram(self, filename: str = None, **kwargs):
293
"""
294
Plot spectrogram representation of PSDs.
295
296
Args:
297
filename: Save plot to file if specified
298
**kwargs: Plotting options
299
"""
300
301
def save_npz(self, filename: str):
302
"""
303
Save PPSD data to NumPy compressed file.
304
305
Args:
306
filename: Output filename (.npz)
307
"""
308
309
def load_npz(self, filename: str):
310
"""
311
Load PPSD data from NumPy compressed file.
312
313
Args:
314
filename: Input filename (.npz)
315
"""
316
317
def get_percentile(self, percentile: float = 50, hist_stack=None):
318
"""
319
Get specified percentile curve from PPSD.
320
321
Args:
322
percentile: Percentile value (0-100)
323
hist_stack: Histogram stack to use
324
325
Returns:
326
Tuple of (periods, power_values)
327
"""
328
329
def calculate_histogram(self, starttime=None, endtime=None):
330
"""
331
Calculate probability density histogram for time window.
332
333
Args:
334
starttime: Window start time
335
endtime: Window end time
336
337
Returns:
338
2D histogram array
339
"""
340
```
341
342
### Instrument Response Simulation
343
344
Instrument response correction and simulation functions for removing instrument effects and simulating different sensor responses.
345
346
```python { .api }
347
# Import from obspy.signal.invsim
348
def simulate_seismometer(data, samp_rate: float, paz_remove=None, paz_simulate=None,
349
taper: bool = True, simulate_sensitivity: bool = True,
350
taper_fraction: float = 0.05, pre_filt=None, zero_mean: bool = True,
351
nfft_pow2: bool = False, **kwargs):
352
"""
353
Simulate seismometer response.
354
355
Args:
356
data: Input data array
357
samp_rate: Sampling rate in Hz
358
paz_remove: Poles and zeros to remove (dict with 'poles', 'zeros', 'gain')
359
paz_simulate: Poles and zeros to simulate
360
taper: Apply taper to data
361
simulate_sensitivity: Include sensitivity simulation
362
taper_fraction: Taper length as fraction of trace
363
pre_filt: Pre-filter frequencies (list of 4 corners)
364
zero_mean: Remove mean before processing
365
nfft_pow2: Use power-of-2 FFT length
366
**kwargs: Additional options
367
368
Returns:
369
Simulated data array
370
"""
371
372
def corn_freq_2_paz(fc: float, damp: float = 0.707):
373
"""
374
Convert corner frequency and damping to poles and zeros.
375
376
Args:
377
fc: Corner frequency in Hz
378
damp: Damping factor (default 0.707 for critical damping)
379
380
Returns:
381
Dictionary with poles, zeros, and gain
382
"""
383
384
def paz_2_amplitude_value_of_freq_resp(paz, freq: float):
385
"""
386
Calculate amplitude response at specific frequency.
387
388
Args:
389
paz: Poles and zeros dictionary
390
freq: Frequency in Hz
391
392
Returns:
393
Amplitude response value
394
"""
395
396
def evalresp(t_samp: float, nfft: int, filename: str, date, units: str = "VEL",
397
freq: bool = False, **kwargs):
398
"""
399
Use evalresp to calculate instrument response.
400
401
Args:
402
t_samp: Sampling period in seconds
403
nfft: Number of FFT points
404
filename: RESP filename or SEED response
405
date: Response date (UTCDateTime)
406
units: Output units ('DIS', 'VEL', 'ACC')
407
freq: Return frequency array if True
408
**kwargs: Additional evalresp options
409
410
Returns:
411
Complex response array (and frequencies if freq=True)
412
"""
413
414
def cosine_taper(npts: int, p: float = 0.1):
415
"""
416
Generate cosine taper window.
417
418
Args:
419
npts: Number of points
420
p: Taper fraction (0.1 = 10% taper on each end)
421
422
Returns:
423
Taper window array
424
"""
425
```
426
427
### Array Processing and Beamforming
428
429
Multi-station array processing techniques for detecting and locating seismic sources using coherent signal processing across seismic arrays.
430
431
```python { .api }
432
# Import from obspy.signal.array_analysis
433
def array_processing(stream, win_len: float, win_frac: float, sll_x: float,
434
slm_x: float, sll_y: float, slm_y: float, sl_s: float,
435
semb_thres: float = -1e9, vel_thres: float = -1e9,
436
frqlow: float = 1.0, frqhigh: float = 8.0, samp_rate: float = 40.0,
437
prewhiten: int = 0, **kwargs):
438
"""
439
Array processing for slowness and back-azimuth estimation.
440
441
Args:
442
stream: Stream with array data (requires coordinates)
443
win_len: Sliding window length in seconds
444
win_frac: Window overlap fraction (0-1)
445
sll_x: Slowness grid minimum X in s/km
446
slm_x: Slowness grid maximum X in s/km
447
sll_y: Slowness grid minimum Y in s/km
448
slm_y: Slowness grid maximum Y in s/km
449
sl_s: Slowness grid spacing in s/km
450
semb_thres: Semblance threshold
451
vel_thres: Velocity threshold in km/s
452
frqlow: Low frequency in Hz
453
frqhigh: High frequency in Hz
454
samp_rate: Sampling rate in Hz
455
prewhiten: Pre-whitening (0=off, 1=on)
456
**kwargs: Additional options
457
458
Returns:
459
Structured array with processing results
460
"""
461
462
def get_geometry(stream, coordsys: str = 'lonlat', return_center: bool = False):
463
"""
464
Get array geometry from stream coordinates.
465
466
Args:
467
stream: Stream with coordinate metadata
468
coordsys: Coordinate system ('lonlat', 'xy')
469
return_center: Return array center coordinates
470
471
Returns:
472
Array geometry matrix (and center if requested)
473
"""
474
475
def get_timeshift(geometry, sll_x: float, slm_x: float, sll_y: float, slm_y: float,
476
sl_s: float, grdpts_x: int, grdpts_y: int):
477
"""
478
Calculate time shifts for slowness grid.
479
480
Args:
481
geometry: Array geometry matrix
482
sll_x: Slowness grid minimum X
483
slm_x: Slowness grid maximum X
484
sll_y: Slowness grid minimum Y
485
slm_y: Slowness grid maximum Y
486
sl_s: Slowness grid spacing
487
grdpts_x: Grid points in X direction
488
grdpts_y: Grid points in Y direction
489
490
Returns:
491
Time shift array for grid points
492
"""
493
```
494
495
### Coordinate Rotation
496
497
Coordinate system transformations for seismological data including horizontal component rotation and three-component rotations to ray-coordinate systems.
498
499
```python { .api }
500
# Import from obspy.signal.rotate
501
def rotate_ne_rt(n, e, ba: float):
502
"""
503
Rotate North-East components to Radial-Transverse.
504
505
Args:
506
n: North component data array
507
e: East component data array
508
ba: Back-azimuth in degrees
509
510
Returns:
511
Tuple of (radial, transverse) component arrays
512
"""
513
514
def rotate_rt_ne(r, t, ba: float):
515
"""
516
Rotate Radial-Transverse components to North-East.
517
518
Args:
519
r: Radial component data array
520
t: Transverse component data array
521
ba: Back-azimuth in degrees
522
523
Returns:
524
Tuple of (north, east) component arrays
525
"""
526
527
def rotate_zne_lqt(z, n, e, ba: float, inc: float):
528
"""
529
Rotate ZNE components to LQT ray coordinate system.
530
531
Args:
532
z: Vertical component data array
533
n: North component data array
534
e: East component data array
535
ba: Back-azimuth in degrees
536
inc: Inclination angle in degrees
537
538
Returns:
539
Tuple of (L, Q, T) component arrays
540
L: P-wave direction, Q: SV-wave direction, T: SH-wave direction
541
"""
542
543
def rotate_lqt_zne(l, q, t, ba: float, inc: float):
544
"""
545
Rotate LQT ray coordinates back to ZNE components.
546
547
Args:
548
l: L component data array (P-wave direction)
549
q: Q component data array (SV-wave direction)
550
t: T component data array (SH-wave direction)
551
ba: Back-azimuth in degrees
552
inc: Inclination angle in degrees
553
554
Returns:
555
Tuple of (Z, N, E) component arrays
556
"""
557
```
558
559
### Cross Correlation
560
561
Cross-correlation analysis for measuring time delays between signals and template matching applications.
562
563
```python { .api }
564
# Import from obspy.signal.cross_correlation
565
def correlate(a, b, shift_len: int, demean: bool = True, normalize: str = 'naive',
566
method: str = 'auto'):
567
"""
568
Cross-correlate two signals.
569
570
Args:
571
a: First signal array
572
b: Second signal array
573
shift_len: Maximum shift length in samples
574
demean: Remove mean before correlation
575
normalize: Normalization method ('naive', 'full')
576
method: Correlation method ('auto', 'fft', 'direct')
577
578
Returns:
579
Cross-correlation array
580
"""
581
582
def xcorr_max(a, b, abs_max: bool = True):
583
"""
584
Find maximum cross-correlation and lag.
585
586
Args:
587
a: First signal array
588
b: Second signal array
589
abs_max: Use absolute maximum if True
590
591
Returns:
592
Tuple of (correlation_coefficient, lag_in_samples)
593
"""
594
595
def correlate_template(data, template, mode: str = 'valid', normalize: str = 'full'):
596
"""
597
Template matching using cross-correlation.
598
599
Args:
600
data: Data array to search
601
template: Template array to match
602
mode: Correlation mode ('valid', 'full', 'same')
603
normalize: Normalization method
604
605
Returns:
606
Cross-correlation array
607
"""
608
```
609
610
### Spectral Analysis
611
612
Frequency domain analysis tools including power spectral density estimation, spectrograms, and multitaper methods.
613
614
```python { .api }
615
# Import from obspy.signal.spectral_estimation
616
def PPSD(stats, paz_or_resp, **kwargs):
617
"""Create PPSD instance (alias for main PPSD class)."""
618
619
def psd(data, NFFT: int = 256, Fs: float = 2, detrend: str = 'linear',
620
window: str = 'hann', noverlap: int = 0):
621
"""
622
Power spectral density using Welch's method.
623
624
Args:
625
data: Input data array
626
NFFT: FFT length
627
Fs: Sampling frequency
628
detrend: Detrend method ('linear', 'constant', 'none')
629
window: Window function
630
noverlap: Overlap between segments
631
632
Returns:
633
Tuple of (frequencies, power_spectral_density)
634
"""
635
636
def spectrogram(data, samp_rate: float, per_lap: float = 0.9, wlen: float = None,
637
log: bool = False, outfile: str = None, fmt: str = None,
638
axes=None, dbscale: bool = False, **kwargs):
639
"""
640
Create spectrogram of time series data.
641
642
Args:
643
data: Input data array
644
samp_rate: Sampling rate in Hz
645
per_lap: Percentage of overlap (0-1)
646
wlen: Window length in seconds
647
log: Use logarithmic frequency scale
648
outfile: Save to file if specified
649
fmt: File format for saving
650
axes: Matplotlib axes object
651
dbscale: Use dB scale for power
652
**kwargs: Additional plotting options
653
654
Returns:
655
Tuple of (time, frequency, spectrogram_array)
656
"""
657
```
658
659
## Usage Examples
660
661
### Basic Filtering Workflow
662
663
```python
664
from obspy import read
665
from obspy.signal.filter import bandpass, lowpass
666
import numpy as np
667
668
# Read seismic data
669
st = read('seismic_data.mseed')
670
trace = st[0]
671
672
# Apply bandpass filter (1-10 Hz)
673
filtered_data = bandpass(trace.data, 1.0, 10.0, trace.stats.sampling_rate,
674
corners=4, zerophase=True)
675
676
# Apply to trace directly using trace method
677
trace.filter('bandpass', freqmin=1.0, freqmax=10.0, corners=4, zerophase=True)
678
679
# Multiple filter stages
680
trace.filter('highpass', freq=0.5) # Remove long periods
681
trace.filter('lowpass', freq=25.0) # Anti-alias before decimation
682
trace.decimate(2) # Reduce sampling rate by factor of 2
683
```
684
685
### Event Detection with STA/LTA
686
687
```python
688
from obspy import read
689
from obspy.signal.trigger import classic_sta_lta, trigger_onset, plot_trigger
690
import matplotlib.pyplot as plt
691
692
# Read data and select trace
693
st = read('continuous_data.mseed')
694
trace = st[0]
695
696
# Calculate STA/LTA characteristic function
697
df = trace.stats.sampling_rate
698
cft = classic_sta_lta(trace.data, int(5 * df), int(10 * df))
699
700
# Determine trigger times
701
thr_on = 1.5
702
thr_off = 0.5
703
on_off = trigger_onset(cft, thr_on, thr_off)
704
705
# Plot results
706
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
707
708
# Plot original trace
709
times = trace.times()
710
ax1.plot(times, trace.data, 'black')
711
ax1.set_ylabel('Amplitude')
712
ax1.set_title(f'{trace.id} - Raw Data')
713
714
# Plot characteristic function with triggers
715
ax2.plot(times, cft, 'blue')
716
ax2.axhline(thr_on, color='red', linestyle='--', label='Thr On')
717
ax2.axhline(thr_off, color='orange', linestyle='--', label='Thr Off')
718
719
# Mark trigger times
720
for triggers in on_off:
721
on_time = times[triggers[0]]
722
off_time = times[triggers[1]]
723
ax2.axvspan(on_time, off_time, alpha=0.3, color='red')
724
725
ax2.set_ylabel('STA/LTA')
726
ax2.set_xlabel('Time (s)')
727
ax2.legend()
728
plt.tight_layout()
729
plt.show()
730
```
731
732
### PPSD Noise Analysis
733
734
```python
735
from obspy import read
736
from obspy.signal import PPSD
737
from obspy.clients.fdsn import Client
738
739
# Get station response
740
client = Client("IRIS")
741
inventory = client.get_stations(network="IU", station="ANMO", location="00",
742
channel="BHZ", level="response")
743
744
# Read continuous data
745
st = read('continuous_noise.mseed')
746
747
# Create PPSD instance
748
ppsd = PPSD(st[0].stats, metadata=inventory)
749
750
# Add data to analysis (can add multiple days/files)
751
ppsd.add(st)
752
753
# Plot results
754
ppsd.plot(show_coverage=True, show_histogram=True)
755
ppsd.plot_temporal() # Show temporal evolution
756
ppsd.plot_spectrogram() # Show spectrogram view
757
758
# Get percentile curves
759
periods, median_psd = ppsd.get_percentile(percentile=50)
760
periods, low_noise = ppsd.get_percentile(percentile=10)
761
periods, high_noise = ppsd.get_percentile(percentile=90)
762
```
763
764
### Array Beamforming
765
766
```python
767
from obspy import read
768
from obspy.signal.array_analysis import array_processing, get_geometry
769
import numpy as np
770
771
# Read array data (requires coordinate metadata)
772
st = read('array_data.mseed')
773
774
# Get array geometry
775
geometry = get_geometry(st, coordsys='lonlat')
776
777
# Array processing parameters
778
kwargs = dict(
779
# sliding window properties
780
win_len=1.0, # window length in seconds
781
win_frac=0.05, # window overlap fraction
782
# slowness grid
783
sll_x=-3.0, slm_x=3.0, # X slowness limits (s/km)
784
sll_y=-3.0, slm_y=3.0, # Y slowness limits (s/km)
785
sl_s=0.03, # slowness step (s/km)
786
# frequency band
787
frqlow=1.0, frqhigh=8.0, # frequency band (Hz)
788
# processing
789
samp_rate=40.0, # sampling rate
790
prewhiten=0, # pre-whitening off
791
semb_thres=-1e9, # semblance threshold
792
vel_thres=-1e9, # velocity threshold
793
)
794
795
# Perform array processing
796
out = array_processing(st, **kwargs)
797
798
# Extract results
799
times = out[:, 0] # time stamps
800
rel_power = out[:, 1] # relative power
801
abs_power = out[:, 2] # absolute power
802
baz = out[:, 3] # back-azimuth
803
slow = out[:, 4] # slowness magnitude
804
805
# Convert slowness to apparent velocity
806
app_vel = 1.0 / slow # km/s
807
808
print(f"Mean back-azimuth: {np.mean(baz):.1f} degrees")
809
print(f"Mean apparent velocity: {np.mean(app_vel):.2f} km/s")
810
```
811
812
## Types
813
814
```python { .api }
815
# Poles and zeros dictionary structure
816
PoleZeroDict = {
817
'poles': list[complex], # List of complex poles
818
'zeros': list[complex], # List of complex zeros
819
'gain': float, # System gain
820
'sensitivity': float # Instrument sensitivity (optional)
821
}
822
823
# Array processing result structure (structured NumPy array)
824
ArrayProcessingResult = np.dtype([
825
('time', 'f8'), # Time stamp
826
('rel_power', 'f8'), # Relative power
827
('abs_power', 'f8'), # Absolute power
828
('baz', 'f8'), # Back-azimuth in degrees
829
('slow', 'f8') # Slowness magnitude in s/km
830
])
831
832
# Trigger onset result structure
833
TriggerOnset = np.dtype([
834
('time_on', 'i4'), # Trigger on time (sample index)
835
('time_off', 'i4') # Trigger off time (sample index)
836
])
837
```