def hilbert_transform(X, rate, filters=None, phase=None, X_fft_h=None):
    """
    Apply bandpass filtering with Hilbert transform using
    a prespecified set of filters.

    Parameters
    ----------
    X : ndarray (n_channels, n_time)
        Input data, dimensions
    rate : float
        Number of samples per second.
    filters : filter or list of filters (optional)
        One or more bandpass filters

    Returns
    -------
    Xh : ndarray, complex
        Bandpassed analytic signal
    """
    if not isinstance(filters, list):
        filters = [filters]
    time = X.shape[-1]
    freq = fftfreq(time, 1. / rate)

    Xh = np.zeros((len(filters),) + X.shape, dtype=np.complex)
    if X_fft_h is None:
        # Heavyside filter
        h = np.zeros(len(freq))
        h[freq > 0] = 2.
        h[0] = 1.
        h = h[np.newaxis, :]
        X_fft_h = fft(X) * h
        if phase is not None:
            X_fft_h *= phase
    for ii, f in enumerate(filters):
        if f is None:
            Xh[ii] = ifft(X_fft_h)
        else:
            f = f / np.linalg.norm(f)
            Xh[ii] = ifft(X_fft_h * f)
    if Xh.shape[0] == 1:
        return Xh[0], X_fft_h

    return Xh, X_fft_h
def hilbert_transform(X, rate, filters=None):
    """
    Apply bandpass filtering with Hilbert transform using
    a prespecified set of filters.

    Parameters
    ----------
    X : ndarray (n_channels, n_time)
        Input data, dimensions
    rate : float
        Number of samples per second.
    filters : filter or list of filters (optional)
        One or more bandpass filters

    Returns
    -------
    Xc : array
        Bandpassed analytical signal (dtype: complex)
    """
    if not isinstance(filters, list):
        filters = [filters]
    n_channels, time = X.shape
    freq = fftfreq(time, 1. / rate)

    # Heavyside filter
    h = np.zeros(len(freq))
    h[freq > 0] = 2.
    h[0] = 1.
    h = h[np.newaxis, :]

    Xh = np.zeros((len(filters), ) + X.shape, dtype=np.complex)
    X_fft_h = fft(X) * h
    for ii, f in enumerate(filters):
        if f is None:
            Xh[ii] = ifft(X_fft_h)
        else:
            Xh[ii] = ifft(X_fft_h * f)
    if Xh.shape[0] == 1:
        return Xh[0]

    return Xh
Beispiel #3
0
def spec(x, dt, axis=-1):
    """
Returns the normalized power spectrum (1 / sqrt(n) forward, 1 / sqrt(n)
backwards). This is so that Perseval's rule may be used easily.
Usage:
    [f, g] = spec(x, dt)
INPUTS:
    x    - Array of data to perform the FFT on.
    dt   - Time element between array elements. (Scalar). Used to generate the
            frequency array.
OUTPUTS:
    f   - Frequency Array
    g   - FFT array.
    """
    n = x.shape[axis]
    f0 = 1/abs(n * dt)
    P = fp.fft(x, axis=axis)/sqrt(n)
    #Generate an array 1:N, subtract (N + 1) /2 if odd, (N+2)/2 if even.
    f = (linspace(1, n, n) - (n + (1 + (n % 2 ==0 )))/2) * f0
    P = spfp.fftshift(P, axes=axis)
    return f, P
Beispiel #4
0
def wavelet(data, freqs, srate, wave_num, demean=True):
  '''
  @title Wavelet Transformation With Phase
  @param data - vector of time series to be decomposed
  @param freqs - vector of center frequencies for decomposition
  @param srate - sample rate (in Hz)
  @param wave_num - desired number of cycles in wavelet (typically 3-20 for frequencies 2-200).
  @param demean - whether to de-mean data first?
  @details
  Wavelet
  Function for decomposing time series data into time-frequency
  representation (spectral decomposition) using wavelet transform. Employs
  Morlet wavelet method (gaussian taper sine wave) to obtain the analytic
  signal for specified frequencies (via convolution).
  Inputs:
   data - vector of time series to be decomposed
   freqs - vector of center frequencies for decomposition
   srate - sample rate (in Hz)
   wave_num - desired number of cycles in wavelet (typically 5-10).
  
  Translated from Matlab script written by Brett Foster,
  Stanford Memory Lab, Feb. 2015
  '''
  srate = round(srate);
  
  # wave_num can be an array equals to length of freqs or just two
  if not len(wave_num) == len(freqs):
    ratio = (np.log(np.max(wave_num)) - np.log(np.min(wave_num))) / (np.log(np.max(freqs)) - np.log(np.min(freqs)))
    wavelet_cycles = np.exp((np.log(freqs) - np.log(np.min(freqs))) * ratio + np.log(np.min(wave_num)))
  else:
    wavelet_cycles = np.array(wave_num)
    
  f_l = len(freqs)
  d_l = data.shape[-1]
  
  # normalize data, and fft
  if demean:
    fft_data = fft(data - np.mean(data))
  else:
    fft_data = fft(data)
  
  # wavelet window calc - each columns of final wave is a wavelet kernel (after fft)
  # sts = wavelet_cycles / (2 * pi * freqs)
  # wavelet_wins = cbind(-3 * sts, 3 * sts)
  
  # calculate and fft wavelet kernels
  fft_waves = np.ndarray(shape = [f_l, d_l], dtype = np.complex128)
  
  for ii in range(f_l):
    fq = freqs[ii]
    cycles = wavelet_cycles[ii]
    
    # standard error
    st = cycles / (2 * np.pi * fq)
    
    # calculate window size
    wavelet_win = np.arange(-3 * st, 3 * st, 1/srate)
    # half of window length
    w_l_half = (len(wavelet_win) - 1) / 2
    
    # wavelet 1: calc sinus in complex domain
    tmp_sine = np.exp((1j) * 2 * np.pi * fq / srate * np.arange(-w_l_half,w_l_half+1))
    # Gaussian normalization part
    A = 1/np.sqrt(st*np.sqrt(np.pi))
    
    # wavelet 2: calc gaussian wrappers
    tmp_gaus_win = A * np.exp(-np.power(wavelet_win, 2)/(2 * np.power(cycles/(2 * np.pi * fq), 2)))
    
    # wave kernel
    tmp_wavelet = tmp_sine * tmp_gaus_win
    
    # padding
    w_l = len(tmp_wavelet)
    n_pre  = int(np.ceil(d_l / 2) - np.floor(w_l/2))
    n_post = int(d_l - n_pre - w_l)
    
    wvi = np.pad(tmp_wavelet, [n_pre, n_post], 'constant', constant_values = 0)
    fft_wave = np.conj(fft(wvi))
    
    fft_waves[ii, :] = fft_wave
  
  # Convolution Notice that since we don't pad zeros to data
  # d_l is nrows of wave_spectrum. However, if wave_spectrum is changed
  # we should not use d_l anymore. instead, use nrow(fft_waves)
  wave_len = fft_waves.shape[-1]
  wave_spectrum = ifft(fft_data * fft_waves)
  
  # use numpy fancy index
  cut_off = np.ceil(wave_len / 2)
  ind = np.concatenate([np.arange(cut_off, wave_len, dtype = np.int), np.arange(cut_off, dtype = np.int)])
  wave_spectrum = wave_spectrum[:, ind] / np.sqrt(srate / 2)
  
  # returns amplitude and phase data
  phase = np.angle(wave_spectrum)
  power = np.power(np.real(wave_spectrum), 2) + np.power(np.imag(wave_spectrum), 2)
  coef = np.sqrt(power)
  
  return {
    'power' : power,
    'phase' : phase,
    'coef' : coef
  }
def resample(x, num, t=None, axis=0, window=None):
    """
    Resample `x` to `num` samples using Fourier method along the given axis.
    The resampled signal starts at the same value as `x` but is sampled
    with a spacing of ``len(x) / num * (spacing of x)``.  Because a
    Fourier method is used, the signal is assumed to be periodic.
    Parameters
    ----------
    x : array_like
        The data to be resampled.
    num : int
        The number of samples in the resampled signal.
    t : array_like, optional
        If `t` is given, it is assumed to be the sample positions
        associated with the signal data in `x`.
    axis : int, optional
        The axis of `x` that is resampled.  Default is 0.
    window : array_like, callable, string, float, or tuple, optional
        Specifies the window applied to the signal in the Fourier
        domain.  See below for details.
    Returns
    -------
    resampled_x or (resampled_x, resampled_t)
        Either the resampled array, or, if `t` was given, a tuple
        containing the resampled array and the corresponding resampled
        positions.
    Notes
    -----
    The argument `window` controls a Fourier-domain window that tapers
    the Fourier spectrum before zero-padding to alleviate ringing in
    the resampled values for sampled signals you didn't intend to be
    interpreted as band-limited.
    If `window` is a function, then it is called with a vector of inputs
    indicating the frequency bins (i.e. fftfreq(x.shape[axis]) ).
    If `window` is an array of the same length as `x.shape[axis]` it is
    assumed to be the window to be applied directly in the Fourier
    domain (with dc and low-frequency first).
    For any other type of `window`, the function `scipy.signal.get_window`
    is called to generate the window.
    The first sample of the returned vector is the same as the first
    sample of the input vector.  The spacing between samples is changed
    from ``dx`` to ``dx * len(x) / num``.
    If `t` is not None, then it represents the old sample positions,
    and the new sample positions will be returned as well as the new
    samples.
    As noted, `resample` uses FFT transformations, which can be very
    slow if the number of input samples is large and prime, see
    `scipy.fftpack.fft`.
    """
    x = np.asarray(x)
    X = fft(x, axis=axis)
    Nx = x.shape[axis]
    if window is not None:
        if callable(window):
            W = window(fftfreq(Nx))
        elif isinstance(window, np.ndarray):
            if window.shape != (Nx, ):
                raise ValueError('window must have the same length as data')
            W = window
        else:
            W = ifftshift(get_window(window, Nx))
        newshape = [1] * x.ndim
        newshape[axis] = len(W)
        W.shape = newshape
        X = X * W
    sl = [slice(None)] * len(x.shape)
    newshape = list(x.shape)
    newshape[axis] = num
    N = int(np.minimum(num, Nx))
    Y = np.zeros(newshape, 'D')
    sl[axis] = slice(0, (N + 1) // 2)
    Y[sl] = X[sl]
    sl[axis] = slice(-(N - 1) // 2, None)
    Y[sl] = X[sl]
    y = ifft(Y, axis=axis) * (float(num) / float(Nx))

    if x.dtype.char not in ['F', 'D']:
        y = y.real

    if t is None:
        return y
    else:
        new_t = np.arange(0, num) * (t[1] - t[0]) * Nx / float(num) + t[0]
        return y, new_t