예제 #1
0
def single_taper_spectrum(data, delta, taper_name=None):
    """
    Returns the spectrum and the corresponding frequencies for data with the
    given taper.
    """
    length = len(data)
    good_length = length // 2 + 1
    # Create the frequencies.
    # XXX: This might be some kind of hack
    freq = abs(np.fft.fftfreq(length, delta)[:good_length])
    # Create the tapers.
    if taper_name == 'bartlett':
        taper = np.bartlett(length)
    elif taper_name == 'blackman':
        taper = np.blackman(length)
    elif taper_name == 'boxcar':
        taper = np.ones(length)
    elif taper_name == 'hamming':
        taper = np.hamming(length)
    elif taper_name == 'hanning':
        taper = np.hanning(length)
    elif 'kaiser' in taper_name:
        taper = np.kaiser(length, beta=14)
    # Should never happen.
    else:
        msg = 'Something went wrong.'
        raise Exception(msg)
    # Detrend the data.
    data = detrend(data)
    # Apply the taper.
    data *= taper
    spec = abs(np.fft.rfft(data)) ** 2
    return spec, freq
예제 #2
0
def MoninObukhov_length(u,v,w, T):
    """Calculate the Monin Obukhov length

    Not validated!!!

    parameters
    ----------
    u : array_like
        Horizontal wind fluctuations in mean wind direction
    v : array_like
        Horizontal wind fluctuations in perpendicular to mean wind direction
    w : array_like
        Vertical wind fluctuations
    T : array_like
        Potential temperature (close to sonic temperature)
    """
    K = 0.4
    g = 9.82
    u = detrend(u)
    u = u-np.mean(u)
    v = v-np.mean(v)
    w = w-np.mean(w)
    u_star = (np.mean(u*w)**2+np.mean(v*w)**2)**(1/4)
    wT = np.mean(w*T)
    return -u_star ** 3 * (T.mean() + 273.15) / (K * g * wT)
예제 #3
0
파일: main.py 프로젝트: zeppdev/semi-active
 def fftByChannel(self, tab):
     fft_powers = np.ndarray((self.NR_OF_CHANNELS, self.INPUT_RATE / 2 - 1))
     for j in range(self.NR_OF_CHANNELS):
         if tab.CHANNEL_SELECTIONS[j] == 1:
             sp = np.abs(np.fft.fft(detrend(tab.CHANNEL_DEQUES[j]), 512))
             sp = sp[1:256]
             sp_real = sp.real
             fft_powers[j] = 10 * np.log10(sp_real.clip(min=0.000001))
             # fft_powers[j] = sp_real.clip(min=0.000001)
         else:
             # TODO - this is a hack to not get all-zero arrays in the result
             fft_powers[j] = [0.0000001] * 255
     return fft_powers
예제 #4
0
 def fftByChannel(self, tab):
     fft_powers = np.ndarray((self.NR_OF_CHANNELS, self.INPUT_RATE / 2 - 1))
     for j in range(self.NR_OF_CHANNELS):
         if tab.CHANNEL_SELECTIONS[j] == 1:
             sp = np.abs(np.fft.fft(detrend(tab.CHANNEL_DEQUES[j]), 512))
             sp = sp[1:256]
             sp_real = sp.real
             fft_powers[j] = 10 * np.log10(sp_real.clip(min=0.000001))
             # fft_powers[j] = sp_real.clip(min=0.000001)
         else:
             # TODO - this is a hack to not get all-zero arrays in the result
             fft_powers[j] = [0.0000001] * 255
     return fft_powers
예제 #5
0
def detrend_taper_rotate(eventdir, sacfiles):
    """preprocess performs the demean,detrend,taper and rotation into radial and
    transverse components. It saves these at STACK_R.sac and STACK_T.sac"""

    ev = []
    # READ 3 Component SAC files into object array.
    for i in range(3):
        ff = os.path.join(eventdir, sacfiles[i])
        st = read(ff)
        ev.append(st[0])

    # Calculate values to be used in transformations
    dt = ev[1].stats.delta
    pslow = ev[1].stats.sac['user0']
    baz = ev[1].stats.sac['baz']
    PP = ev[1].stats.sac['t7']
    N = ev[1].stats.npts
    # Begin seismogram 50 seconds before P arrival
    # Here we either a full size taper, or a short taper padded with zeros
    if PP and (PP < ev[1].stats.sac['e'] ):
        nend = (PP - ev[1].stats.sac['b'] - 0.5)/dt # Window out 1/2 second before PP
        ctap = np.append( cosTaper(nend), np.zeros(N-nend + 1) )
    else:
        ctap = cosTaper(N)

    # detrend, taper all three components

    for i in range(3):
        ####### DETREND & TAPER #################
        ev[i].data = detrend(ev[i].data) * ctap

    # R, T = rotate(N, E)
    ev[1].data, ev[0].data = rotate(ev[1].data, ev[0].data, baz)
    # Call freetran and rotate into P and S space
    ev[1].data, ev[2].data = freetran(
        ev[1].data, ev[2].data, pslow, 6.06, 3.5)
    # Save freetran transformed data objects
    ev[1].write(os.path.join(eventdir,'stack_P.sac'), format='SAC')
    ev[2].write(os.path.join(eventdir,'stack_S.sac'), format='SAC')
예제 #6
0
def single_taper_spectrum(data, delta, taper_name=None):
    """
    Returns the spectrum and the corresponding frequencies for data with the
    given taper.
    """
    length = len(data)
    good_length = length // 2 + 1
    # Create the frequencies.
    # XXX: This might be some kind of hack

    # Create the tapers.
    if taper_name == "bartlett":
        taper = np.bartlett(length)
    elif taper_name == "blackman":
        taper = np.blackman(length)
    elif taper_name == "boxcar":
        taper = np.ones(length)
    elif taper_name == "hamming":
        taper = np.hamming(length)
    elif taper_name == "hanning":
        taper = np.hanning(length)
    elif "kaiser" in taper_name:
        taper = np.kaiser(length, beta=14)
    # Should never happen.
    else:
        msg = "Something went wrong."
        raise Exception(msg)
    # Detrend the data.
    data = detrend(data)
    # Apply the taper.
    data *= taper
    # TLQ HACK
    nfft = int(2 ** nextpow2(len(data)))
    spec = abs(np.fft.rfft(data)) ** 2
    freq = abs(np.fft.fftfreq(length, delta)[:good_length])
    #    freq = abs(scipy.fftpack.fftfreq(nfft, delta))[:nfft/2]
    #    spec = abs(scipy.fftpack.rfft(data, n=nfft))[:nfft/2] ** 2
    return spec, freq
예제 #7
0
def single_taper_spectrum(data, delta, taper_name=None):
    """
    Returns the spectrum and the corresponding frequencies for data with the
    given taper.
    """
    length = len(data)
    good_length = length // 2 + 1
    # Create the frequencies.
    # XXX: This might be some kind of hack
    
    # Create the tapers.
    if taper_name == 'bartlett':
        taper = np.bartlett(length)
    elif taper_name == 'blackman':
        taper = np.blackman(length)
    elif taper_name == 'boxcar':
        taper = np.ones(length)
    elif taper_name == 'hamming':
        taper = np.hamming(length)
    elif taper_name == 'hanning':
        taper = np.hanning(length)
    elif 'kaiser' in taper_name:
        taper = np.kaiser(length, beta=14)
    # Should never happen.
    else:
        msg = 'Something went wrong.'
        raise Exception(msg)
    # Detrend the data.
    data = detrend(data)
    # Apply the taper.
    data *= taper
    #TLQ HACK
    nfft = int(2**nextpow2(len(data)))
    spec = abs(np.fft.rfft(data)) ** 2
    freq = abs(np.fft.fftfreq(length, delta)[:good_length])
#    freq = abs(scipy.fftpack.fftfreq(nfft, delta))[:nfft/2]
#    spec = abs(scipy.fftpack.rfft(data, n=nfft))[:nfft/2] ** 2
    return spec, freq
예제 #8
0
    def fft(self, x, win, nperseg, noverlap):

        if nperseg == 1 and noverlap == 0:
            result = x[..., np.newaxis]
        else:
            step = nperseg - noverlap
            shape = x.shape[:-1] + ((x.shape[-1] - noverlap) // step, nperseg)
            strides = x.strides[:-1] + (step * x.strides[-1], x.strides[-1])
            result = np.lib.stride_tricks.as_strided(x,
                                                     shape=shape,
                                                     strides=strides)

        result = signaltools.detrend(result, type='constant', axis=-1)
        result = win * result

        if np.iscomplexobj(x):
            func = fftpack.fft
        else:
            result = result.real
            func = np.fft.rfft

        result = func(result, n=nperseg)

        return result
예제 #9
0
파일: fixes.py 프로젝트: HSMin/mne-python
 def detrend_func(d):
     return signaltools.detrend(d, type=detrend, axis=-1)
예제 #10
0
def getrr_v1(data,
             fps=125.0,
             min_rr=300,
             peakfindrange=200,
             slopewidthrange=100,
             smooth_slope_ycenters=0.5,
             discard_short_peaks=False,
             interpolate=True,
             interp_n=50,
             convert_to_ms=False,
             plt=None,
             getslopes=False):
    if data.shape[0] < fps:
        return [], [], []

    data = np.copy(data)
    if convert_to_ms:
        data[:, 0] *= 1000.0

    if fps >= 250:
        factor = int(fps / 250)
        data = data[np.arange(0, len(data), factor), :]

    firstsecond = (data[int(fps), 0] - data[0, 0])
    if firstsecond < 900:
        raise Warning(
            "Warning: FPS set to " + str(fps) +
            ", but timeframe of first FPS data points is " + str(firstsecond) +
            "ms (set convert_to_ms flag to convert between seconds and ms)")

    data[:, 1] /= np.std(data[:, 1])
    data[:, 1] = detrend(data[:, 1])

    # better detrending
    #data[:,1] -= strongsmoothing(data[:,1], 4)
    data[:, 1] = highpass(highpass(data[:, 1], fps), fps)  # highpass detrend
    data[:, 1] = lowpass_fft(data[:, 1], fps, cf=3,
                             tw=0.2)  #slight noise filtering

    # outlier removal
    mn, mx = np.min(data[:, 1]), np.max(data[:, 1])
    m = min(abs(mn), abs(mx))
    containthreshold = 0.001
    N = 100
    step = m / float(N)
    for i in range(N):
        n = len(np.where(data[:, 1] < -m)[0]) + len(
            np.where(data[:, 1] > m)[0])
        if n > containthreshold * len(data[:, 1]):
            break
        m -= step
    mn, mx = -m, m
    data[data[:, 1] < mn, 1] = mn
    data[data[:, 1] > mx, 1] = mx

    data[:, 1] /= np.std(data[:, 1])
    data[:, 1] = detrend(data[:, 1])

    # savgol peaks may be off (lower than the actual peak) - do a local max to deal with that
    maxdata = np.zeros((len(data), ))
    for i in range(len(maxdata)):
        mini = max(0, i - 1)
        maxi = min(len(maxdata), i + 2)
        maxdata[i] = np.max(data[mini:maxi, 1])

    # get initial maxima and minima
    filtered = savgol_filter(data[:, 1], SAVGOL_WINDOWSIZE, SAVGOL_DEGREE)
    mintf = np.array(heartbeat_localmax(-1 * filtered))
    maxtf = np.array(heartbeat_localmax(filtered))

    # get trend of peaks
    hyp_peakidx = np.where(maxtf)[0]
    peakidx = []
    max_window = min_rr
    for p in hyp_peakidx:
        mini = np.max((0, (p - int(fps * max_window / 1e3))))
        maxi = np.min((len(maxdata), (p + int(fps * max_window / 1e3))))
        m = mini + np.argmax(maxdata[mini:maxi])
        while m < len(maxdata) - 1 and maxdata[m + 1] > maxdata[m]:
            m += 1
        while m > 0 and maxdata[m - 1] > maxdata[m]:
            m -= 1
        if m < len(data):
            peakidx.append(m)
    peakidx = np.unique(peakidx)
    f = interp1d(data[peakidx, 0].flatten(),
                 filtered[peakidx].flatten(),
                 bounds_error=False,
                 kind='linear')  # quadratic
    macrotrend = f(data[:, 0].flatten())
    macrotrend[np.where(np.isnan(macrotrend))] = np.mean(
        macrotrend[np.where(~np.isnan(macrotrend))])
    macrotrend = savgol_filter(macrotrend, TREND_SAVGOL_WINDOWSIZE,
                               SAVGOL_DEGREE)

    # delete if not high or low enough
    """"T = macrotrend+MIN_PEAK_DIST_IN_STDEVS*np.std(filtered) # can leave minima - won't matter
    mintf[np.where(maxdata>T)[0]] = False
    T = macrotrend-MIN_PEAK_DIST_IN_STDEVS*np.std(filtered)
    maxtf[np.where(maxdata<T)[0]] = False"""

    minindices = np.where(mintf)[0]
    maxindices = np.where(maxtf)[0]

    if plt:
        plt.plot(data[:, 0],
                 macrotrend - MIN_PEAK_DIST_IN_STDEVS * np.std(filtered))
        #plt.plot(data[:,0], macrotrend+MIN_PEAK_DIST_IN_STDEVS*np.std(filtered), 'k')
        plt.scatter(data[minindices, 0], filtered[minindices], c='g', s=10)
        plt.scatter(data[maxindices, 0], filtered[maxindices], c='b', s=8)

    # ensure we start with min
    i = 0
    while i < len(maxindices) and maxindices[i] < minindices[0]:
        i += 1
    maxindices = maxindices[i:]
    i = len(minindices) - 1
    while i > 1 and minindices[i] > maxindices[-1]:
        i -= 1
    minindices = minindices[:(i + 1)]

    # min,min or max,max
    n = np.min((len(maxindices), len(minindices)))
    for i in range(n):
        needchange = True
        while needchange:
            needchange = False
            if i < len(minindices) - 1 and len(
                    np.where((maxindices > minindices[i]) & (
                        maxindices < minindices[i + 1]))[0]) == 0:  # min,min
                minindices = np.delete(minindices, i)
                needchange = True
            if i < len(maxindices) - 1 and len(
                    np.where((minindices > maxindices[i]) & (
                        minindices < maxindices[i + 1]))[0]) == 0:  # min,min
                maxindices = np.delete(maxindices, i + 1)
                needchange = True
    maxindices = maxindices[:len(minindices)]

    # get rid of too small inter-beat intervals
    maxindices, minindices = remove_double_beats(data,
                                                 filtered,
                                                 maxindices,
                                                 minindices,
                                                 min_rr=min_rr)
    maxindices, minindices = remove_double_beats(data,
                                                 filtered,
                                                 maxindices,
                                                 minindices,
                                                 min_rr=min_rr)

    if plt:
        #plt.plot(data[:,0], data[:,1], '--k', linewidth=3)
        plt.plot(data[:, 0], maxdata)
        plt.plot(data[:, 0], macrotrend, 'r', linewidth=0.2)
        plt.plot(data[:, 0], filtered, 'k', linewidth=0.5)
        #plt.scatter(data[:,0], data[:,1], s=20, c='b')

    mean_slopemid = (np.mean(maxdata[maxindices]) +
                     np.mean(data[minindices, 1])) / 2.0
    max_slopeheight = np.mean(maxdata[maxindices]) - np.mean(data[minindices,
                                                                  1])
    std_slopeheight = np.std(maxdata[maxindices]) + np.std(data[minindices, 1])

    slopebeats = []
    slopes = []
    n = np.min((len(maxindices), len(minindices)))
    # loop through each upslope (min->max) and find an exact beat location in its middle
    for i in range(n - 1):

        if discard_short_peaks:
            slopeheight = maxdata[maxindices[i]] - data[minindices[i], 1]
            if slopeheight < max_slopeheight - 2 * std_slopeheight - MIN_PEAK_DIST_IN_STDEVS * np.std(
                    filtered):
                if plt:
                    print slopeheight, "<", max_slopeheight - 2 * std_slopeheight - MIN_PEAK_DIST_IN_STDEVS * np.std(
                        filtered)
                    plt.scatter(data[maxindices[i], 0],
                                maxdata[maxindices[i]],
                                180,
                                c='k')
                continue

        if interpolate:
            # interpolated
            #ymid = (1-smooth_slope_ycenters)*np.mean([filtered[minindices[i]], filtered[maxindices[i]]])
            ymid = smooth_slope_ycenters * mean_slopemid + (
                1 - smooth_slope_ycenters) * np.mean(
                    [maxdata[minindices[i]], 0.5 * maxdata[maxindices[i]]])
            idxrange = crossing_bracket_indices(data[:, 1],
                                                minindices[i],
                                                maxindices[i],
                                                crossing=ymid)

            mni = max(0, idxrange[0] - 2)
            mxi = idxrange[1] + 2
            if mxi > len(data) - 1:
                mxi = len(data) - 1
            ii = np.linspace(data[mni, 0], data[mxi - 1, 0], interp_n)
            f = interp1d(data[mni:mxi, 0], data[mni:mxi, 1],
                         kind='linear')  # quadratic
            idata = f(ii)
            iidxrange = crossing_bracket_indices(idata, crossing=ymid)
            try:
                k = (idata[iidxrange][-1] - idata[iidxrange][0]) / (
                    ii[iidxrange][-1] - ii[iidxrange][0])
            except:
                k = np.float32.max()
            if plt:
                plt.plot(ii, idata, c='m', linewidth=2)
                plt.scatter(ii[iidxrange], idata[iidxrange], s=40, c='c')
                #k = (idata[iidxrange][-1]-idata[iidxrange][0])/(ii[iidxrange][-1]-ii[iidxrange][0])
                #plt.plot([ii[0]-100, ii[0]+200], [idata[0]-100*k, idata[0]+200*k], '--k', linewidth=2)

            #ymid = (1-smooth_slope_ycenters)*np.mean([filtered[minindices[i]], filtered[maxindices[i]]])
            ymid = smooth_slope_ycenters * mean_slopemid + (
                1 - smooth_slope_ycenters) * np.mean(
                    [maxdata[minindices[i]], 0.5 * maxdata[maxindices[i]]])
            x, y = ii[iidxrange], idata[iidxrange]
            #x, y = ii[iidxrange].reshape(-1,1), idata[iidxrange].reshape(-1,1)
            #model = LinearRegression()
            #model.fit(y.reshape(-1,1), x.reshape(-1,1))
            #xmid = model.predict([[ymid]]) # #xmid2 = [x[0]+y[0]*(x[1]-x[0])/(y[1]-y[0])]
            f = interp1d(y.flatten(),
                         x.flatten(),
                         bounds_error=False,
                         kind='linear')  # quadratic
            xmid = f([ymid])
            if np.isnan(xmid[0]):
                xmid = [x[0] + y[0] * (x[1] - x[0]) / (y[1] - y[0])]
            if xmid < x[0] or xmid >= x[1]:
                xmid = [np.mean([x[0], x[1]])]
            slopebeats.append(xmid[0])
            slopes.append(k)
        else:
            mididx = minindices[i] + (maxindices[i] - minindices[i]) / 2
            if mididx - int(mididx) > 0.5 or int(mididx) >= len(data) - 1:
                idxrange = [int(mididx) - 2, int(mididx)]
            else:
                idxrange = [int(mididx) - 1, int(mididx)]
            x, y = data[idxrange, 0], data[idxrange, 1]
            ymid = (1 - smooth_slope_ycenters) * np.mean(
                [filtered[minindices[i]], filtered[maxindices[i]]])
            f = interp1d(y.flatten(),
                         x.flatten(),
                         bounds_error=False,
                         kind='linear')  # quadratic
            xmid = f([ymid])
            slopebeats.append(xmid[0])

        if plt:
            try:
                plt.scatter(data[idxrange, 0], data[idxrange, 1], s=40, c='y')
                plt.scatter(data[minindices[i], 0],
                            maxdata[minindices[i]],
                            c='g',
                            s=100)
                plt.scatter(data[maxindices[i], 0],
                            maxdata[maxindices[i]],
                            c='b',
                            s=80)
                plt.plot(x, y)
                #yp = data[minindices[i]:maxindices[i], 1]
                #xp = model.predict(yp.reshape(-1,1))
                #plt.plot(xp, yp, '--k', linewidth=2)
                plt.scatter(xmid, ymid, s=80, c='r')
            except Exception, e:
                pass
def _welch(x,
           y,
           fs=1.0,
           window='hanning',
           nperseg=256,
           noverlap=None,
           nfft=None,
           detrend='constant',
           scaling='density',
           axis=-1):
    """
    A helper function to estimate cross spectral density using Welch's method.
    This function is a slightly modified version of `scipy.signal.welch()` with
    modifications based on `matplotlib.mlab._spectral_helper()`.

    Welch's method [1]_ computes an estimate of the cross spectral density
    by dividing the data into overlapping segments, computing a modified
    periodogram for each segment and averaging the cross-periodograms.

    Parameters
    ----------
    x, y : array_like
        Time series of measurement values
    fs : float, optional
        Sampling frequency of the `x` and `y` time series in units of Hz.
        Defaults to 1.0.
    window : str or tuple or array_like, optional
        Desired window to use. See `get_window` for a list of windows and
        required parameters. If `window` is array_like it will be used
        directly as the window and its length will be used for nperseg.
        Defaults to 'hanning'.
    nperseg : int, optional
        Length of each segment.  Defaults to 256.
    noverlap: int, optional
        Number of points to overlap between segments. If None,
        ``noverlap = nperseg / 2``.  Defaults to None.
    nfft : int, optional
        Length of the FFT used, if a zero padded FFT is desired.  If None,
        the FFT length is `nperseg`. Defaults to None.
    detrend : str or function, optional
        Specifies how to detrend each segment. If `detrend` is a string,
        it is passed as the ``type`` argument to `detrend`. If it is a
        function, it takes a segment and returns a detrended segment.
        Defaults to 'constant'.
    scaling : { 'density', 'spectrum' }, optional
        Selects between computing the power spectral density ('density')
        where Pxx has units of V**2/Hz if x is measured in V and computing
        the power spectrum ('spectrum') where Pxx has units of V**2 if x is
        measured in V. Defaults to 'density'.
    axis : int, optional
        Axis along which the periodogram is computed; the default is over
        the last axis (i.e. ``axis=-1``).

    Returns
    -------
    f : ndarray
        Array of sample frequencies.
    Pxy : ndarray
        Cross spectral density or cross spectrum of x and y.

    Notes
    -----
    An appropriate amount of overlap will depend on the choice of window
    and on your requirements.  For the default 'hanning' window an
    overlap of 50% is a reasonable trade off between accurately estimating
    the signal power, while not over counting any of the data.  Narrower
    windows may require a larger overlap.

    If `noverlap` is 0, this method is equivalent to Bartlett's method [2]_.

    References
    ----------
    .. [1] P. Welch, "The use of the fast Fourier transform for the
           estimation of power spectra: A method based on time averaging
           over short, modified periodograms", IEEE Trans. Audio
           Electroacoust. vol. 15, pp. 70-73, 1967.
    .. [2] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra",
           Biometrika, vol. 37, pp. 1-16, 1950.
    """
    # TODO: This function should be replaced by `scipy.signal.csd()`, which
    # will appear in SciPy 0.16.0.

    # The checks for if y is x are so that we can use the same function to
    # obtain both power spectrum and cross spectrum without doing extra
    # calculations.
    same_data = y is x
    # Make sure we're dealing with a numpy array. If y and x were the same
    # object to start with, keep them that way
    x = np.asarray(x)
    if same_data:
        y = x
    else:
        if x.shape != y.shape:
            raise ValueError("x and y must be of the same shape.")
        y = np.asarray(y)

    if x.size == 0:
        return np.empty(x.shape), np.empty(x.shape)

    if axis != -1:
        x = np.rollaxis(x, axis, len(x.shape))
        if not same_data:
            y = np.rollaxis(y, axis, len(y.shape))

    if x.shape[-1] < nperseg:
        warnings.warn('nperseg = %d, is greater than x.shape[%d] = %d, using '
                      'nperseg = x.shape[%d]' %
                      (nperseg, axis, x.shape[axis], axis))
        nperseg = x.shape[-1]

    if isinstance(window, string_types) or type(window) is tuple:
        win = get_window(window, nperseg)
    else:
        win = np.asarray(window)
        if len(win.shape) != 1:
            raise ValueError('window must be 1-D')
        if win.shape[0] > x.shape[-1]:
            raise ValueError('window is longer than x.')
        nperseg = win.shape[0]

    if scaling == 'density':
        scale = 1.0 / (fs * (win * win).sum())
    elif scaling == 'spectrum':
        scale = 1.0 / win.sum()**2
    else:
        raise ValueError('Unknown scaling: %r' % scaling)

    if noverlap is None:
        noverlap = nperseg // 2
    elif noverlap >= nperseg:
        raise ValueError('noverlap must be less than nperseg.')

    if nfft is None:
        nfft = nperseg
    elif nfft < nperseg:
        raise ValueError('nfft must be greater than or equal to nperseg.')

    if not hasattr(detrend, '__call__'):
        detrend_func = lambda seg: signaltools.detrend(seg, type=detrend)
    elif axis != -1:
        # Wrap this function so that it receives a shape that it could
        # reasonably expect to receive.
        def detrend_func(seg):
            seg = np.rollaxis(seg, -1, axis)
            seg = detrend(seg)
            return np.rollaxis(seg, axis, len(seg.shape))
    else:
        detrend_func = detrend

    step = nperseg - noverlap
    indices = np.arange(0, x.shape[-1] - nperseg + 1, step)

    for k, ind in enumerate(indices):
        x_dt = detrend_func(x[..., ind:ind + nperseg])
        xft = fftpack.fft(x_dt * win, nfft)
        if same_data:
            yft = xft
        else:
            y_dt = detrend_func(y[..., ind:ind + nperseg])
            yft = fftpack.fft(y_dt * win, nfft)
        if k == 0:
            Pxy = (xft * yft.conj())
        else:
            Pxy *= k / (k + 1.0)
            Pxy += (xft * yft.conj()) / (k + 1.0)
    Pxy *= scale
    f = fftpack.fftfreq(nfft, 1.0 / fs)

    if axis != -1:
        Pxy = np.rollaxis(Pxy, -1, axis)

    return f, Pxy
예제 #12
0
def welch(x, fs=1.0, window='hanning', nperseg=256, noverlap=None, nfft=None,
          detrend='constant', return_onesided=True, scaling='density', axis=-1):
    """
    Estimate power spectral density using Welch's method.
    Welch's method [1]_ computes an estimate of the power spectral density
    by dividing the data into overlapping segments, computing a modified
    periodogram for each segment and averaging the periodograms.
    Parameters
    ----------
    x : array_like
        Time series of measurement values
    fs : float, optional
        Sampling frequency of the `x` time series in units of Hz. Defaults
        to 1.0.
    window : str or tuple or array_like, optional
        Desired window to use. See `get_window` for a list of windows and
        required parameters. If `window` is array_like it will be used
        directly as the window and its length will be used for nperseg.
        Defaults to 'hanning'.
    nperseg : int, optional
        Length of each segment.  Defaults to 256.
    noverlap: int, optional
        Number of points to overlap between segments. If None,
        ``noverlap = nperseg / 2``.  Defaults to None.
    nfft : int, optional
        Length of the FFT used, if a zero padded FFT is desired.  If None,
        the FFT length is `nperseg`. Defaults to None.
    detrend : str or function or False, optional
        Specifies how to detrend each segment. If `detrend` is a string,
        it is passed as the ``type`` argument to `detrend`.  If it is a
        function, it takes a segment and returns a detrended segment.
        If `detrend` is False, no detrending is done.  Defaults to 'constant'.
    return_onesided : bool, optional
        If True, return a one-sided spectrum for real data. If False return
        a two-sided spectrum. Note that for complex data, a two-sided
        spectrum is always returned.
    scaling : { 'density', 'spectrum' }, optional
        Selects between computing the power spectral density ('density')
        where Pxx has units of V**2/Hz if x is measured in V and computing
        the power spectrum ('spectrum') where Pxx has units of V**2 if x is
        measured in V. Defaults to 'density'.
    axis : int, optional
        Axis along which the periodogram is computed; the default is over
        the last axis (i.e. ``axis=-1``).
    Returns
    -------
    f : ndarray
        Array of sample frequencies.
    Pxx : ndarray
        Power spectral density or power spectrum of x.
    See Also
    --------
    periodogram: Simple, optionally modified periodogram
    lombscargle: Lomb-Scargle periodogram for unevenly sampled data
    Notes
    -----
    An appropriate amount of overlap will depend on the choice of window
    and on your requirements.  For the default 'hanning' window an
    overlap of 50% is a reasonable trade off between accurately estimating
    the signal power, while not over counting any of the data.  Narrower
    windows may require a larger overlap.
    If `noverlap` is 0, this method is equivalent to Bartlett's method [2]_.
    .. versionadded:: 0.12.0
    References
    ----------
    .. [1] P. Welch, "The use of the fast Fourier transform for the
           estimation of power spectra: A method based on time averaging
           over short, modified periodograms", IEEE Trans. Audio
           Electroacoust. vol. 15, pp. 70-73, 1967.
    .. [2] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra",
           Biometrika, vol. 37, pp. 1-16, 1950.
    Examples
    --------
    >>> from scipy import signal
    >>> import matplotlib.pyplot as plt
    Generate a test signal, a 2 Vrms sine wave at 1234 Hz, corrupted by
    0.001 V**2/Hz of white noise sampled at 10 kHz.
    >>> fs = 10e3
    >>> N = 1e5
    >>> amp = 2*np.sqrt(2)
    >>> freq = 1234.0
    >>> noise_power = 0.001 * fs / 2
    >>> time = np.arange(N) / fs
    >>> x = amp*np.sin(2*np.pi*freq*time)
    >>> x += np.random.normal(scale=np.sqrt(noise_power), size=time.shape)
    Compute and plot the power spectral density.
    >>> f, Pxx_den = signal.welch(x, fs, nperseg=1024)
    >>> plt.semilogy(f, Pxx_den)
    >>> plt.ylim([0.5e-3, 1])
    >>> plt.xlabel('frequency [Hz]')
    >>> plt.ylabel('PSD [V**2/Hz]')
    >>> plt.show()
    If we average the last half of the spectral density, to exclude the
    peak, we can recover the noise power on the signal.
    >>> np.mean(Pxx_den[256:])
    0.0009924865443739191
    Now compute and plot the power spectrum.
    >>> f, Pxx_spec = signal.welch(x, fs, 'flattop', 1024, scaling='spectrum')
    >>> plt.figure()
    >>> plt.semilogy(f, np.sqrt(Pxx_spec))
    >>> plt.xlabel('frequency [Hz]')
    >>> plt.ylabel('Linear spectrum [V RMS]')
    >>> plt.show()
    The peak height in the power spectrum is an estimate of the RMS amplitude.
    >>> np.sqrt(Pxx_spec.max())
    2.0077340678640727
    """
    x = np.asarray(x)

    if x.size == 0:
        return np.empty(x.shape), np.empty(x.shape)

    if axis != -1:
        x = np.rollaxis(x, axis, len(x.shape))

    if x.shape[-1] < nperseg:
        warnings.warn('nperseg = %d, is greater than x.shape[%d] = %d, using '
                      'nperseg = x.shape[%d]'
                      % (nperseg, axis, x.shape[axis], axis))
        nperseg = x.shape[-1]

    if isinstance(window, string_types) or type(window) is tuple:
        win = get_window(window, nperseg)
    else:
        win = np.asarray(window)
        if len(win.shape) != 1:
            raise ValueError('window must be 1-D')
        if win.shape[0] > x.shape[-1]:
            raise ValueError('window is longer than x.')
        nperseg = win.shape[0]

    # numpy 1.5.1 doesn't have result_type.
    outdtype = (np.array([x[0]]) * np.array([1], 'f')).dtype.char.lower()
    if win.dtype != outdtype:
        win = win.astype(outdtype)
 
    if scaling == 'density':
        scale = 1.0 / (fs * (win*win).sum())
    elif scaling == 'spectrum':
        scale = 1.0 / win.sum()**2
    else:
        raise ValueError('Unknown scaling: %r' % scaling)

    if noverlap is None:
        noverlap = nperseg // 2
    elif noverlap >= nperseg:
        raise ValueError('noverlap must be less than nperseg.')

    if nfft is None:
        nfft = nperseg
    elif nfft < nperseg:
        raise ValueError('nfft must be greater than or equal to nperseg.')

    if not detrend:
        detrend_func = lambda seg: seg
    elif not hasattr(detrend, '__call__'):
        detrend_func = lambda seg: signaltools.detrend(seg, type=detrend)
    elif axis != -1:
        # Wrap this function so that it receives a shape that it could
        # reasonably expect to receive.
        def detrend_func(seg):
            seg = np.rollaxis(seg, -1, axis)
            seg = detrend(seg)
            return np.rollaxis(seg, axis, len(seg.shape))
    else:
        detrend_func = detrend

    step = nperseg - noverlap
    indices = np.arange(0, x.shape[-1]-nperseg+1, step)

    if np.isrealobj(x) and return_onesided:
        outshape = list(x.shape)
        if nfft % 2 == 0:  # even
            outshape[-1] = nfft // 2 + 1
            Pxx = np.empty(outshape, outdtype)
            for k, ind in enumerate(indices):
                x_dt = detrend_func(x[..., ind:ind+nperseg])
                xft = my_rfft(x_dt*win, nfft)
                #xft = fftpack.rfft(x_dt*win, nfft)
                # fftpack.rfft returns the positive frequency part of the fft
                # as real values, packed r r i r i r i ...
                # this indexing is to extract the matching real and imaginary
                # parts, while also handling the pure real zero and nyquist
                # frequencies.
                if k == 0:
                    Pxx[..., (0,-1)] = xft[..., (0,-1)]**2
                    Pxx[..., 1:-1] = xft[..., 1:-1:2]**2 + xft[..., 2::2]**2
                else:
                    Pxx *= k/(k+1.0)
                    Pxx[..., (0,-1)] += xft[..., (0,-1)]**2 / (k+1.0)
                    Pxx[..., 1:-1] += (xft[..., 1:-1:2]**2 + xft[..., 2::2]**2) \
                                    / (k+1.0)
        else:  # odd
            outshape[-1] = (nfft+1) // 2
            Pxx = np.empty(outshape, outdtype)
            for k, ind in enumerate(indices):
                x_dt = detrend_func(x[..., ind:ind+nperseg])
                xft = my_rfft(x_dt*win, nfft)
                #xft = fftpack.rfft(x_dt*win, nfft)
                #print (nfft, xft.shape, xft_2.shape)
                if k == 0:
                    Pxx[..., 0] = xft[..., 0]**2
                    Pxx[..., 1:] = xft[..., 1::2]**2 + xft[..., 2::2]**2
                else:
                    Pxx *= k/(k+1.0)
                    Pxx[..., 0] += xft[..., 0]**2 / (k+1)
                    Pxx[..., 1:] += (xft[..., 1::2]**2 + xft[..., 2::2]**2) \
                                  / (k+1.0)

        Pxx[..., 1:-1] *= 2*scale
        Pxx[..., (0,-1)] *= scale
        f = np.arange(Pxx.shape[-1]) * (fs/nfft)
    else:
        for k, ind in enumerate(indices):
            x_dt = detrend_func(x[..., ind:ind+nperseg])
            xft = my_rfft(x_dt*win, nfft)
            #xft = fftpack.rfft(x_dt*win, nfft)
            if k == 0:
                Pxx = (xft * xft.conj()).real
            else:
                Pxx *= k/(k+1.0)
                Pxx += (xft * xft.conj()).real / (k+1.0)
        Pxx *= scale
        f = fftpack.fftfreq(nfft, 1.0/fs)

    if axis != -1:
        Pxx = np.rollaxis(Pxx, -1, axis)

    return f, Pxx
예제 #13
0
def welch(x,
          fs=1.0,
          window='hanning',
          nperseg=256,
          noverlap=None,
          nfft=None,
          detrend='constant',
          return_onesided=True,
          scaling='density',
          axis=-1):
    """
    Estimate power spectral density using Welch's method.
    Welch's method [1]_ computes an estimate of the power spectral density
    by dividing the data into overlapping segments, computing a modified
    periodogram for each segment and averaging the periodograms.
    Parameters
    ----------
    x : array_like
        Time series of measurement values
    fs : float, optional
        Sampling frequency of the `x` time series in units of Hz. Defaults
        to 1.0.
    window : str or tuple or array_like, optional
        Desired window to use. See `get_window` for a list of windows and
        required parameters. If `window` is array_like it will be used
        directly as the window and its length will be used for nperseg.
        Defaults to 'hanning'.
    nperseg : int, optional
        Length of each segment.  Defaults to 256.
    noverlap: int, optional
        Number of points to overlap between segments. If None,
        ``noverlap = nperseg / 2``.  Defaults to None.
    nfft : int, optional
        Length of the FFT used, if a zero padded FFT is desired.  If None,
        the FFT length is `nperseg`. Defaults to None.
    detrend : str or function or False, optional
        Specifies how to detrend each segment. If `detrend` is a string,
        it is passed as the ``type`` argument to `detrend`.  If it is a
        function, it takes a segment and returns a detrended segment.
        If `detrend` is False, no detrending is done.  Defaults to 'constant'.
    return_onesided : bool, optional
        If True, return a one-sided spectrum for real data. If False return
        a two-sided spectrum. Note that for complex data, a two-sided
        spectrum is always returned.
    scaling : { 'density', 'spectrum' }, optional
        Selects between computing the power spectral density ('density')
        where Pxx has units of V**2/Hz if x is measured in V and computing
        the power spectrum ('spectrum') where Pxx has units of V**2 if x is
        measured in V. Defaults to 'density'.
    axis : int, optional
        Axis along which the periodogram is computed; the default is over
        the last axis (i.e. ``axis=-1``).
    Returns
    -------
    f : ndarray
        Array of sample frequencies.
    Pxx : ndarray
        Power spectral density or power spectrum of x.
    See Also
    --------
    periodogram: Simple, optionally modified periodogram
    lombscargle: Lomb-Scargle periodogram for unevenly sampled data
    Notes
    -----
    An appropriate amount of overlap will depend on the choice of window
    and on your requirements.  For the default 'hanning' window an
    overlap of 50% is a reasonable trade off between accurately estimating
    the signal power, while not over counting any of the data.  Narrower
    windows may require a larger overlap.
    If `noverlap` is 0, this method is equivalent to Bartlett's method [2]_.
    .. versionadded:: 0.12.0
    References
    ----------
    .. [1] P. Welch, "The use of the fast Fourier transform for the
           estimation of power spectra: A method based on time averaging
           over short, modified periodograms", IEEE Trans. Audio
           Electroacoust. vol. 15, pp. 70-73, 1967.
    .. [2] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra",
           Biometrika, vol. 37, pp. 1-16, 1950.
    Examples
    --------
    >>> from scipy import signal
    >>> import matplotlib.pyplot as plt
    Generate a test signal, a 2 Vrms sine wave at 1234 Hz, corrupted by
    0.001 V**2/Hz of white noise sampled at 10 kHz.
    >>> fs = 10e3
    >>> N = 1e5
    >>> amp = 2*np.sqrt(2)
    >>> freq = 1234.0
    >>> noise_power = 0.001 * fs / 2
    >>> time = np.arange(N) / fs
    >>> x = amp*np.sin(2*np.pi*freq*time)
    >>> x += np.random.normal(scale=np.sqrt(noise_power), size=time.shape)
    Compute and plot the power spectral density.
    >>> f, Pxx_den = signal.welch(x, fs, nperseg=1024)
    >>> plt.semilogy(f, Pxx_den)
    >>> plt.ylim([0.5e-3, 1])
    >>> plt.xlabel('frequency [Hz]')
    >>> plt.ylabel('PSD [V**2/Hz]')
    >>> plt.show()
    If we average the last half of the spectral density, to exclude the
    peak, we can recover the noise power on the signal.
    >>> np.mean(Pxx_den[256:])
    0.0009924865443739191
    Now compute and plot the power spectrum.
    >>> f, Pxx_spec = signal.welch(x, fs, 'flattop', 1024, scaling='spectrum')
    >>> plt.figure()
    >>> plt.semilogy(f, np.sqrt(Pxx_spec))
    >>> plt.xlabel('frequency [Hz]')
    >>> plt.ylabel('Linear spectrum [V RMS]')
    >>> plt.show()
    The peak height in the power spectrum is an estimate of the RMS amplitude.
    >>> np.sqrt(Pxx_spec.max())
    2.0077340678640727
    """
    x = np.asarray(x)

    if x.size == 0:
        return np.empty(x.shape), np.empty(x.shape)

    if axis != -1:
        x = np.rollaxis(x, axis, len(x.shape))

    if x.shape[-1] < nperseg:
        warnings.warn('nperseg = %d, is greater than x.shape[%d] = %d, using '
                      'nperseg = x.shape[%d]' %
                      (nperseg, axis, x.shape[axis], axis))
        nperseg = x.shape[-1]

    if isinstance(window, string_types) or type(window) is tuple:
        win = get_window(window, nperseg)
    else:
        win = np.asarray(window)
        if len(win.shape) != 1:
            raise ValueError('window must be 1-D')
        if win.shape[0] > x.shape[-1]:
            raise ValueError('window is longer than x.')
        nperseg = win.shape[0]

    # numpy 1.5.1 doesn't have result_type.
    outdtype = (np.array([x[0]]) * np.array([1], 'f')).dtype.char.lower()
    if win.dtype != outdtype:
        win = win.astype(outdtype)

    if scaling == 'density':
        scale = 1.0 / (fs * (win * win).sum())
    elif scaling == 'spectrum':
        scale = 1.0 / win.sum()**2
    else:
        raise ValueError('Unknown scaling: %r' % scaling)

    if noverlap is None:
        noverlap = nperseg // 2
    elif noverlap >= nperseg:
        raise ValueError('noverlap must be less than nperseg.')

    if nfft is None:
        nfft = nperseg
    elif nfft < nperseg:
        raise ValueError('nfft must be greater than or equal to nperseg.')

    if not detrend:
        detrend_func = lambda seg: seg
    elif not hasattr(detrend, '__call__'):
        detrend_func = lambda seg: signaltools.detrend(seg, type=detrend)
    elif axis != -1:
        # Wrap this function so that it receives a shape that it could
        # reasonably expect to receive.
        def detrend_func(seg):
            seg = np.rollaxis(seg, -1, axis)
            seg = detrend(seg)
            return np.rollaxis(seg, axis, len(seg.shape))
    else:
        detrend_func = detrend

    step = nperseg - noverlap
    indices = np.arange(0, x.shape[-1] - nperseg + 1, step)

    if np.isrealobj(x) and return_onesided:
        outshape = list(x.shape)
        if nfft % 2 == 0:  # even
            outshape[-1] = nfft // 2 + 1
            Pxx = np.empty(outshape, outdtype)
            for k, ind in enumerate(indices):
                x_dt = detrend_func(x[..., ind:ind + nperseg])
                xft = my_rfft(x_dt * win, nfft)
                #xft = fftpack.rfft(x_dt*win, nfft)
                # fftpack.rfft returns the positive frequency part of the fft
                # as real values, packed r r i r i r i ...
                # this indexing is to extract the matching real and imaginary
                # parts, while also handling the pure real zero and nyquist
                # frequencies.
                if k == 0:
                    Pxx[..., (0, -1)] = xft[..., (0, -1)]**2
                    Pxx[..., 1:-1] = xft[..., 1:-1:2]**2 + xft[..., 2::2]**2
                else:
                    Pxx *= k / (k + 1.0)
                    Pxx[..., (0, -1)] += xft[..., (0, -1)]**2 / (k + 1.0)
                    Pxx[..., 1:-1] += (xft[..., 1:-1:2]**2 + xft[..., 2::2]**2) \
                                    / (k+1.0)
        else:  # odd
            outshape[-1] = (nfft + 1) // 2
            Pxx = np.empty(outshape, outdtype)
            for k, ind in enumerate(indices):
                x_dt = detrend_func(x[..., ind:ind + nperseg])
                xft = my_rfft(x_dt * win, nfft)
                #xft = fftpack.rfft(x_dt*win, nfft)
                #print (nfft, xft.shape, xft_2.shape)
                if k == 0:
                    Pxx[..., 0] = xft[..., 0]**2
                    Pxx[..., 1:] = xft[..., 1::2]**2 + xft[..., 2::2]**2
                else:
                    Pxx *= k / (k + 1.0)
                    Pxx[..., 0] += xft[..., 0]**2 / (k + 1)
                    Pxx[..., 1:] += (xft[..., 1::2]**2 + xft[..., 2::2]**2) \
                                  / (k+1.0)

        Pxx[..., 1:-1] *= 2 * scale
        Pxx[..., (0, -1)] *= scale
        f = np.arange(Pxx.shape[-1]) * (fs / nfft)
    else:
        for k, ind in enumerate(indices):
            x_dt = detrend_func(x[..., ind:ind + nperseg])
            xft = my_rfft(x_dt * win, nfft)
            #xft = fftpack.rfft(x_dt*win, nfft)
            if k == 0:
                Pxx = (xft * xft.conj()).real
            else:
                Pxx *= k / (k + 1.0)
                Pxx += (xft * xft.conj()).real / (k + 1.0)
        Pxx *= scale
        f = fftpack.fftfreq(nfft, 1.0 / fs)

    if axis != -1:
        Pxx = np.rollaxis(Pxx, -1, axis)

    return f, Pxx
예제 #14
0
def ndim_welch(x,
               nperseg=256,
               window='hanning',
               scaling='density',
               detrend='constant',
               fs=1.0,
               axis=-1,
               padded=False):
    """Multidimensional Welch method: Calculating Power Spectral Density for time series of multiple [and one] dimension."""

    ##################################################################
    # checking whether the time series lengths are longer than window
    # length for welch method. If negative the size has been set to time series
    # length. (Taken from original scipy.signal.welch)

    if (not padded) and x.shape[-1] < nperseg:
        warnings.warn('nperseg = %d, is greater than x.shape[%d] = %d, using '
                      'nperseg = x.shape[%d]' %
                      (nperseg, axis, x.shape[axis], axis))
        nperseg = x.shape[-1]

    #########################################################
    # setting the window as is done in original scipy.signal

    if isinstance(window, string_types) or type(window) is tuple:
        win = get_window(window, nperseg)
    else:
        win = np.asarray(window)
        if len(win.shape) != 1:
            raise ValueError('window must be 1-D')
        if win.shape[0] > x.shape[-1]:
            raise ValueError('window is longer than x.')
        nperseg = win.shape[0]

    ######################################################
    #setting the scale as is done in original scipy.signal

    if scaling == 'density':
        scale = 1.0 / (fs * (win * win).sum())
    elif scaling == 'spectrum':
        scale = 1.0 / win.sum()**2
    else:
        raise ValueError('Unknown scaling: %r' % scaling)

    #########################################################
    #  windowing the signal
    # (turning the multidimensional time series into multiple t
    # time series of windowed sections using the function 'win_seg')

    windowed_sig = win_sig(x, nperseg, padded)

    ##################################
    # detrending step

    if not detrend:
        detrend_func = lambda seg: seg
    elif not hasattr(detrend, '__call__'):
        detrend_func = lambda seg: signaltools.detrend(seg, type=detrend)
    elif axis != -1:
        # Wrap this function so that it receives a shape that it could
        # reasonably expect to receive.
        def detrend_func(seg):
            seg = np.rollaxis(seg, -1, axis)
            seg = detrend(seg)
            return np.rollaxis(seg, axis, len(seg.shape))
    else:
        detrend_func = detrend

    windowed_sig = detrend_func(windowed_sig)

    #######################################
    # spectral density estimation

    # multiplying by window
    windowed_sig = np.multiply(win, windowed_sig)

    # calculating the fourier transform
    windowed_seg_fft = fft(windowed_sig)
    windowed_fft = windowed_seg_fft.T

    # returning the spectral density with calcualting outerproducts to get the
    # crossspectrum matrix and also returning the frequency set

    spec_density = np.mean(
        np.einsum('...i,...j->...ij', windowed_fft, windowed_fft.conjugate()) *
        scale,
        axis=1)
    spec_freq = fftfreq(nperseg)
    return spec_freq, np.squeeze(spec_density).real
예제 #15
0
def _welch(x, y, fs=1.0, window='hanning', nperseg=256, noverlap=None,
          nfft=None, detrend='constant', scaling='density', axis=-1):
    """
    A helper function to estimate cross spectral density using Welch's method.
    This function is a slightly modified version of `scipy.signal.welch()` with
    modifications based on `matplotlib.mlab._spectral_helper()`.

    Welch's method [1]_ computes an estimate of the cross spectral density
    by dividing the data into overlapping segments, computing a modified
    periodogram for each segment and averaging the cross-periodograms.

    Parameters
    ----------
    x, y : array_like
        Time series of measurement values
    fs : float, optional
        Sampling frequency of the `x` and `y` time series in units of Hz.
        Defaults to 1.0.
    window : str or tuple or array_like, optional
        Desired window to use. See `get_window` for a list of windows and
        required parameters. If `window` is array_like it will be used
        directly as the window and its length will be used for nperseg.
        Defaults to 'hanning'.
    nperseg : int, optional
        Length of each segment.  Defaults to 256.
    noverlap: int, optional
        Number of points to overlap between segments. If None,
        ``noverlap = nperseg / 2``.  Defaults to None.
    nfft : int, optional
        Length of the FFT used, if a zero padded FFT is desired.  If None,
        the FFT length is `nperseg`. Defaults to None.
    detrend : str or function, optional
        Specifies how to detrend each segment. If `detrend` is a string,
        it is passed as the ``type`` argument to `detrend`. If it is a
        function, it takes a segment and returns a detrended segment.
        Defaults to 'constant'.
    scaling : { 'density', 'spectrum' }, optional
        Selects between computing the power spectral density ('density')
        where Pxx has units of V**2/Hz if x is measured in V and computing
        the power spectrum ('spectrum') where Pxx has units of V**2 if x is
        measured in V. Defaults to 'density'.
    axis : int, optional
        Axis along which the periodogram is computed; the default is over
        the last axis (i.e. ``axis=-1``).

    Returns
    -------
    f : ndarray
        Array of sample frequencies.
    Pxy : ndarray
        Cross spectral density or cross spectrum of x and y.

    Notes
    -----
    An appropriate amount of overlap will depend on the choice of window
    and on your requirements.  For the default 'hanning' window an
    overlap of 50% is a reasonable trade off between accurately estimating
    the signal power, while not over counting any of the data.  Narrower
    windows may require a larger overlap.

    If `noverlap` is 0, this method is equivalent to Bartlett's method [2]_.

    References
    ----------
    .. [1] P. Welch, "The use of the fast Fourier transform for the
           estimation of power spectra: A method based on time averaging
           over short, modified periodograms", IEEE Trans. Audio
           Electroacoust. vol. 15, pp. 70-73, 1967.
    .. [2] M.S. Bartlett, "Periodogram Analysis and Continuous Spectra",
           Biometrika, vol. 37, pp. 1-16, 1950.
    """
    # TODO: This function should be replaced by `scipy.signal.csd()`, which
    # will appear in SciPy 0.16.0.

    # The checks for if y is x are so that we can use the same function to
    # obtain both power spectrum and cross spectrum without doing extra
    # calculations.
    same_data = y is x
    # Make sure we're dealing with a numpy array. If y and x were the same
    # object to start with, keep them that way
    x = np.asarray(x)
    if same_data:
        y = x
    else:
        if x.shape != y.shape:
            raise ValueError("x and y must be of the same shape.")
        y = np.asarray(y)

    if x.size == 0:
        return np.empty(x.shape), np.empty(x.shape)

    if axis != -1:
        x = np.rollaxis(x, axis, len(x.shape))
        if not same_data:
            y = np.rollaxis(y, axis, len(y.shape))

    if x.shape[-1] < nperseg:
        warnings.warn('nperseg = %d, is greater than x.shape[%d] = %d, using '
                      'nperseg = x.shape[%d]'
                      % (nperseg, axis, x.shape[axis], axis))
        nperseg = x.shape[-1]

    if isinstance(window, string_types) or type(window) is tuple:
        win = get_window(window, nperseg)
    else:
        win = np.asarray(window)
        if len(win.shape) != 1:
            raise ValueError('window must be 1-D')
        if win.shape[0] > x.shape[-1]:
            raise ValueError('window is longer than x.')
        nperseg = win.shape[0]

    if scaling == 'density':
        scale = 1.0 / (fs * (win * win).sum())
    elif scaling == 'spectrum':
        scale = 1.0 / win.sum()**2
    else:
        raise ValueError('Unknown scaling: %r' % scaling)

    if noverlap is None:
        noverlap = nperseg // 2
    elif noverlap >= nperseg:
        raise ValueError('noverlap must be less than nperseg.')

    if nfft is None:
        nfft = nperseg
    elif nfft < nperseg:
        raise ValueError('nfft must be greater than or equal to nperseg.')

    if not hasattr(detrend, '__call__'):
        detrend_func = lambda seg: signaltools.detrend(seg, type=detrend)
    elif axis != -1:
        # Wrap this function so that it receives a shape that it could
        # reasonably expect to receive.
        def detrend_func(seg):
            seg = np.rollaxis(seg, -1, axis)
            seg = detrend(seg)
            return np.rollaxis(seg, axis, len(seg.shape))
    else:
        detrend_func = detrend

    step = nperseg - noverlap
    indices = np.arange(0, x.shape[-1] - nperseg + 1, step)

    for k, ind in enumerate(indices):
        x_dt = detrend_func(x[..., ind:ind + nperseg])
        xft = fftpack.fft(x_dt * win, nfft)
        if same_data:
            yft = xft
        else:
            y_dt = detrend_func(y[..., ind:ind + nperseg])
            yft = fftpack.fft(y_dt * win, nfft)
        if k == 0:
            Pxy = (xft * yft.conj())
        else:
            Pxy *= k / (k + 1.0)
            Pxy += (xft * yft.conj()) / (k + 1.0)
    Pxy *= scale
    f = fftpack.fftfreq(nfft, 1.0 / fs)

    if axis != -1:
        Pxy = np.rollaxis(Pxy, -1, axis)

    return f, Pxy
예제 #16
0
def ndim_welch(x,nperseg=256,window='hanning',scaling = 'density',detrend='constant',fs=1.0,axis=-1,padded=False):
    """Multidimensional Welch method: Calculating Power Spectral Density for time series of multiple [and one] dimension."""

    ##################################################################
    # checking whether the time series lengths are longer than window 
    # length for welch method. If negative the size has been set to time series 
    # length. (Taken from original scipy.signal.welch)
    
    if (not padded) and x.shape[-1] < nperseg:
        warnings.warn('nperseg = %d, is greater than x.shape[%d] = %d, using '
                    'nperseg = x.shape[%d]'
                    % (nperseg, axis, x.shape[axis], axis))
        nperseg = x.shape[-1]

    #########################################################
    # setting the window as is done in original scipy.signal

    if isinstance(window, string_types) or type(window) is tuple:
            win = get_window(window, nperseg)
    else:
        win = np.asarray(window)
        if len(win.shape) != 1:
            raise ValueError('window must be 1-D')
        if win.shape[0] > x.shape[-1]:
            raise ValueError('window is longer than x.')
        nperseg = win.shape[0]     
        
    ######################################################
    #setting the scale as is done in original scipy.signal

    if scaling == 'density':
        scale = 1.0 / (fs * (win*win).sum())
    elif scaling == 'spectrum':
        scale = 1.0 / win.sum()**2
    else:
        raise ValueError('Unknown scaling: %r' % scaling)    

    #########################################################
    #  windowing the signal    
    # (turning the multidimensional time series into multiple t
    # time series of windowed sections using the function 'win_seg')

    windowed_sig=win_sig(x,nperseg,padded)


    ##################################    
    # detrending step
    
    if not detrend:
        detrend_func = lambda seg: seg
    elif not hasattr(detrend, '__call__'):
        detrend_func = lambda seg: signaltools.detrend(seg, type=detrend)
    elif axis != -1:
        # Wrap this function so that it receives a shape that it could
        # reasonably expect to receive.
        def detrend_func(seg):
            seg = np.rollaxis(seg, -1, axis)
            seg = detrend(seg)
            return np.rollaxis(seg, axis, len(seg.shape))
    else:
        detrend_func = detrend
    
    windowed_sig=detrend_func(windowed_sig)
    
    
    #######################################
    # spectral density estimation
    

    # multiplying by window
    windowed_sig=np.multiply(win,windowed_sig)
    
    # calculating the fourier transform    
    windowed_seg_fft=fft(windowed_sig)
    windowed_fft=windowed_seg_fft.T
    
    # returning the spectral density with calcualting outerproducts to get the 
    # crossspectrum matrix and also returning the frequency set

    spec_density=np.mean(np.einsum('...i,...j->...ij',windowed_fft,windowed_fft.conjugate())*scale,axis=1)
    spec_freq=fftfreq(nperseg)
    return spec_freq,np.squeeze(spec_density).real
    
예제 #17
0
 def DetrendFunc(self, d):
     return signaltools.detrend(d, type='constant', axis=-1)
예제 #18
0
 def detrend_func(d):
     return signaltools.detrend(d, type=detrend, axis=-1)