示例#1
0
def cfrequency(data, fs, smoothie, fk):
    """
    Central frequency of a signal.

    Computes the central frequency of the given data which can be windowed or
    not. The central frequency is a measure of the frequency where the
    power is concentrated. It corresponds to the second moment of the power
    spectral density function.

    The central frequency is returned.

    :type data: :class:`~numpy.ndarray`
    :param data: Data to estimate central frequency from.
    :param fs: Sampling frequency in Hz.
    :param smoothie: Factor for smoothing the result.
    :param fk: Coefficients for calculating time derivatives
        (calculated via central difference).
    :return: **cfreq[, dcfreq]** - Central frequency, Time derivative of center
        frequency (windowed only).
    """
    nfft = util.nextpow2(data.shape[1])
    freq = np.linspace(0, fs, nfft + 1)
    freqaxis = freq[0:nfft / 2]
    cfreq = np.zeros(data.shape[0])
    if np.size(data.shape) > 1:
        i = 0
        for row in data:
            Px_wm = welch(row, np.hamming(len(row)), util.nextpow2(len(row)))
            Px = Px_wm[0:len(Px_wm) / 2]
            cfreq[i] = np.sqrt(np.sum(freqaxis ** 2 * Px) / (sum(Px)))
            i = i + 1
        cfreq = util.smooth(cfreq, smoothie)
        #cfreq_add = \
        #        np.append(np.append([cfreq[0]] * (np.size(fk) // 2), cfreq),
        #        [cfreq[np.size(cfreq) - 1]] * (np.size(fk) // 2))
        # faster alternative
        cfreq_add = np.hstack(
            ([cfreq[0]] * (np.size(fk) // 2), cfreq,
             [cfreq[np.size(cfreq) - 1]] * (np.size(fk) // 2)))
        dcfreq = signal.lfilter(fk, 1, cfreq_add)
        #dcfreq = dcfreq[np.size(fk) // 2:(np.size(dcfreq) - np.size(fk) // 2)]
        # correct start and end values of time derivative
        dcfreq = dcfreq[np.size(fk) - 1:np.size(dcfreq)]
        return cfreq, dcfreq
    else:
        Px_wm = welch(data, np.hamming(len(data)), util.nextpow2(len(data)))
        Px = Px_wm[0:len(Px_wm) / 2]
        cfreq = np.sqrt(np.sum(freqaxis ** 2 * Px) / (sum(Px)))
        return cfreq
示例#2
0
def cfrequency(data, fs, smoothie, fk):
    """
    Central frequency of a signal.

    Computes the central frequency of the given data which can be windowed or
    not. The central frequency is a measure of the frequency where the
    power is concentrated. It corresponds to the second moment of the power
    spectral density function.

    The central frequency is returned.

    :type data: :class:`~numpy.ndarray`
    :param data: Data to estimate central frequency from.
    :param fs: Sampling frequency in Hz.
    :param smoothie: Factor for smoothing the result.
    :param fk: Coefficients for calculating time derivatives
        (calculated via central difference).
    :return: **cfreq[, dcfreq]** - Central frequency, Time derivative of center
        frequency (windowed only).
    """
    nfft = util.nextpow2(data.shape[1])
    freq = np.linspace(0, fs, nfft + 1)
    freqaxis = freq[0:nfft / 2]
    cfreq = np.zeros(data.shape[0])
    if np.size(data.shape) > 1:
        i = 0
        for row in data:
            Px_wm = welch(row, np.hamming(len(row)), util.nextpow2(len(row)))
            Px = Px_wm[0:len(Px_wm) / 2]
            cfreq[i] = np.sqrt(np.sum(freqaxis**2 * Px) / (sum(Px)))
            i = i + 1
        cfreq = util.smooth(cfreq, smoothie)
        #cfreq_add = \
        #        np.append(np.append([cfreq[0]] * (np.size(fk) // 2), cfreq),
        #        [cfreq[np.size(cfreq) - 1]] * (np.size(fk) // 2))
        # faster alternative
        cfreq_add = np.hstack(
            ([cfreq[0]] * (np.size(fk) // 2), cfreq,
             [cfreq[np.size(cfreq) - 1]] * (np.size(fk) // 2)))
        dcfreq = signal.lfilter(fk, 1, cfreq_add)
        #dcfreq = dcfreq[np.size(fk) // 2:(np.size(dcfreq) - np.size(fk) // 2)]
        # correct start and end values of time derivative
        dcfreq = dcfreq[np.size(fk) - 1:np.size(dcfreq)]
        return cfreq, dcfreq
    else:
        Px_wm = welch(data, np.hamming(len(data)), util.nextpow2(len(data)))
        Px = Px_wm[0:len(Px_wm) / 2]
        cfreq = np.sqrt(np.sum(freqaxis**2 * Px) / (sum(Px)))
        return cfreq
示例#3
0
def convolution_acyclic(x, h, N=None):
    '''
        The acyclic convolution of Nx samples with Nh samples produces at most Nx + Nh - 1 nonzero samples

        To embed acyclic convolution within a cyclic convolution (ie fft), zero-paad both operands out to length N,
        where N >= Nx + Nh - 1

        If we don't add enough zeros, some of the convolution terms "wrap around" and add back upon other (due to module indexing).
        This can be called time-domain aliasing!
        Zero-paadding in the time  domain results in more samples (closer spacing over the unit circle) in the frequency domain, ie.,
        a higher sampling  rate in the frequency domain.
    '''

    nx = len(x)
    nh = len(h)
    nfft = N if N is not None else util.nextpow2(nx + nh - 1)
    # zero pad signal and filter
    xzp = np.concatenate((x, np.zeros(nfft - nx)))
    hzp = np.concatenate((h, np.zeros(nfft - nh)))
    X = np.fft.fft(xzp)
    H = np.fft.fft(hzp)

    Y = X * H

    # get zero padded acyclic convolution
    y = np.real(np.fft.ifft(Y))
    # trim zeros
    yt = y[:nx + nh - 1]

    return yt
示例#4
0
def envelope(data):
    """
    Envelope of a signal.

    Computes the envelope of the given data which can be windowed or
    not. The envelope is determined by the absolute value of the analytic
    signal of the given data.

    If data are windowed the analytic signal and the envelope of each
    window is returned.

    :type data: :class:`~numpy.ndarray`
    :param data: Data to make envelope of.
    :return: **A_cpx, A_abs** - Analytic signal of input data, Envelope of
        input data.
    """
    nfft = util.nextpow2(data.shape[size(data.shape) - 1])
    A_cpx = np.zeros((data.shape), dtype='complex64')
    A_abs = np.zeros((data.shape), dtype='float64')
    if (np.size(data.shape) > 1):
        i = 0
        for row in data:
            A_cpx[i, :] = signal.hilbert(row, nfft)
            A_abs[i, :] = abs(signal.hilbert(row, nfft))
            i = i + 1
    else:
        A_cpx = signal.hilbert(data, nfft)
        A_abs = abs(signal.hilbert(data, nfft))
    return A_cpx, A_abs
示例#5
0
def example_lp_fft():
    '''low-pass filterr by FFT convolution'''
    # signal is a sum of sinusoidal components
    f = [440, 880, 1000, 2000]
    # signal length
    M = 256
    Fs = 5000

    x = np.zeros(M)
    n = np.arange(M)

    for fk in f:
        x += np.sin(2 * np.pi * n * fk / Fs)

    plt.figure()
    # input signal amplitude response
    plt.plot(np.fft.fftshift(np.abs(np.fft.fft(x, 1024))))

    # filter params
    L = 257
    fc = 600

    # using the window method:
    Lo2 = (L - 1) // 2
    hsupp = np.linspace(-Lo2, Lo2, num=L)
    hideal = 2 * fc / Fs * np.sinc(2 * fc * hsupp / Fs)
    _, hamm = win.hamming(L)
    h = hamm * hideal
    # filter impulse response
    plt.figure()
    plt.plot(h)
    #plt.figure()
    # filter magnitude response in dB
    #plt.plot(20 * np.log10(np.fft.fftshift(np.abs(np.fft.fft(h, 1024)))))

    # apply filtering
    Nfft = util.nextpow2(L + M - 1)
    print(f'Nfft={Nfft}')
    # zero pad
    xzp = np.concatenate((x, np.zeros(Nfft - M)))
    hzp = np.concatenate((h, np.zeros(Nfft - L)))

    X = np.fft.fft(xzp)
    H = np.fft.fft(hzp)

    Y = X * H

    y = np.fft.ifft(Y)
    #relrmserr = np.norm()
    #print(relrms)

    y = np.real(y)
    plt.figure()
    plt.plot(y)
    plt.show()
示例#6
0
def bwith(data, fs, smoothie, fk):
    """
    Bandwidth of a signal.

    Computes the bandwidth of the given data which can be windowed or not.
    The bandwidth corresponds to the level where the power of the spectrum is
    half its maximum value. It is determined as the level of 1/sqrt(2) times
    the maximum Fourier amplitude.

    If data are windowed the bandwidth of each window is returned.

    :type data: :class:`~numpy.ndarray`
    :param data: Data to make envelope of.
    :param fs: Sampling frequency in Hz.
    :param smoothie: Factor for smoothing the result.
    :param fk: Coefficients for calculating time derivatives
        (calculated via central difference).
    :return: **bwith[, dbwithd]** - Bandwidth, Time derivative of predominant
        period (windowed only).
    """
    nfft = util.nextpow2(data.shape[1])
    freqaxis = np.linspace(0, fs, nfft + 1)
    bwith = np.zeros(data.shape[0])
    f = fftpack.fft(data, nfft)
    f_sm = util.smooth(abs(f[:, 0:nfft / 2]), 10)
    if np.size(data.shape) > 1:
        i = 0
        for row in f_sm:
            minfc = abs(row - max(abs(row * (1 / np.sqrt(2)))))
            [mdist_ind, _mindist] = min(enumerate(minfc), key=itemgetter(1))
            bwith[i] = freqaxis[mdist_ind]
            i = i + 1
        #bwith_add = \
        #        np.append(np.append([bwith[0]] * (np.size(fk) // 2), bwith),
        #        [bwith[np.size(bwith) - 1]] * (np.size(fk) // 2))
        # faster alternative
        bwith_add = np.hstack(
            ([bwith[0]] * (np.size(fk) // 2), bwith,
             [bwith[np.size(bwith) - 1]] * (np.size(fk) // 2)))
        dbwith = signal.lfilter(fk, 1, bwith_add)
        #dbwith = dbwith[np.size(fk) // 2:(np.size(dbwith) - np.size(fk) // 2)]
        # correct start and end values of time derivative
        dbwith = dbwith[np.size(fk) - 1:]
        bwith = util.smooth(bwith, smoothie)
        dbwith = util.smooth(dbwith, smoothie)
        return bwith, dbwith
    else:
        minfc = abs(data - max(abs(data * (1 / np.sqrt(2)))))
        [mdist_ind, _mindist] = min(enumerate(minfc), key=itemgetter(1))
        bwith = freqaxis[mdist_ind]
        return bwith
示例#7
0
def bwith(data, fs, smoothie, fk):
    """
    Bandwidth of a signal.

    Computes the bandwidth of the given data which can be windowed or not.
    The bandwidth corresponds to the level where the power of the spectrum is
    half its maximum value. It is determined as the level of 1/sqrt(2) times
    the maximum Fourier amplitude.

    If data are windowed the bandwidth of each window is returned.

    :type data: :class:`~numpy.ndarray`
    :param data: Data to make envelope of.
    :param fs: Sampling frequency in Hz.
    :param smoothie: Factor for smoothing the result.
    :param fk: Coefficients for calculating time derivatives
        (calculated via central difference).
    :return: **bwith[, dbwithd]** - Bandwidth, Time derivative of predominant
        period (windowed only).
    """
    nfft = util.nextpow2(data.shape[1])
    freqaxis = np.linspace(0, fs, nfft + 1)
    bwith = np.zeros(data.shape[0])
    f = fftpack.fft(data, nfft)
    f_sm = util.smooth(abs(f[:, 0:nfft / 2]), 10)
    if np.size(data.shape) > 1:
        i = 0
        for row in f_sm:
            minfc = abs(row - max(abs(row * (1 / np.sqrt(2)))))
            [mdist_ind, _mindist] = min(enumerate(minfc), key=itemgetter(1))
            bwith[i] = freqaxis[mdist_ind]
            i = i + 1
        #bwith_add = \
        #        np.append(np.append([bwith[0]] * (np.size(fk) // 2), bwith),
        #        [bwith[np.size(bwith) - 1]] * (np.size(fk) // 2))
        # faster alternative
        bwith_add = np.hstack(
            ([bwith[0]] * (np.size(fk) // 2), bwith,
             [bwith[np.size(bwith) - 1]] * (np.size(fk) // 2)))
        dbwith = signal.lfilter(fk, 1, bwith_add)
        #dbwith = dbwith[np.size(fk) // 2:(np.size(dbwith) - np.size(fk) // 2)]
        # correct start and end values of time derivative
        dbwith = dbwith[np.size(fk) - 1:]
        bwith = util.smooth(bwith, smoothie)
        dbwith = util.smooth(dbwith, smoothie)
        return bwith, dbwith
    else:
        minfc = abs(data - max(abs(data * (1 / np.sqrt(2)))))
        [mdist_ind, _mindist] = min(enumerate(minfc), key=itemgetter(1))
        bwith = freqaxis[mdist_ind]
        return bwith
示例#8
0
def spectral_envelope_cepstrum(sig, f0, fs=1):
    # spectral envelope by the Cepstral Windowing Method
    # compute log magnitude spectrum
    # inverse FFT to obbtain the real cepstrum
    # lowpass-window the cepstrum
    # perform fFT to obbtain the smoothed log-magnitude spectrum

    # 40ms analysis window
    Nframe = util.nextpow2(fs / 25)
    _, w = windows.hamming(Nframe)
    winspeech = w * sig[:Nframe]
    Nfft = 4 * Nframe
    sspec = np.fft.fft(winspeech, Nfft)
    dbsspecfull = 20 * np.log10(np.abs(sspec))

    # real cepstrum
    rcep = np.fft.ifft(dbsspecfull)
    # eliminate round-off noise in imag part
    rcep = np.real(rcep)

    period = round(fs / f0)

    nspec = Nfft // 2 + 1

    # real cepstrum
    aliasing = np.linalg.norm(
        rcep[nspec - 10:nspec + 10]) / np.linalg.norm(rcep)
    print(f'aliasing = {aliasing}')

    # almost 1 period left and right
    nw = 2 * period - 4

    # make window count odd
    if np.floor(nw / 2) == nw / 2:
        nw -= 1

    _, w = windows.rectangle(nw)

    # make it zero phase
    wzp = np.concatenate(
        (w[(nw - 1) // 2:nw], np.zeros(Nfft - nw), w[:(nw - 1) // 2]))

    # lowpass filter (lifter) the cepstrum
    wrcep = wzp * rcep
    rcepenv = np.fft.fft(wrcep)
    # should be real
    rcepenvp = np.real(rcepenv[:nspec])
    rcepenvp = rcepenvp - np.mean(rcepenvp)

    return rcep, rcepenv
示例#9
0
def sonogram(data, fs, fc1, nofb, no_win):
    """
    Sonogram of a signal.

    Computes the sonogram of the given data which can be windowed or not.
    The sonogram is determined by the power in half octave bands of the given
    data.

    If data are windowed the analytic signal and the envelope of each window
    is returned.

    :type data: :class:`~numpy.ndarray`
    :param data: Data to make envelope of.
    :param fs: Sampling frequency in Hz.
    :param fc1: Center frequency of lowest half octave band.
    :param nofb: Number of half octave bands.
    :param no_win: Number of data windows.
    :return: Half octave bands.
    """
    fc = np.zeros([nofb])
    fmin = np.zeros([nofb])
    fmax = np.zeros([nofb])

    fc[0] = float(fc1)
    fmin[0] = fc[0] / np.sqrt(float(5. / 3.))
    fmax[0] = fc[0] * np.sqrt(float(5. / 3.))
    for i in range(1, nofb):
        fc[i] = fc[i - 1] * 1.5
        fmin[i] = fc[i] / np.sqrt(float(5. / 3.))
        fmax[i] = fc[i] * np.sqrt(float(5. / 3.))
    nfft = util.nextpow2(data.shape[np.size(data.shape) - 1])
    #c = np.zeros((data.shape), dtype='complex64')
    c = fftpack.fft(data, nfft)
    z = np.zeros([len(c[:, 1]), nofb])
    z_tot = np.zeros(len(c[:, 1]))
    hob = np.zeros([no_win, nofb])
    for k in xrange(no_win):
        for j in xrange(len(c[1, :])):
            z_tot[k] = z_tot[k] + pow(np.abs(c[k, j]), 2)
        for i in xrange(nofb):
            start = int(round(fmin[i] * nfft * 1. / float(fs), 0))
            end = int(round(fmax[i] * nfft * 1. / float(fs), 0)) + 1
            for j in xrange(start, end):
                z[k, i] = z[k, i] + pow(np.abs(c[k, j - 1]), 2)
            hob[k, i] = np.log(z[k, i] / z_tot[k])
    return hob
示例#10
0
def stft(signal, window, window_length, window_count, hop_size):
    '''
    Assuming - M is the analysis window legth, odd sized
             - N is a power of two larger than M.

        1. grab the data frame, time normalize about 0
        2. multiply time normalized frame from 1. by the spectrum analysis window to obtain the mth timee-normalized windowed data frame
        3. zero-pad the frame, 0's on each side for a size N, time-normalized dataset, until a factor of N/M zero-padding is aachieved
        4. take length N FFT to obtain the time-normalized frequency-sampled STFT at time m, with w_k = 2 * pi * k * fs / N, k is the bin number
        5. if needed, remove the time normalization via a linear phase term (phase = -mR, shift right by mR), this yields the sampled STFT

        NOTE: there is no irreversible time-aliasing wheen the STFT frequency axis w is sampled to the points w_k, provided thee FFT size N
        is greeater than or equal to the window length M.

        Since the STFT offers only one integration time (the window length), it implements a uniform baandpass filter bank, i.e., spectral samples
        are uniformly spaced and correspond to equal bandwidths.
    '''

    # assume odd
    M = window_length
    Mo2 = (M - 1) / 2
    # add Mo2 leading zeros to the signal so the last frame doesn't go out of range

    N = util.nextpow2(len(signal))

    # pre-allocate STFT output array
    Xtwz = np.zeros((N, window_count))

    padding = np.zeros(N - M)
    offset = 0
    for m in range(window_count):
        # grab the frame
        xt = signal[offset:offset + M]
        # apply the window
        xtw = window * xt
        # zero-pad
        xtwz = np.concatenate((xtw[Mo2:M], padding, xtw[:Mo2]))
        # fft and accumulate
        Xtwz[:, m] = np.fft.fft(xtwz)
        offset += hop_size

    return Xtwz
示例#11
0
def logcep(data, fs, nc, p, n, w):  # @UnusedVariable: n is never used!!!
    """
    Cepstrum of a signal.

    Computes the cepstral coefficient on a logarithmic scale of the given data
    which can be windowed or not.

    If data are windowed the analytic signal and the envelope of each window is
    returned.

    :type data: :class:`~numpy.ndarray`
    :param data: Data to make envelope of.
    :param fs: Sampling frequency in Hz.
    :param nc: number of cepstral coefficients.
    :param p: Number of filters in filterbank.
    :param n: Number of data windows.
    :return: Cepstral coefficients.
    """
    dataT = np.transpose(data)
    nfft = util.nextpow2(dataT.shape[0])
    fc = fftpack.fft(dataT, nfft, 0)
    f = fc[1:len(fc) / 2 + 1, :]
    m, a, b = logbankm(p, nfft, fs, w)
    pw = np.real(np.multiply(f[a:b, :], np.conj(f[a:b, :])))
    pth = np.max(pw) * 1E-20
    ath = np.sqrt(pth)
    #h1 = np.transpose(np.array([[ath] * int(b + 1 - a)]))
    #h2 = m * abs(f[a - 1:b, :])
    y = np.log(np.maximum(m * abs(f[a - 1:b, :]), ath))
    z = util.rdct(y)
    z = z[1:, :]
    #nc = nc + 1
    nf = np.size(z, 1)
    if (p > nc):
        z = z[:nc, :]
    elif (p < nc):
        z = np.vstack([z, np.zeros(nf, nc - p)])
    return z
示例#12
0
def logcep(data, fs, nc, p, n, w):  # @UnusedVariable: n is never used!!!
    """
    Cepstrum of a signal.

    Computes the cepstral coefficient on a logarithmic scale of the given data
    which can be windowed or not.

    If data are windowed the analytic signal and the envelope of each window is
    returned.

    :type data: :class:`~numpy.ndarray`
    :param data: Data to make envelope of.
    :param fs: Sampling frequency in Hz.
    :param nc: number of cepstral coefficients.
    :param p: Number of filters in filterbank.
    :param n: Number of data windows.
    :return: Cepstral coefficients.
    """
    dataT = np.transpose(data)
    nfft = util.nextpow2(dataT.shape[0])
    fc = fftpack.fft(dataT, nfft, 0)
    f = fc[1:len(fc) / 2 + 1, :]
    m, a, b = logbankm(p, nfft, fs, w)
    pw = np.real(np.multiply(f[a:b, :], np.conj(f[a:b, :])))
    pth = np.max(pw) * 1E-20
    ath = np.sqrt(pth)
    #h1 = np.transpose(np.array([[ath] * int(b + 1 - a)]))
    #h2 = m * abs(f[a - 1:b, :])
    y = np.log(np.maximum(m * abs(f[a - 1:b, :]), ath))
    z = util.rdct(y)
    z = z[1:, :]
    #nc = nc + 1
    nf = np.size(z, 1)
    if (p > nc):
        z = z[:nc, :]
    elif (p < nc):
        z = np.vstack([z, np.zeros(nf, nc - p)])
    return z
示例#13
0
def cfrequency_unwindowed(data, fs):
    """
    Central frequency of a signal.

    Computes the central frequency of the given data (a single waveform).
    The central frequency is a measure of the frequency where the
    power is concentrated. It corresponds to the second moment of the power
    spectral density function.

    The central frequency is returned in Hz.

    :type data: :class:`~numpy.array`
    :param data: Data to estimate central frequency from.
    :param fs: Sampling frequency in Hz.
    :return: **cfreq** - Central frequency in Hz
    """
    nfft = util.nextpow2(len(data))
    freq = np.linspace(0, fs, nfft + 1)
    freqaxis = freq[0:nfft / 2]
    Px_wm = welch(data, np.hamming(len(data)), nfft)
    Px = Px_wm[0:len(Px_wm) / 2]
    cfreq = np.sqrt(np.sum(freqaxis**2 * Px) / (sum(Px)))
    return cfreq
示例#14
0
def cfrequency_unwindowed(data, fs):
    """
    Central frequency of a signal.

    Computes the central frequency of the given data (a single waveform).
    The central frequency is a measure of the frequency where the
    power is concentrated. It corresponds to the second moment of the power
    spectral density function.

    The central frequency is returned in Hz.

    :type data: :class:`~numpy.array`
    :param data: Data to estimate central frequency from.
    :param fs: Sampling frequency in Hz.
    :return: **cfreq** - Central frequency in Hz
    """
    nfft = util.nextpow2(len(data))
    freq = np.linspace(0, fs, nfft + 1)
    freqaxis = freq[0:nfft / 2]
    Px_wm = welch(data, np.hamming(len(data)), nfft)
    Px = Px_wm[0:len(Px_wm) / 2]
    cfreq = np.sqrt(np.sum(freqaxis ** 2 * Px) / (sum(Px)))
    return cfreq
示例#15
0
文件: invsim.py 项目: jwfundsd/obspy
def seisSim(
    data,
    samp_rate,
    paz_remove=None,
    paz_simulate=None,
    remove_sensitivity=True,
    simulate_sensitivity=True,
    water_level=600.0,
    zero_mean=True,
    taper=True,
    taper_fraction=0.05,
    pre_filt=None,
    seedresp=None,
    nfft_pow2=False,
    pitsasim=True,
    sacsim=False,
    shsim=False,
    **_kwargs
):
    """
    Simulate/Correct seismometer.

    :type data: NumPy ndarray
    :param data: Seismogram, detrend before hand (e.g. zero mean)
    :type samp_rate: Float
    :param samp_rate: Sample Rate of Seismogram
    :type paz_remove: Dictionary, None
    :param paz_remove: Dictionary containing keys 'poles', 'zeros', 'gain'
        (A0 normalization factor). poles and zeros must be a list of complex
        floating point numbers, gain must be of type float. Poles and Zeros are
        assumed to correct to m/s, SEED convention. Use None for no inverse
        filtering.
    :type paz_simulate: Dictionary, None
    :param paz_simulate: Dictionary containing keys 'poles', 'zeros', 'gain'.
        Poles and zeros must be a list of complex floating point numbers, gain
        must be of type float. Or None for no simulation.
    :type remove_sensitivity: Boolean
    :param remove_sensitivity: Determines if data is divided by
        `paz_remove['sensitivity']` to correct for overall sensitivity of
        recording instrument (seismometer/digitizer) during instrument
        correction.
    :type simulate_sensitivity: Boolean
    :param simulate_sensitivity: Determines if data is multiplied with
        `paz_simulate['sensitivity']` to simulate overall sensitivity of
        new instrument (seismometer/digitizer) during instrument simulation.
    :type water_level: Float
    :param water_level: Water_Level for spectrum to simulate
    :type zero_mean: Boolean
    :param zero_mean: If true the mean of the data is subtracted
    :type taper: Boolean
    :param taper: If true a cosine taper is applied.
    :type taper_fraction: Float
    :param taper_fraction: Taper fraction of cosine taper to use
    :type pre_filt: List or tuple of floats
    :param pre_filt: Apply a bandpass filter to the data trace before
        deconvolution. The list or tuple defines the four corner frequencies
        (f1,f2,f3,f4) of a cosine taper which is one between f2 and f3 and
        tapers to zero for f1 < f < f2 and f3 < f < f4.
    :type seedresp: Dictionary, None
    :param seedresp: Dictionary contains keys 'filename', 'date', 'units'.
        'filename' is the path to a RESP-file generated from a dataless SEED
        volume (or a file like object with RESP information);
        'date' is a `~obspy.core.utcdatetime.UTCDateTime` object for the date
        that the response function should be extracted for (can be omitted when
        calling simulate() on Trace/Stream. the Trace's starttime will then be
        used);
        'units' defines the units of the response function.
        Can be either 'DIS', 'VEL' or 'ACC'.
    :type nfft_pow2: Boolean
    :param nfft_pow2: Number of frequency points to use for FFT. If True,
        the exact power of two is taken (default in PITSA). If False the
        data are not zeropadded to the next power of two which makes a
        slower FFT but is then much faster for e.g. evalresp which scales
        with the FFT points.
    :type pitsasim: Boolean
    :param pitsasim: Choose parameters to match
        instrument correction as done by PITSA.
    :type sacsim: Boolean
    :param sacsim: Choose parameters to match
        instrument correction as done by SAC.
    :type shsim: Boolean
    :param shsim: Choose parameters to match
        instrument correction as done by Seismic Handler.
    :return: The corrected data are returned as numpy.ndarray float64
        array. float64 is chosen to avoid numerical instabilities.

    This function works in the frequency domain, where nfft is the next power
    of len(data) to avoid wrap around effects during convolution. The inverse
    of the frequency response of the seismometer (``paz_remove``) is
    convolved with the spectrum of the data and with the frequency response
    of the seismometer to simulate (``paz_simulate``). A 5% cosine taper is
    taken before simulation. The data must be detrended (e.g.) zero mean
    beforehand. If paz_simulate=None only the instrument correction is done.
    In the latter case, a broadband filter can be applied to the data trace
    using pre_filt. This restricts the signal to the valid frequency band and
    thereby avoids artefacts due to amplification of frequencies outside of the
    instrument's passband (for a detailed discussion see
    *Of Poles and Zeros*, F. Scherbaum, Kluwer Academic Publishers).

    .. versionchanged:: 0.5.1
        The default for `remove_sensitivity` and `simulate_sensitivity` has
        been changed to ``True``. Old deprecated keyword arguments `paz`,
        `inst_sim`, `no_inverse_filtering` have been removed.
    """
    # Checking the types
    if not paz_remove and not paz_simulate and not seedresp:
        msg = "Neither inverse nor forward instrument simulation specified."
        raise TypeError(msg)

    for d in [paz_remove, paz_simulate]:
        if d is None:
            continue
        for key in ["poles", "zeros", "gain"]:
            if key not in d:
                raise KeyError("Missing key: %s" % key)
    # Translated from PITSA: spr_resg.c
    delta = 1.0 / samp_rate
    #
    ndat = len(data)
    data = data.astype("float64")
    if zero_mean:
        data -= data.mean()
    if taper:
        if sacsim:
            data *= cosTaper(ndat, taper_fraction, sactaper=sacsim, halfcosine=False)
        else:
            data *= cosTaper(ndat, taper_fraction)
    # The number of points for the FFT has to be at least 2 * ndat (in
    # order to prohibit wrap around effects during convolution) cf.
    # Numerical Recipes p. 429 calculate next power of 2.
    if nfft_pow2:
        nfft = util.nextpow2(2 * ndat)
    # evalresp scales directly with nfft, therefor taking the next power of
    # two has a greater negative performance impact than the slow down of a
    # not power of two in the FFT
    elif ndat & 0x1:  # check if uneven
        nfft = 2 * (ndat + 1)
    else:
        nfft = 2 * ndat
    # Transform data in Fourier domain
    data = np.fft.rfft(data, n=nfft)
    # Inverse filtering = Instrument correction
    if paz_remove:
        freq_response, freqs = pazToFreqResp(
            paz_remove["poles"], paz_remove["zeros"], paz_remove["gain"], delta, nfft, freq=True
        )
    if seedresp:
        freq_response, freqs = evalresp(
            delta, nfft, seedresp["filename"], seedresp["date"], units=seedresp["units"], freq=True
        )
        if not remove_sensitivity:
            msg = (
                "remove_sensitivity is set to False, but since seedresp "
                + "is selected the overall sensitivity will be corrected "
                + " for anyway!"
            )
            warnings.warn(msg)
    if paz_remove or seedresp:
        if pre_filt:
            # make cosine taper
            fl1, fl2, fl3, fl4 = pre_filt
            if sacsim:
                cos_win = c_sac_taper(freqs.size, freqs=freqs, flimit=(fl1, fl2, fl3, fl4))
            else:
                cos_win = cosTaper(freqs.size, freqs=freqs, flimit=(fl1, fl2, fl3, fl4))
            data *= cos_win
        specInv(freq_response, water_level)
        data *= freq_response
        del freq_response
    # Forward filtering = Instrument simulation
    if paz_simulate:
        data *= pazToFreqResp(paz_simulate["poles"], paz_simulate["zeros"], paz_simulate["gain"], delta, nfft)

    data[-1] = abs(data[-1]) + 0.0j
    # transform data back into the time domain
    data = np.fft.irfft(data)[0:ndat]
    if pitsasim:
        # linear detrend
        data = simpleDetrend(data)
    if shsim:
        # detrend using least squares
        data = scipy.signal.detrend(data, type="linear")
    # correct for involved overall sensitivities
    if paz_remove and remove_sensitivity and not seedresp:
        data /= paz_remove["sensitivity"]
    if paz_simulate and simulate_sensitivity:
        data *= paz_simulate["sensitivity"]
    return data
示例#16
0
文件: ola.py 项目: molfat/dsp
def ola_example():
    # impulse-train signal, 4KHz sampling-rate
    # Length L = 31 causal lowpass filter, 600 Hz cut-off
    # Length M = L rectangular window
    # Hop size R = M (no overlap)

    # simulation params
    L = 31
    fc = 600
    fs = 4000

    # signal sample count
    Nsig = 150
    # signal period in samples
    period = int(round(L / 3))

    # FFT params
    M = L
    Nfft = util.nextpow2(M + L - 1)
    print(f'FFT size={Nfft}')
    # efficient window size
    M = Nfft - L + 1
    print(f'Window size={M}')
    R = M
    Nframes = 1 + np.floor((Nsig - M) / R)

    # impulse train
    sig = np.zeros(Nsig)
    sig[::period] = np.ones(len(range(0, Nsig, period)))
    plt.plot(sig, 'o')

    # low pass filter design via window method

    # zero-phase
    Lo2 = (L - 1) / 2

    # avoid 0 / 0
    epsilon = .0001
    nfilt = np.linspace(-Lo2, Lo2, L) + epsilon
    hideal = np.sin(2 * np.pi * fc * nfilt / fs) / (np.pi * nfilt)
    _, w = windows.hamming(L)
    # window the ideal impulse response
    h = w * hideal

    # zero-pad
    hzp = np.concatenate((h, np.zeros(Nfft-L)))
    H = np.fft.fft(hzp)

    # process via overlap-add

    # allocate output (Nfft) + ringing (Nsig) vector
    # pre/post-ringing length = half of filter length
    y = np.zeros(Nsig + Nfft)

    for m in np.arange(Nframes).astype(int):
        # indices for mth frame
        index = range(m*R, int(np.min(m*R+M)))
        xm = sig[index]
        xmzp = np.concatenate((xm, np.zeros(Nfft - len(xm))))
        Xm = np.fft.fft(xmzp)
        Ym = Xm * H
        ym = np.real(np.fft.ifft(Ym))
        outindex = range(m*R, m*R+Nfft)
        # overlap add
        y[outindex] += ym

    plt.figure()
    plt.plot(y)
    plt.show()
示例#17
0
def seisSim(data,
            samp_rate,
            paz_remove=None,
            paz_simulate=None,
            remove_sensitivity=True,
            simulate_sensitivity=True,
            water_level=600.0,
            zero_mean=True,
            taper=True,
            taper_fraction=0.05,
            pre_filt=None,
            seedresp=None,
            nfft_pow2=False,
            pitsasim=True,
            sacsim=False,
            shsim=False,
            **_kwargs):
    """
    Simulate/Correct seismometer.

    :type data: NumPy ndarray
    :param data: Seismogram, detrend before hand (e.g. zero mean)
    :type samp_rate: Float
    :param samp_rate: Sample Rate of Seismogram
    :type paz_remove: Dictionary, None
    :param paz_remove: Dictionary containing keys 'poles', 'zeros', 'gain'
        (A0 normalization factor). poles and zeros must be a list of complex
        floating point numbers, gain must be of type float. Poles and Zeros are
        assumed to correct to m/s, SEED convention. Use None for no inverse
        filtering.
    :type paz_simulate: Dictionary, None
    :param paz_simulate: Dictionary containing keys 'poles', 'zeros', 'gain'.
        Poles and zeros must be a list of complex floating point numbers, gain
        must be of type float. Or None for no simulation.
    :type remove_sensitivity: Boolean
    :param remove_sensitivity: Determines if data is divided by
        `paz_remove['sensitivity']` to correct for overall sensitivity of
        recording instrument (seismometer/digitizer) during instrument
        correction.
    :type simulate_sensitivity: Boolean
    :param simulate_sensitivity: Determines if data is multiplied with
        `paz_simulate['sensitivity']` to simulate overall sensitivity of
        new instrument (seismometer/digitizer) during instrument simulation.
    :type water_level: Float
    :param water_level: Water_Level for spectrum to simulate
    :type zero_mean: Boolean
    :param zero_mean: If true the mean of the data is subtracted
    :type taper: Boolean
    :param taper: If true a cosine taper is applied.
    :type taper_fraction: Float
    :param taper_fraction: Taper fraction of cosine taper to use
    :type pre_filt: List or tuple of floats
    :param pre_filt: Apply a bandpass filter to the data trace before
        deconvolution. The list or tuple defines the four corner frequencies
        (f1,f2,f3,f4) of a cosine taper which is one between f2 and f3 and
        tapers to zero for f1 < f < f2 and f3 < f < f4.
    :type seedresp: Dictionary, None
    :param seedresp: Dictionary contains keys 'filename', 'date', 'units'.
        'filename' is the path to a RESP-file generated from a dataless SEED
        volume (or a file like object with RESP information);
        'date' is a `~obspy.core.utcdatetime.UTCDateTime` object for the date
        that the response function should be extracted for (can be omitted when
        calling simulate() on Trace/Stream. the Trace's starttime will then be
        used);
        'units' defines the units of the response function.
        Can be either 'DIS', 'VEL' or 'ACC'.
    :type nfft_pow2: Boolean
    :param nfft_pow2: Number of frequency points to use for FFT. If True,
        the exact power of two is taken (default in PITSA). If False the
        data are not zeropadded to the next power of two which makes a
        slower FFT but is then much faster for e.g. evalresp which scales
        with the FFT points.
    :type pitsasim: Boolean
    :param pitsasim: Choose parameters to match
        instrument correction as done by PITSA.
    :type sacsim: Boolean
    :param sacsim: Choose parameters to match
        instrument correction as done by SAC.
    :type shsim: Boolean
    :param shsim: Choose parameters to match
        instrument correction as done by Seismic Handler.
    :return: The corrected data are returned as numpy.ndarray float64
        array. float64 is chosen to avoid numerical instabilities.

    This function works in the frequency domain, where nfft is the next power
    of len(data) to avoid wrap around effects during convolution. The inverse
    of the frequency response of the seismometer (``paz_remove``) is
    convolved with the spectrum of the data and with the frequency response
    of the seismometer to simulate (``paz_simulate``). A 5% cosine taper is
    taken before simulation. The data must be detrended (e.g.) zero mean
    beforehand. If paz_simulate=None only the instrument correction is done.
    In the latter case, a broadband filter can be applied to the data trace
    using pre_filt. This restricts the signal to the valid frequency band and
    thereby avoids artefacts due to amplification of frequencies outside of the
    instrument's passband (for a detailed discussion see
    *Of Poles and Zeros*, F. Scherbaum, Kluwer Academic Publishers).

    .. versionchanged:: 0.5.1
        The default for `remove_sensitivity` and `simulate_sensitivity` has
        been changed to ``True``. Old deprecated keyword arguments `paz`,
        `inst_sim`, `no_inverse_filtering` have been removed.
    """
    # Checking the types
    if not paz_remove and not paz_simulate and not seedresp:
        msg = "Neither inverse nor forward instrument simulation specified."
        raise TypeError(msg)

    for d in [paz_remove, paz_simulate]:
        if d is None:
            continue
        for key in ['poles', 'zeros', 'gain']:
            if key not in d:
                raise KeyError("Missing key: %s" % key)
    # Translated from PITSA: spr_resg.c
    delta = 1.0 / samp_rate
    #
    ndat = len(data)
    data = data.astype("float64")
    if zero_mean:
        data -= data.mean()
    if taper:
        if sacsim:
            data *= cosTaper(ndat,
                             taper_fraction,
                             sactaper=sacsim,
                             halfcosine=False)
        else:
            data *= cosTaper(ndat, taper_fraction)
    # The number of points for the FFT has to be at least 2 * ndat (in
    # order to prohibit wrap around effects during convolution) cf.
    # Numerical Recipes p. 429 calculate next power of 2.
    if nfft_pow2:
        nfft = util.nextpow2(2 * ndat)
    # evalresp scales directly with nfft, therefor taking the next power of
    # two has a greater negative performance impact than the slow down of a
    # not power of two in the FFT
    elif ndat & 0x1:  # check if uneven
        nfft = 2 * (ndat + 1)
    else:
        nfft = 2 * ndat
    # Transform data in Fourier domain
    data = np.fft.rfft(data, n=nfft)
    # Inverse filtering = Instrument correction
    if paz_remove:
        freq_response, freqs = pazToFreqResp(paz_remove['poles'],
                                             paz_remove['zeros'],
                                             paz_remove['gain'],
                                             delta,
                                             nfft,
                                             freq=True)
    if seedresp:
        freq_response, freqs = evalresp(delta,
                                        nfft,
                                        seedresp['filename'],
                                        seedresp['date'],
                                        units=seedresp['units'],
                                        freq=True)
        if not remove_sensitivity:
            msg = "remove_sensitivity is set to False, but since seedresp " + \
                  "is selected the overall sensitivity will be corrected " + \
                  " for anyway!"
            warnings.warn(msg)
    if paz_remove or seedresp:
        if pre_filt:
            # make cosine taper
            fl1, fl2, fl3, fl4 = pre_filt
            if sacsim:
                cos_win = c_sac_taper(freqs.size,
                                      freqs=freqs,
                                      flimit=(fl1, fl2, fl3, fl4))
            else:
                cos_win = cosTaper(freqs.size,
                                   freqs=freqs,
                                   flimit=(fl1, fl2, fl3, fl4))
            data *= cos_win
        specInv(freq_response, water_level)
        data *= freq_response
        del freq_response
    # Forward filtering = Instrument simulation
    if paz_simulate:
        data *= pazToFreqResp(paz_simulate['poles'], paz_simulate['zeros'],
                              paz_simulate['gain'], delta, nfft)

    data[-1] = abs(data[-1]) + 0.0j
    # transform data back into the time domain
    data = np.fft.irfft(data)[0:ndat]
    if pitsasim:
        # linear detrend
        data = simpleDetrend(data)
    if shsim:
        # detrend using least squares
        data = scipy.signal.detrend(data, type="linear")
    # correct for involved overall sensitivities
    if paz_remove and remove_sensitivity and not seedresp:
        data /= paz_remove['sensitivity']
    if paz_simulate and simulate_sensitivity:
        data *= paz_simulate['sensitivity']
    return data
示例#18
0
def example_hilbert_window():
    count = 257
    N = util.nextpow2(count * 8)

    print(f'fft count={N}')
    fs = 22050
    f1 = 530  # transition bandwidth
    beta = 8

    fn = fs / 2  # nyquest
    f2 = fn - f1  # upper transition bandwidth

    # lower-band edge in bins
    k1 = round(N * f1 / fs)
    if k1 < 2:
        # cannot have dc or fn response
        k1 = 2

    k1 = int(k1)

    # bin index at nyquest limit, N even
    kn = N / 2 + 1
    print(f'bin index at nyquest limit, N even. kn={kn}')

    # high-frequency band edge
    k2 = kn - k1 + 1
    k2 = int(k2)

    print(k1, k2, f1, f2)

    # quantized band-edge frequencies
    f1 = k1 * fs / N
    f2 = k2 * fs / N

    print(k1, k2, f1, f2)

    # ideal frequency response
    a = np.concatenate(
        ((np.arange(k1 - 1) / (k1 - 1))**8, np.ones((k2 - k1 + 1))))

    b = np.concatenate(
        ((np.arange(k1 - 2, -1, -1) / (k1 - 1))**8, np.zeros((N // 2 - 1))))
    c = np.concatenate((a, b))

    plt.figure()
    plt.plot(c)

    impulse_response = np.fft.ifft(c)

    # this should be zero
    hodd = np.imag(impulse_response[::2])
    ierr = np.linalg.norm(hodd) / np.linalg.norm(impulse_response)
    print(f'Numerical round-off error = {ierr}')
    aerr = np.linalg.norm(
        impulse_response[N // 2 - N // 32:N // 2 +
                         N // 32]) / np.linalg.norm(impulse_response)
    print(f'Time aliasing = {aerr}')

    plt.figure()
    plt.plot(np.fft.ifftshift(np.real(impulse_response)))

    plt.figure()
    plt.plot(np.fft.ifftshift(np.imag(impulse_response)))

    wrange, winimpulse = win.kaiser(count, beta=beta)
    #plt.figure()
    #plt.plot(wrange, winimpulse)

    # put the kaiser window in zero-phase form:
    wzp1 = winimpulse[count // 2:]
    wzp2 = np.zeros(N - count)
    wzp3 = winimpulse[:count // 2]
    wzp = np.concatenate((wzp1, wzp2, wzp3))

    plt.figure()
    plt.plot(wzp)

    hw = wzp * impulse_response
    plt.figure()
    plt.plot(np.real(hw))

    # final causal version
    hh = np.concatenate((hw[N - (count - 1) // 2:N - 1], hw[:count // 2]))

    response = np.fft.fft(hw)
    gain = np.abs(response)
    gain_db = 20 * np.log10(gain)
    gain_db = gain_db - np.nanmax(gain_db)
    plt.figure()
    plt.plot(np.fft.fftshift(gain_db))
    plt.show()
示例#19
0
def spectral_envelope_example():
    # formant resonance for an "ah" vowel
    F = np.array([700, 1220, 2600])
    # formant bandwidths
    B = np.array([130, 70, 160])

    fs = 8192

    # pole radii
    R = np.exp(-np.pi * B / fs)
    # pole angles
    theta = 2 * np.pi * F / fs
    poles = R * np.exp(1j * theta)

    b, a = signal.zpk2tf([0], np.concatenate((poles, np.conj(poles))), 1)

    # fundamental frequency in Hz
    f0 = 200
    w0T = 2 * np.pi * f0 / fs

    nharm = int((fs / 2) // f0)
    # a second's worth of samples
    nsamps = fs
    sig = np.zeros(nsamps)

    # synthesize the bandlimited impulse train
    n = np.arange(nsamps)
    for i in range(1, nharm + 1):
        sig += np.cos(i * w0T * n)

    # normalize
    sig /= np.max(sig)
    _, w = windows.hamming(512)
    # compute the speech vowel
    sigbl = sig[:len(w)] * w

    #plt.plot(10*np.log10(np.abs(np.fft.rfft(sig, fs))**2))
    #plt.xlim(0, 4500)

    speech = signal.lfilter([1], a, sig)
    # hamming windowed
    speechbl = speech[:len(w)] * w

    #plt.plot(speech)
    #plt.xlim(0, 600)
    #plt.plot(10*np.log10(np.abs(np.fft.rfft(speech, fs))**2))
    #plt.xlim(0, 4500)

    #plt.show()

    rcep, rcepenv = spectral_envelope_cepstrum(speech, f0, fs)
    #plt.plot(rcep)
    #plt.xlim(0, 140)
    #plt.show()

    # spectral envelope by linear prediction

    # assume three formants and no noise
    M = 6

    # compute Mth order autocorrelation function
    rx = np.zeros(M + 1)

    for i in range(M + 1):
        rx[i] = rx[i] + speech[:nsamps - i] @ speech[i:nsamps]

    # prep the M by M Toeplitz covariance matrix
    covmatrix = np.zeros((M, M))
    for i in range(M):
        covmatrix[i, i:] = rx[:M - i]
        covmatrix[i:, i] = rx[:M - i]

    print(covmatrix)

    # solve normal equations for prediction coefficients
    Acoeffs = np.linalg.solve(-covmatrix, rx[1:])

    # linear prediction polynomial
    Alp = np.concatenate(([1], Acoeffs))

    Nframe = util.nextpow2(fs / 25)
    Nfft = 4 * Nframe
    nspec = Nfft // 2 + 1
    _, w = windows.hamming(Nframe)
    winspeech = w * sig[:Nframe]
    Nfft = 4 * Nframe
    sspec = np.fft.fft(winspeech, Nfft)
    dbsspecfull = 20 * np.log10(np.abs(sspec))

    w, h = signal.freqz(1, Alp, nspec, fs=fs)
    dbenvlp = 20 * np.log10(np.abs(h))
    diff = np.max(dbenvlp) - np.max(dbsspecfull)
    dbsspecn = dbsspecfull + np.sum(diff)

    plt.plot(w, dbenvlp)
    plt.plot(w, dbsspecn[:1022:-1])
    plt.show()