Module libquantum.blast_pulse

This module facilitates the rapid construction of the GT blast pulse synthetic, its integral and derivatives, and its spectrum References: - Garcés, M. A. (2019). Explosion Source Models, Chapter in Infrasound Monitoring for Atmospheric Studies, Second Edition, Springer, Switzerland, DOI 10.1007/978-3-319-75140_5, p. 273-345. - Schnurr, J. M., K. Kim, M. A. Garcés, A. Rodgers (2020). Improved parametric models for explosion pressure signals derived from large datasets, Seism. Res. Lett. - Kim, K, A. R. Rodgers, M. A. Garces, and S. C. Myers (2021). Empirical Acoustic Source Model for Chemical Explosions in Air. Bulletin of the Seismological Society of America

Expand source code
"""
This module facilitates the rapid construction of the GT blast pulse synthetic,
its integral and derivatives, and its spectrum
References:
- Garcés, M. A. (2019). Explosion Source Models,
Chapter in Infrasound Monitoring for Atmospheric Studies,
Second Edition, Springer, Switzerland, DOI 10.1007/978-3-319-75140_5, p. 273-345.
- Schnurr, J. M., K. Kim, M. A. Garcés, A. Rodgers (2020).
Improved parametric models for explosion pressure signals derived from large datasets,
Seism. Res. Lett.
- Kim, K, A. R. Rodgers, M. A. Garces, and S. C. Myers (2021).
Empirical Acoustic Source Model for Chemical Explosions in Air.
Bulletin of the Seismological Society of America
"""

import numpy as np
from typing import Optional, Tuple, Union
from libquantum.synthetics import white_noise_fbits, antialias_halfNyquist
from libquantum.scales import EPSILON


def gt_blast_period_center(time_center_s: np.ndarray,
                           pseudo_period_s: float) -> np.ndarray:
    """
    GT blast pulse

    :param time_center_s: array with time
    :param pseudo_period_s: period in seconds
    :return: numpy array with GT blast pulse
    """
    # With the +1, tau is the zero crossing time - time_start renamed to time_zero for first zero crossing.
    time_pos_s = pseudo_period_s/4.
    tau = time_center_s/time_pos_s + 1.
    # Initialize GT
    p_GT = np.zeros(tau.size)  # Granstrom-Triangular (GT), 2019
    # Initialize time ranges
    sigint1 = np.where((0.0 <= tau) & (tau <= 1.))  # ONLY positive pulse
    sigintG17 = np.where((1. < tau) & (tau <= 1 + np.sqrt(6.)))  # GT balanced pulse
    p_GT[sigint1] = (1. - tau[sigint1])
    p_GT[sigintG17] = 1./6. * (1. - tau[sigintG17]) * (1. + np.sqrt(6) - tau[sigintG17]) ** 2.

    return p_GT


def gt_hilbert_blast_period_center(time_center_s: np.ndarray,
                                   pseudo_period_s: float) -> np.ndarray:
    """
    Hilbert transform of the GT blast pulse

    :param time_center_s: array with time
    :param pseudo_period_s: period in seconds
    :return: numpy array with Hilbert transform of the GT blast pulse
    """
    # With the +1, tau is the zero crossing time - time_start renamed to time_zero for first zero crossing.
    time_pos_s = pseudo_period_s/4.
    tau = time_center_s/time_pos_s + 1.
    a = 1 + np.sqrt(6)
    # Initialize GT
    p_GT_H = np.zeros(tau.size)  # Hilbert of Granstrom-Triangular (GT), 2019
    # Initialize time ranges
    sigint1 = np.where((0.0 <= tau) & (tau <= 1.))  # ONLY positive pulse
    sigint2 = np.where((1. < tau) & (tau <= 1 + np.sqrt(6.)))  # GT balanced pulse
    tau1 = tau[sigint1]
    tau2 = tau[sigint2]

    p_GT_H[sigint1] = 1. + (1-tau1)*np.log(tau1+EPSILON) - (1-tau1)*np.log(1-tau1+EPSILON)
    p_GT_H21 = (a-1)/6. * (a*(2*a+5) - 1 + 6*tau2**2 - 3*tau2*(1+3*a))
    p_GT_H22 = (tau2-1)*(a-tau2)**2 * (np.log(a-tau2+EPSILON) - np.log(tau2-1+EPSILON))
    p_GT_H[sigint2] = 1./6. * (p_GT_H21 + p_GT_H22)
    p_GT_H /= np.pi

    return p_GT_H


def gt_blast_center_fast(frequency_peak_hz: float = 6.3,
                         sample_rate_hz: float = 100.,
                         noise_std_loss_bits: float = 16) -> Tuple[np.ndarray, np.ndarray]:
    """
    Fast computation of GT pulse with noise

    :param frequency_peak_hz: peak frequency, nominal 6.3 Hz for 1 tonne TNT
    :param sample_rate_hz: sample rate, nominal 100 Hz
    :param noise_std_loss_bits: noise loss relative to signal variance
    :return: centered time in seconds, GT pulse with white noise
    """

    duration_s = 16/frequency_peak_hz       # 16 cycles for 6th octave (M = 14)
    pseudo_period_s = 1/frequency_peak_hz
    duration_points = int(duration_s*sample_rate_hz)
    time_center_s = np.arange(duration_points)/sample_rate_hz
    time_center_s -= time_center_s[-1]/2.
    sig_gt = gt_blast_period_center(time_center_s, pseudo_period_s)
    sig_noise = white_noise_fbits(sig_gt, noise_std_loss_bits)
    gt_white = sig_gt + sig_noise
    # AA filter
    gt_white_aa = antialias_halfNyquist(gt_white)
    return time_center_s, gt_white_aa


def gt_blast_center_noise(duration_s: float = 16,
                          frequency_peak_hz: float = 6.3,
                          sample_rate_hz: float = 100,
                          noise_std_loss_bits: float = 16) -> Tuple[np.ndarray, np.ndarray]:
    """
    Fast computation of GT pulse with noise for a specified duration in seconds

    :param duration_s: signal duration in seconds
    :param frequency_peak_hz: peak frequency, nominal 6.3 Hz for 1 tonne TNT
    :param sample_rate_hz: sample rate, nominal 100 Hz
    :param noise_std_loss_bits: noise loss relative to signal variance
    :return: centered time in seconds, GT pulse with white noise
    """
    pseudo_period_s = 1/frequency_peak_hz
    duration_points = int(duration_s*sample_rate_hz)
    time_center_s = np.arange(duration_points)/sample_rate_hz
    time_center_s -= time_center_s[-1]/2.
    sig_gt = gt_blast_period_center(time_center_s, pseudo_period_s)
    sig_noise = white_noise_fbits(sig_gt, noise_std_loss_bits)
    gt_white = sig_gt + sig_noise
    # AA filter
    gt_white_aa = antialias_halfNyquist(gt_white)
    return time_center_s, gt_white_aa


def gt_blast_center_noise_uneven(sensor_epoch_s: np.array,
                                 noise_std_loss_bits: float = 2,
                                 frequency_center_hz: Optional[float] = None) -> np.ndarray:
    """
    Construct the GT explosion pulse of Garces (2019) for even or uneven sensor time
    in Gaussion noise with SNR in bits re signal STD.
    This is a very flexible variation.

    :param sensor_epoch_s: array with timestamps for signal in epoch seconds
    :param noise_std_loss_bits: number of bits below signal standard deviation. Default is 2
    :param frequency_center_hz: center frequency in Hz. Optional
    :return: numpy array with anti-aliased GT explosion pulse with Gaussian noise
    """

    time_duration_s = sensor_epoch_s[-1]-sensor_epoch_s[0]

    if frequency_center_hz:
        pseudo_period_s = 1/frequency_center_hz
    else:
        pseudo_period_s = time_duration_s/4.

    # Convert to seconds
    time_center_s = sensor_epoch_s - sensor_epoch_s[0] - time_duration_s/2.
    sig_gt = gt_blast_period_center(time_center_s, pseudo_period_s)
    sig_noise = white_noise_fbits(np.copy(sig_gt), noise_std_loss_bits)
    gt_white = sig_gt + sig_noise
    # AA filter
    gt_white_aa = antialias_halfNyquist(gt_white)

    return gt_white_aa


# Integrals and derivatives, with delta function estimate and discontinuity boundary conditions
def gt_blast_derivative_period_center(time_center_s: np.ndarray,
                                      pseudo_period_s: float) -> np.ndarray:
    """
    Derivative of the GT blast with delta function approximation

    :param time_center_s: array with time
    :param pseudo_period_s: period in seconds
    :return: numpy ndarray with derivative of the GT blast with delta function approximation
    """
    # Garces (2019) ground truth GT blast pulse
    # with the +1, tau is the zero crossing time - time_start renamed to time_zero for first zero crossing.
    time_pos_s = pseudo_period_s/4.
    tau = time_center_s/time_pos_s + 1.
    # Initialize GT
    p_GTd = np.zeros(tau.size)  # Granstrom-Triangular (GT), 2019
    # Initialize time ranges
    sigint1 = np.where((0.0 <= tau) & (tau <= 1.))  # ONLY positive pulse
    sigintG17 = np.where((1. < tau) & (tau <= 1 + np.sqrt(6.)))  # GT balanced pulse
    p_GTd[sigint1] = -1.
    p_GTd[sigintG17] = -1./6. * (3. + np.sqrt(6) - 3*tau[sigintG17]) * (1. + np.sqrt(6) - tau[sigintG17])

    return p_GTd


def gt_blast_integral_period_center(time_center_s: np.ndarray,
                                    pseudo_period_s: float) -> np.ndarray:
    """
    Integral of the GT blast with initial condition at zero crossing

    :param time_center_s: array with time
    :param pseudo_period_s: period in seconds
    :return: numpy ndarray with integral of GT blast pulse
    """
    # Garces (2019) ground truth GT blast pulse
    # with the +1, tau is the zero crossing time - time_start renamed to time_zero for first zero crossing.
    time_pos_s = pseudo_period_s/4.
    tau = time_center_s/time_pos_s + 1.
    # Initialize GT
    p_GTi = np.zeros(tau.size)  # Granstrom-Triangular (GT), 2019
    # Initialize time ranges
    sigint1 = np.where((0.0 <= tau) & (tau <= 1.))  # ONLY positive pulse
    sigintG17 = np.where((1. < tau) & (tau <= 1 + np.sqrt(6.)))  # GT balanced pulse
    p_GTi[sigint1] = (1. - tau[sigint1]/2.)*tau[sigint1]

    p_GTi[sigintG17] = -tau[sigintG17]/72. * (
            3 * tau[sigintG17]**3 - 4 * (3 + 2 * np.sqrt(6)) * tau[sigintG17]**2 +
            6 * (9 + 4 * np.sqrt(6)) * tau[sigintG17] - 12 * (7 + 2 * np.sqrt(6)))

    integration_constant = p_GTi[sigint1][-1] - p_GTi[sigintG17][0]
    p_GTi[sigintG17] += integration_constant

    return p_GTi


def gt_blast_center_integral_and_derivative(frequency_peak_hz,
                                            sample_rate_hz: float) -> Tuple[float, np.ndarray, np.ndarray, np.ndarray]:
    """
    Integral and derivative of GT pulse relative to tau (NOT time_s)

    :param frequency_peak_hz: peak frequency in Hz
    :param sample_rate_hz: sample rate in Hz
    :return: tau center, numpy ndarray with GT blast pulse, numpy ndarray with integral of GT blast pulse, numpy array
        with derivative of GT blast pulse
    """

    duration_s = 2/frequency_peak_hz       # 16 cycles for 6th octave (M = 14)
    pseudo_period_s = 1/frequency_peak_hz
    time_pos_s = pseudo_period_s/4.
    duration_points = int(duration_s*sample_rate_hz)
    time_center_s = np.arange(duration_points)/sample_rate_hz
    time_center_s -= time_center_s[-1]/2.
    tau_center = time_center_s/time_pos_s
    tau_interval = np.mean(np.diff(tau_center))

    sig_gt = gt_blast_period_center(time_center_s, pseudo_period_s)
    sig_gt_i = gt_blast_integral_period_center(time_center_s, pseudo_period_s)
    sig_gt_d = gt_blast_derivative_period_center(time_center_s, pseudo_period_s)
    sig_gt_d[np.argmax(sig_gt)-1] = np.max(np.diff(sig_gt))/tau_interval

    return tau_center, sig_gt, sig_gt_i, sig_gt_d


def gt_blast_ft(frequency_peak_hz: float,
                frequency_hz: Union[float, np.ndarray]) -> Union[float, complex, np.ndarray]:
    """
    Fourier transform of the GT blast pulse

    :param frequency_peak_hz: peak frequency in Hz
    :param frequency_hz: frequency in Hz, float or np.ndarray
    :return: Fourier transform of the GT blast pulse
    """
    w_scaled = 0.5*np.pi*frequency_hz/frequency_peak_hz
    ft_G17_positive = (1. - 1j*w_scaled - np.exp(-1j*w_scaled))/w_scaled**2.
    ft_G17_negative = np.exp(-1j*w_scaled*(1+np.sqrt(6.)))/(3.*w_scaled**4.) * \
                      (1j*w_scaled*np.sqrt(6.) + 3. +
                       np.exp(1j*w_scaled*np.sqrt(6.))*(3.*w_scaled**2. + 1j*w_scaled*2.*np.sqrt(6.)-3.))
    ft_G17 = (ft_G17_positive + ft_G17_negative)*np.pi/(2*np.pi*frequency_peak_hz)
    return ft_G17


def gt_blast_spectral_density(frequency_peak_hz: float,
                              frequency_hz: Union[float, np.ndarray]) -> Tuple[Union[float, np.ndarray], float]:
    """
    Spectral density of the GT blast pulse

    :param frequency_peak_hz: peak frequency in Hz
    :param frequency_hz: frequency in Hz, float or np.ndarray
    :return: spectral_density, spectral_density_peak
    """
    fourier_tx = gt_blast_ft(frequency_peak_hz, frequency_hz)
    spectral_density = 2*np.abs(fourier_tx*np.conj(fourier_tx))
    spectral_density_peak = np.max(spectral_density)
    return spectral_density, spectral_density_peak

Functions

def gt_blast_center_fast(frequency_peak_hz: float = 6.3, sample_rate_hz: float = 100.0, noise_std_loss_bits: float = 16) ‑> Tuple[numpy.ndarray, numpy.ndarray]

Fast computation of GT pulse with noise

:param frequency_peak_hz: peak frequency, nominal 6.3 Hz for 1 tonne TNT :param sample_rate_hz: sample rate, nominal 100 Hz :param noise_std_loss_bits: noise loss relative to signal variance :return: centered time in seconds, GT pulse with white noise

Expand source code
def gt_blast_center_fast(frequency_peak_hz: float = 6.3,
                         sample_rate_hz: float = 100.,
                         noise_std_loss_bits: float = 16) -> Tuple[np.ndarray, np.ndarray]:
    """
    Fast computation of GT pulse with noise

    :param frequency_peak_hz: peak frequency, nominal 6.3 Hz for 1 tonne TNT
    :param sample_rate_hz: sample rate, nominal 100 Hz
    :param noise_std_loss_bits: noise loss relative to signal variance
    :return: centered time in seconds, GT pulse with white noise
    """

    duration_s = 16/frequency_peak_hz       # 16 cycles for 6th octave (M = 14)
    pseudo_period_s = 1/frequency_peak_hz
    duration_points = int(duration_s*sample_rate_hz)
    time_center_s = np.arange(duration_points)/sample_rate_hz
    time_center_s -= time_center_s[-1]/2.
    sig_gt = gt_blast_period_center(time_center_s, pseudo_period_s)
    sig_noise = white_noise_fbits(sig_gt, noise_std_loss_bits)
    gt_white = sig_gt + sig_noise
    # AA filter
    gt_white_aa = antialias_halfNyquist(gt_white)
    return time_center_s, gt_white_aa
def gt_blast_center_integral_and_derivative(frequency_peak_hz, sample_rate_hz: float) ‑> Tuple[float, numpy.ndarray, numpy.ndarray, numpy.ndarray]

Integral and derivative of GT pulse relative to tau (NOT time_s)

:param frequency_peak_hz: peak frequency in Hz :param sample_rate_hz: sample rate in Hz :return: tau center, numpy ndarray with GT blast pulse, numpy ndarray with integral of GT blast pulse, numpy array with derivative of GT blast pulse

Expand source code
def gt_blast_center_integral_and_derivative(frequency_peak_hz,
                                            sample_rate_hz: float) -> Tuple[float, np.ndarray, np.ndarray, np.ndarray]:
    """
    Integral and derivative of GT pulse relative to tau (NOT time_s)

    :param frequency_peak_hz: peak frequency in Hz
    :param sample_rate_hz: sample rate in Hz
    :return: tau center, numpy ndarray with GT blast pulse, numpy ndarray with integral of GT blast pulse, numpy array
        with derivative of GT blast pulse
    """

    duration_s = 2/frequency_peak_hz       # 16 cycles for 6th octave (M = 14)
    pseudo_period_s = 1/frequency_peak_hz
    time_pos_s = pseudo_period_s/4.
    duration_points = int(duration_s*sample_rate_hz)
    time_center_s = np.arange(duration_points)/sample_rate_hz
    time_center_s -= time_center_s[-1]/2.
    tau_center = time_center_s/time_pos_s
    tau_interval = np.mean(np.diff(tau_center))

    sig_gt = gt_blast_period_center(time_center_s, pseudo_period_s)
    sig_gt_i = gt_blast_integral_period_center(time_center_s, pseudo_period_s)
    sig_gt_d = gt_blast_derivative_period_center(time_center_s, pseudo_period_s)
    sig_gt_d[np.argmax(sig_gt)-1] = np.max(np.diff(sig_gt))/tau_interval

    return tau_center, sig_gt, sig_gt_i, sig_gt_d
def gt_blast_center_noise(duration_s: float = 16, frequency_peak_hz: float = 6.3, sample_rate_hz: float = 100, noise_std_loss_bits: float = 16) ‑> Tuple[numpy.ndarray, numpy.ndarray]

Fast computation of GT pulse with noise for a specified duration in seconds

:param duration_s: signal duration in seconds :param frequency_peak_hz: peak frequency, nominal 6.3 Hz for 1 tonne TNT :param sample_rate_hz: sample rate, nominal 100 Hz :param noise_std_loss_bits: noise loss relative to signal variance :return: centered time in seconds, GT pulse with white noise

Expand source code
def gt_blast_center_noise(duration_s: float = 16,
                          frequency_peak_hz: float = 6.3,
                          sample_rate_hz: float = 100,
                          noise_std_loss_bits: float = 16) -> Tuple[np.ndarray, np.ndarray]:
    """
    Fast computation of GT pulse with noise for a specified duration in seconds

    :param duration_s: signal duration in seconds
    :param frequency_peak_hz: peak frequency, nominal 6.3 Hz for 1 tonne TNT
    :param sample_rate_hz: sample rate, nominal 100 Hz
    :param noise_std_loss_bits: noise loss relative to signal variance
    :return: centered time in seconds, GT pulse with white noise
    """
    pseudo_period_s = 1/frequency_peak_hz
    duration_points = int(duration_s*sample_rate_hz)
    time_center_s = np.arange(duration_points)/sample_rate_hz
    time_center_s -= time_center_s[-1]/2.
    sig_gt = gt_blast_period_center(time_center_s, pseudo_period_s)
    sig_noise = white_noise_fbits(sig_gt, noise_std_loss_bits)
    gt_white = sig_gt + sig_noise
    # AA filter
    gt_white_aa = antialias_halfNyquist(gt_white)
    return time_center_s, gt_white_aa
def gt_blast_center_noise_uneven(sensor_epoch_s: , noise_std_loss_bits: float = 2, frequency_center_hz: Union[float, NoneType] = None) ‑> numpy.ndarray

Construct the GT explosion pulse of Garces (2019) for even or uneven sensor time in Gaussion noise with SNR in bits re signal STD. This is a very flexible variation.

:param sensor_epoch_s: array with timestamps for signal in epoch seconds :param noise_std_loss_bits: number of bits below signal standard deviation. Default is 2 :param frequency_center_hz: center frequency in Hz. Optional :return: numpy array with anti-aliased GT explosion pulse with Gaussian noise

Expand source code
def gt_blast_center_noise_uneven(sensor_epoch_s: np.array,
                                 noise_std_loss_bits: float = 2,
                                 frequency_center_hz: Optional[float] = None) -> np.ndarray:
    """
    Construct the GT explosion pulse of Garces (2019) for even or uneven sensor time
    in Gaussion noise with SNR in bits re signal STD.
    This is a very flexible variation.

    :param sensor_epoch_s: array with timestamps for signal in epoch seconds
    :param noise_std_loss_bits: number of bits below signal standard deviation. Default is 2
    :param frequency_center_hz: center frequency in Hz. Optional
    :return: numpy array with anti-aliased GT explosion pulse with Gaussian noise
    """

    time_duration_s = sensor_epoch_s[-1]-sensor_epoch_s[0]

    if frequency_center_hz:
        pseudo_period_s = 1/frequency_center_hz
    else:
        pseudo_period_s = time_duration_s/4.

    # Convert to seconds
    time_center_s = sensor_epoch_s - sensor_epoch_s[0] - time_duration_s/2.
    sig_gt = gt_blast_period_center(time_center_s, pseudo_period_s)
    sig_noise = white_noise_fbits(np.copy(sig_gt), noise_std_loss_bits)
    gt_white = sig_gt + sig_noise
    # AA filter
    gt_white_aa = antialias_halfNyquist(gt_white)

    return gt_white_aa
def gt_blast_derivative_period_center(time_center_s: numpy.ndarray, pseudo_period_s: float) ‑> numpy.ndarray

Derivative of the GT blast with delta function approximation

:param time_center_s: array with time :param pseudo_period_s: period in seconds :return: numpy ndarray with derivative of the GT blast with delta function approximation

Expand source code
def gt_blast_derivative_period_center(time_center_s: np.ndarray,
                                      pseudo_period_s: float) -> np.ndarray:
    """
    Derivative of the GT blast with delta function approximation

    :param time_center_s: array with time
    :param pseudo_period_s: period in seconds
    :return: numpy ndarray with derivative of the GT blast with delta function approximation
    """
    # Garces (2019) ground truth GT blast pulse
    # with the +1, tau is the zero crossing time - time_start renamed to time_zero for first zero crossing.
    time_pos_s = pseudo_period_s/4.
    tau = time_center_s/time_pos_s + 1.
    # Initialize GT
    p_GTd = np.zeros(tau.size)  # Granstrom-Triangular (GT), 2019
    # Initialize time ranges
    sigint1 = np.where((0.0 <= tau) & (tau <= 1.))  # ONLY positive pulse
    sigintG17 = np.where((1. < tau) & (tau <= 1 + np.sqrt(6.)))  # GT balanced pulse
    p_GTd[sigint1] = -1.
    p_GTd[sigintG17] = -1./6. * (3. + np.sqrt(6) - 3*tau[sigintG17]) * (1. + np.sqrt(6) - tau[sigintG17])

    return p_GTd
def gt_blast_ft(frequency_peak_hz: float, frequency_hz: Union[float, numpy.ndarray]) ‑> Union[float, complex, numpy.ndarray]

Fourier transform of the GT blast pulse

:param frequency_peak_hz: peak frequency in Hz :param frequency_hz: frequency in Hz, float or np.ndarray :return: Fourier transform of the GT blast pulse

Expand source code
def gt_blast_ft(frequency_peak_hz: float,
                frequency_hz: Union[float, np.ndarray]) -> Union[float, complex, np.ndarray]:
    """
    Fourier transform of the GT blast pulse

    :param frequency_peak_hz: peak frequency in Hz
    :param frequency_hz: frequency in Hz, float or np.ndarray
    :return: Fourier transform of the GT blast pulse
    """
    w_scaled = 0.5*np.pi*frequency_hz/frequency_peak_hz
    ft_G17_positive = (1. - 1j*w_scaled - np.exp(-1j*w_scaled))/w_scaled**2.
    ft_G17_negative = np.exp(-1j*w_scaled*(1+np.sqrt(6.)))/(3.*w_scaled**4.) * \
                      (1j*w_scaled*np.sqrt(6.) + 3. +
                       np.exp(1j*w_scaled*np.sqrt(6.))*(3.*w_scaled**2. + 1j*w_scaled*2.*np.sqrt(6.)-3.))
    ft_G17 = (ft_G17_positive + ft_G17_negative)*np.pi/(2*np.pi*frequency_peak_hz)
    return ft_G17
def gt_blast_integral_period_center(time_center_s: numpy.ndarray, pseudo_period_s: float) ‑> numpy.ndarray

Integral of the GT blast with initial condition at zero crossing

:param time_center_s: array with time :param pseudo_period_s: period in seconds :return: numpy ndarray with integral of GT blast pulse

Expand source code
def gt_blast_integral_period_center(time_center_s: np.ndarray,
                                    pseudo_period_s: float) -> np.ndarray:
    """
    Integral of the GT blast with initial condition at zero crossing

    :param time_center_s: array with time
    :param pseudo_period_s: period in seconds
    :return: numpy ndarray with integral of GT blast pulse
    """
    # Garces (2019) ground truth GT blast pulse
    # with the +1, tau is the zero crossing time - time_start renamed to time_zero for first zero crossing.
    time_pos_s = pseudo_period_s/4.
    tau = time_center_s/time_pos_s + 1.
    # Initialize GT
    p_GTi = np.zeros(tau.size)  # Granstrom-Triangular (GT), 2019
    # Initialize time ranges
    sigint1 = np.where((0.0 <= tau) & (tau <= 1.))  # ONLY positive pulse
    sigintG17 = np.where((1. < tau) & (tau <= 1 + np.sqrt(6.)))  # GT balanced pulse
    p_GTi[sigint1] = (1. - tau[sigint1]/2.)*tau[sigint1]

    p_GTi[sigintG17] = -tau[sigintG17]/72. * (
            3 * tau[sigintG17]**3 - 4 * (3 + 2 * np.sqrt(6)) * tau[sigintG17]**2 +
            6 * (9 + 4 * np.sqrt(6)) * tau[sigintG17] - 12 * (7 + 2 * np.sqrt(6)))

    integration_constant = p_GTi[sigint1][-1] - p_GTi[sigintG17][0]
    p_GTi[sigintG17] += integration_constant

    return p_GTi
def gt_blast_period_center(time_center_s: numpy.ndarray, pseudo_period_s: float) ‑> numpy.ndarray

GT blast pulse

:param time_center_s: array with time :param pseudo_period_s: period in seconds :return: numpy array with GT blast pulse

Expand source code
def gt_blast_period_center(time_center_s: np.ndarray,
                           pseudo_period_s: float) -> np.ndarray:
    """
    GT blast pulse

    :param time_center_s: array with time
    :param pseudo_period_s: period in seconds
    :return: numpy array with GT blast pulse
    """
    # With the +1, tau is the zero crossing time - time_start renamed to time_zero for first zero crossing.
    time_pos_s = pseudo_period_s/4.
    tau = time_center_s/time_pos_s + 1.
    # Initialize GT
    p_GT = np.zeros(tau.size)  # Granstrom-Triangular (GT), 2019
    # Initialize time ranges
    sigint1 = np.where((0.0 <= tau) & (tau <= 1.))  # ONLY positive pulse
    sigintG17 = np.where((1. < tau) & (tau <= 1 + np.sqrt(6.)))  # GT balanced pulse
    p_GT[sigint1] = (1. - tau[sigint1])
    p_GT[sigintG17] = 1./6. * (1. - tau[sigintG17]) * (1. + np.sqrt(6) - tau[sigintG17]) ** 2.

    return p_GT
def gt_blast_spectral_density(frequency_peak_hz: float, frequency_hz: Union[float, numpy.ndarray]) ‑> Tuple[Union[float, numpy.ndarray], float]

Spectral density of the GT blast pulse

:param frequency_peak_hz: peak frequency in Hz :param frequency_hz: frequency in Hz, float or np.ndarray :return: spectral_density, spectral_density_peak

Expand source code
def gt_blast_spectral_density(frequency_peak_hz: float,
                              frequency_hz: Union[float, np.ndarray]) -> Tuple[Union[float, np.ndarray], float]:
    """
    Spectral density of the GT blast pulse

    :param frequency_peak_hz: peak frequency in Hz
    :param frequency_hz: frequency in Hz, float or np.ndarray
    :return: spectral_density, spectral_density_peak
    """
    fourier_tx = gt_blast_ft(frequency_peak_hz, frequency_hz)
    spectral_density = 2*np.abs(fourier_tx*np.conj(fourier_tx))
    spectral_density_peak = np.max(spectral_density)
    return spectral_density, spectral_density_peak
def gt_hilbert_blast_period_center(time_center_s: numpy.ndarray, pseudo_period_s: float) ‑> numpy.ndarray

Hilbert transform of the GT blast pulse

:param time_center_s: array with time :param pseudo_period_s: period in seconds :return: numpy array with Hilbert transform of the GT blast pulse

Expand source code
def gt_hilbert_blast_period_center(time_center_s: np.ndarray,
                                   pseudo_period_s: float) -> np.ndarray:
    """
    Hilbert transform of the GT blast pulse

    :param time_center_s: array with time
    :param pseudo_period_s: period in seconds
    :return: numpy array with Hilbert transform of the GT blast pulse
    """
    # With the +1, tau is the zero crossing time - time_start renamed to time_zero for first zero crossing.
    time_pos_s = pseudo_period_s/4.
    tau = time_center_s/time_pos_s + 1.
    a = 1 + np.sqrt(6)
    # Initialize GT
    p_GT_H = np.zeros(tau.size)  # Hilbert of Granstrom-Triangular (GT), 2019
    # Initialize time ranges
    sigint1 = np.where((0.0 <= tau) & (tau <= 1.))  # ONLY positive pulse
    sigint2 = np.where((1. < tau) & (tau <= 1 + np.sqrt(6.)))  # GT balanced pulse
    tau1 = tau[sigint1]
    tau2 = tau[sigint2]

    p_GT_H[sigint1] = 1. + (1-tau1)*np.log(tau1+EPSILON) - (1-tau1)*np.log(1-tau1+EPSILON)
    p_GT_H21 = (a-1)/6. * (a*(2*a+5) - 1 + 6*tau2**2 - 3*tau2*(1+3*a))
    p_GT_H22 = (tau2-1)*(a-tau2)**2 * (np.log(a-tau2+EPSILON) - np.log(tau2-1+EPSILON))
    p_GT_H[sigint2] = 1./6. * (p_GT_H21 + p_GT_H22)
    p_GT_H /= np.pi

    return p_GT_H