Source code for seismic.correlate.preprocessing_fd
'''
Module containing functions for preprocessing in the frequency domain
:copyright:
The SeisMIC development team (makus@gfz-potsdam.de).
:license:
EUROPEAN UNION PUBLIC LICENCE v. 1.2
(https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12)
:author:
Peter Makus (makus@gfz-potsdam.de)
Created: Tuesday, 20th July 2021 03:40:11 pm
Last Modified: Wednesday, 28th June 2023 02:00:00 pm
'''
from copy import deepcopy
import logging
import numpy as np
import obspy.signal as osignal
[docs]def FDfilter(B: np.ndarray, args: dict, params: dict) -> np.ndarray:
"""
Filter Fourier-transformed data
Filter Fourier tranformed data by tapering in frequency domain. The `args`
dictionary is supposed to contain the key `flimit` with a value that is a
four element list or tuple defines the corner frequencies (f1, f2, f3, f4)
in Hz of the cosine taper which is one between f2 and f3 and tapers to zero
for f1 < f < f2 and f3 < f < f4.
:type B: numpy.ndarray
:param B: Fourier transformed time series data with frequency oriented\\
along the first dimension (columns)
:type args: dictionary
:param args: arguments dictionary as described above
:type params: dictionary
:param params: params['freqs'] contains an array with the freqency values
of the samples in `B`
:rtype: numpy.ndarray
:return: filtered spectal data
"""
args = deepcopy(args)
args.update({'freqs': params['freqs']})
tap = osignal.invsim.cosine_taper(B.shape[1], **args)
B *= np.tile(np.atleast_2d(tap), (B.shape[0], 1))
return B
[docs]def FDsignBitNormalization(
B: np.ndarray, args: dict, params: dict) -> np.ndarray:
"""
Sign bit normalization of frequency transformed data
Divides each sample by its amplitude resulting in trace with amplidues of
(-1, 0, 1). As this operation is done in frequency domain it requires two
Fourier transforms and is thus quite costly but alows to be performed
after other steps of frequency domain procesing e.g. whitening.
:type B: numpy.ndarray
:param B: Fourier transformed time series data with frequency oriented\\
along the first dimension (columns)
:type args: dictionary
:param args: not used in this function
:type params: dictionary
:param params: not used in this function
:rtype: numpy.ndarray
:return: frequency transform of the 1-bit normalized data
"""
B = np.fft.irfft(B)
# np.sign only takes the real part into account
C = np.sign(B)
return np.fft.rfft(C)
[docs]def spectralWhitening(B: np.ndarray, args: dict, params) -> np.ndarray:
"""
Spectal whitening of Fourier-transformed date
Normalize the amplitude spectrum of the complex spectra in `B`. The
`args` dictionary may contain the keyword `joint_norm`. If its value is
True the normalization of sets of three traces are normalized jointly by
the mean of their amplitude spectra. This is useful for later rotation of
correlated traces in the ZNE system into the ZRT system.
:type B: numpy.ndarray
:param B: Fourier transformed time series data with frequency oriented\\
along the first dimension (columns)
:type args: dictionary
:param args: arguments dictionary as described above
:type params: dictionary
:param params: not used here
:rtype: numpy.ndarray
:return: whitened spectal data
"""
absB = np.absolute(B)
if 'joint_norm' in list(args.keys()):
if args['joint_norm']:
assert B.shape[0] % 3 == 0, "for joint normalization the number\
of traces needs to the multiple of 3: %d" % B.shape[1]
for ii in np.arange(0, B.shape[0], 3):
absB[ii:ii+3, :] = np.tile(
np.atleast_2d(np.mean(absB[ii:ii+3, :], axis=0)), [3, 1])
with np.errstate(invalid='raise'):
try:
B = np.true_divide(B, absB)
except FloatingPointError as e:
errargs = np.argwhere(absB == 0)
# Report error where there is zero divides for a non-zero freq
if not np.all(errargs[:, 0] == 0):
logging.debug(f'{e} {errargs}')
# Set zero frequency component to zero
B[:, 0] = 0.j
return B