def welch(self, x, nfft, pad_to, sampRate): window = mlab.window_hanning x = np.asarray(x) numFreqs = pad_to if pad_to % 2: freqcenter = (pad_to - 1)//2 + 1 else: freqcenter = pad_to//2 # Split input vector into slices temp = stride_windows(x, nfft, nfft/2, axis=0) # Apply window function temp, windowVal = apply_window(temp, window, axis=0, return_window=True) # Calculate window normalization S_1 = (np.abs(windowVal)).sum() # Calculate FFT power = np.fft.fft(temp, pad_to, axis=0)[:numFreqs, :] freqs = np.fft.fftfreq(pad_to, 1/sampRate)[:numFreqs] power = np.conjugate(power) * power power /= S_1**2 freqs = np.concatenate((freqs[freqcenter:], freqs[:freqcenter])) power = np.concatenate((power[freqcenter:, :], power[:freqcenter, :]), 0) # Average the power spectra power = np.mean(power, axis=1) power = power.real return power, freqs
def cal_mag(IQcomplex, fc=2472, sr=20, fft_size=1024): ''' 该函数用于计算线性振幅频谱图的频率与振幅 :param IQcomplex: 样本集 :param fc: 中心频率 :param sr: 采样率,默认20M :param fft_size: fft数量 :return: spec振幅, freq频率 ''' # fft_size = 1024 IQcomplex_1024 = np.asarray(IQcomplex[0:fft_size]) # 加窗,不然频谱现象不明显 result, windowVal = mlab.apply_window(IQcomplex_1024, window=mlab.window_hanning, axis=0, return_window=True) # 进行fft result = np.fft.fft(result, n=fft_size) # 参数1:FFT点数,参数2:采样周期,即1/采样频率 freqs = np.fft.fftfreq(fft_size, 1 / sr) # 以下代码的作用貌似是调整双边的位置 freqcenter = fft_size // 2 freqs = np.concatenate((freqs[freqcenter:], freqs[:freqcenter])) result = np.concatenate((result[freqcenter:], result[:freqcenter]), 0) # 貌似是归一化 result = np.abs(result) / np.abs(windowVal).sum() # 加上中心频率 Fc = fc freqs += Fc # 取对数坐标 spec = 20. * np.log10(result) return spec, freqs
def cal_mag(IQcomplex, fft_size=1024): # fft_size = 1024 IQcomplex_1024 = np.asarray(IQcomplex[0:fft_size]) # 加窗,不然频谱现象不明显 result, windowVal = mlab.apply_window(IQcomplex_1024, window=mlab.window_hanning, axis=0, return_window=True) # 进行fft result = np.fft.fft(result, n=fft_size) # 参数1:FFT点数,参数2:采样周期,即1/采样频率 freqs = np.fft.fftfreq(fft_size, 1 / 25) # 以下代码的作用貌似是调整双边的位置 freqcenter = fft_size // 2 freqs = np.concatenate((freqs[freqcenter:], freqs[:freqcenter])) result = np.concatenate((result[freqcenter:], result[:freqcenter]), 0) # 貌似是归一化 result = np.abs(result) / np.abs(windowVal).sum() # 加上中心频率 Fc = 2433 freqs += Fc # 取对数坐标 spec = 20. * np.log10(result) return spec, freqs
def welch(self, x, nfft, pad_to, sampRate): window = mlab.window_hanning x = np.asarray(x) numFreqs = pad_to if pad_to % 2: freqcenter = (pad_to - 1) // 2 + 1 else: freqcenter = pad_to // 2 # Split input vector into slices temp = stride_windows(x, nfft, nfft / 2, axis=0) # Apply window function temp, windowVal = apply_window(temp, window, axis=0, return_window=True) # Calculate window normalization S_1 = (np.abs(windowVal)).sum() # Calculate FFT power = np.fft.fft(temp, pad_to, axis=0)[:numFreqs, :] freqs = np.fft.fftfreq(pad_to, 1 / sampRate)[:numFreqs] power = np.conjugate(power) * power power /= S_1**2 freqs = np.concatenate((freqs[freqcenter:], freqs[:freqcenter])) power = np.concatenate((power[freqcenter:, :], power[:freqcenter, :]), 0) # Average the power spectra power = np.mean(power, axis=1) power = power.real return power, freqs
def window(x, n, samprate, overlap, normalize=True, KOsmooth=False, window_correction=None, KOnormalize=False, bandwidth=40, detrend=True, subtractmean=False, winlen=201, polyorder=3): """ Windowing borrowed from matplotlib.mlab for specgram (_spectral_helper) Applies hanning window (if use 0.5 overlap, amplitudes are ~preserved) https://github.com/matplotlib/matplotlib/blob/f92bd013ea8f0f99d2e177fd572b86f3b42bb652/lib/matplotlib/mlab.py#L434 DIVIDES BY NFFT TO CORRECT AMPLITUDES Args: x (array): 1xn array data to window n (int): length each window should be, in samples (before padding) overlap (float): proportion of overlap of windows, should be between 0 and 1 normalize (bool): if True, will normalize by signal length by dividing by 1/NFFT (1/NFFT = deltat/time length), if False, will scale by deltat (1/samprate) to approximate continuous transform samprate (float): sampling rate of x, in samples per second KOsmooth (bool): If True, will return Konno Ohmachi smoothed spectra with only positive frequencies window_correction (str): Apply correction for fourier spectrum to account for windowing. If 'amp', will multiply spectrum by 2 to preserve amplitude, if 'energy' will multiply by 1.63 normalize (bool): If True, KOsmooth will be smoothed linearly, otherwise will be smooth logarithmically bandwidth (float): bandwidth for KO smoothing detrend (bool): if True, will detrend each window subtractmean (bool): if True, will subtract time-averaged mean from entire time series before windowing using savgol filter winlen polyorder Returns: tmid: time vector taken at midpoints of each window (in sec from 0) tstart: time vector taken at beginning of each window (in sec from 0) freqs: vector of frequency (Hz), applyFT and applyKOsmooth are False, will return None resultF: fourier transform, or smoothed fourier transform of each time window resultT: time series of each time window (note, will have hanning window applied) """ if overlap < 0. or overlap > 1.: raise Exception('overlap must be between 0 and 1') if subtractmean: mean1 = savgol_filter(np.copy(x), winlen, polyorder) x = np.copy(x) - mean1 noverlap = int(overlap * n) resultT1 = mlab.stride_windows(x, n, noverlap) row, col = np.shape(resultT1) newsampint = (n-noverlap)/samprate tstart = np.linspace(0, (col-1)*newsampint, num=col) tmid = tstart + 0.5*(n/samprate) # shift by half of window length NFFT = nextpow2(n) if detrend: resultT = mlab.detrend(resultT1, key='mean', axis=0) else: resultT = resultT1 resultTwin, windowVals = mlab.apply_window(resultT, mlab.window_hanning, axis=0, return_window=True) if KOsmooth: if normalize: resultF = np.fft.rfft(resultTwin, n=NFFT, axis=0)/NFFT else: resultF = np.fft.rfft(resultTwin, n=NFFT, axis=0)/samprate if window_correction == 'amp': # For hanning window resultF *= 2.0 elif window_correction == 'energy': resultF *= 1.63 freqs = np.fft.rfftfreq(NFFT, 1/samprate) resultF = ksmooth(np.abs(resultF.T), freqs, normalize=KOnormalize, bandwidth=bandwidth) resultF = resultF.T else: if normalize: resultF = np.fft.fft(resultTwin, n=NFFT, axis=0)/NFFT else: resultF = np.fft.fft(resultTwin, n=NFFT, axis=0)/samprate freqs = np.fft.fftfreq(NFFT, 1/samprate) if window_correction == 'amp': resultF *= 2.0 elif window_correction == 'energy': resultF *= 1.63 return tmid, tstart, freqs, resultF, resultT, resultTwin
def getFFTs(self, x, detrend=mlab.detrend_none, window=mlab.window_hanning): '''Get array of FFTs corresponding to each realization of `x`. Parameters: ----------- x - array_like, (`N`,) Signal to be analyzed. Signal is split into several realizations, and the FFT of each realization is computed. [x] = arbitrary units detrend - string The function applied to each realization before taking FFT. May be [ 'default' | 'constant' | 'mean' | 'linear' | 'none'] or callable, as specified in :py:func: `csd <matplotlib.mlab.csd>`. *Warning*: Naively detrending (even with something as simple as `mean` or `linear` detrending) can introduce detrimental artifacts into the computed spectrum, so *no* detrending is the default. window - callable or ndarray The window applied to each realization before taking FFT, as specified in :py:func: `csd <matplotlib.mlab.csd>`. Returns: -------- Xk - array_like, (L, M, N) where L = `len(self.f)` = `(self.Npts_per_real // 2) + 1`, M = number of whole ensembles in data record `x`, and N = `self.Nreal_per_ens` The FFTs of each realization in each ensemble. The FFTs are indexed by frequency, ensemble, and realization. [Xk] = [x] ''' # Only real-valued signals are expected/supported at the moment if np.iscomplexobj(x): raise ValueError('`x` must be a real-valued signal!') # Determine the number of *whole* ensembles in the data record # (Disregard fractional ensemble at the end of the data, if present) Nens = np.int(len(x) / self.Npts_per_ens) # Determine number of frequencies in 1-sided FFT, noting that # `self.Npts_per_real` is constrained to be a power of 2 Nf = (self.Npts_per_real // 2) + 1 # Initialize. Xk = np.zeros( (Nf, Nens, self.Nreal_per_ens), dtype='complex') # Loop through each ensemble, computing the FFT of each realization # via strides for efficient use of memory. (Note that the below # procedure closely parallels that of Matplotlib's internal function # # :py:func:`_spectral_helper <matplotlib.mlab._spectral_helper>` # # Here, we use our own implementation so as not to rely on # an internal function) stride_axis = 0 for ens in np.arange(Nens): # Split the ensemble into realizations sl = slice( ens * self.Npts_per_ens, (ens + 1) * self.Npts_per_ens) result = mlab.stride_windows( x[sl], self.Npts_per_real, self.Npts_overlap, axis=stride_axis) # Detrend each realization result = mlab.detrend( result, detrend, axis=stride_axis) # Window each realization (power loss compensated outside loop) result, windowVals = mlab.apply_window( result, window, axis=stride_axis, return_window=True) # Finally compute and return the FFT of each realization Xk[:, ens, :] = np.fft.rfft(result, axis=stride_axis) # Compensate for windowing power loss norm = np.sqrt(np.mean((np.abs(windowVals)) ** 2)) Xk /= norm return Xk
def getFFTs(self, x, detrend=mlab.detrend_none, window=mlab.window_hanning): '''Get array of FFTs corresponding to each realization of `x`. Parameters: ----------- x - array_like, (`N`,) Signal to be analyzed. Signal is split into several realizations, and the FFT of each realization is computed. [x] = arbitrary units detrend - string The function applied to each realization before taking FFT. May be [ 'default' | 'constant' | 'mean' | 'linear' | 'none'] or callable, as specified in :py:func: `csd <matplotlib.mlab.csd>`. *Warning*: Naively detrending (even with something as simple as `mean` or `linear` detrending) can introduce detrimental artifacts into the computed spectrum, so *no* detrending is the default. window - callable or ndarray The window applied to each realization before taking FFT, as specified in :py:func: `csd <matplotlib.mlab.csd>`. Returns: -------- Xk - array_like, (L, M, N) where L = `len(self.f)` = `(self.Npts_per_real // 2) + 1`, M = number of whole ensembles in data record `x`, and N = `self.Nreal_per_ens` The FFTs of each realization in each ensemble. The FFTs are indexed by frequency, ensemble, and realization. [Xk] = [x] ''' # Only real-valued signals are expected/supported at the moment if np.iscomplexobj(x): raise ValueError('`x` must be a real-valued signal!') # Determine the number of *whole* ensembles in the data record # (Disregard fractional ensemble at the end of the data, if present) Nens = np.int(len(x) / self.Npts_per_ens) # Determine number of frequencies in 1-sided FFT, noting that # `self.Npts_per_real` is constrained to be a power of 2 Nf = (self.Npts_per_real // 2) + 1 # Initialize. Xk = np.zeros((Nf, Nens, self.Nreal_per_ens), dtype='complex') # Loop through each ensemble, computing the FFT of each realization # via strides for efficient use of memory. (Note that the below # procedure closely parallels that of Matplotlib's internal function # # :py:func:`_spectral_helper <matplotlib.mlab._spectral_helper>` # # Here, we use our own implementation so as not to rely on # an internal function) stride_axis = 0 for ens in np.arange(Nens): # Split the ensemble into realizations sl = slice(ens * self.Npts_per_ens, (ens + 1) * self.Npts_per_ens) result = mlab.stride_windows(x[sl], self.Npts_per_real, self.Npts_overlap, axis=stride_axis) # Detrend each realization result = mlab.detrend(result, detrend, axis=stride_axis) # Window each realization (power loss compensated outside loop) result, windowVals = mlab.apply_window(result, window, axis=stride_axis, return_window=True) # Finally compute and return the FFT of each realization Xk[:, ens, :] = np.fft.rfft(result, axis=stride_axis) # Compensate for windowing power loss norm = np.sqrt(np.mean((np.abs(windowVals))**2)) Xk /= norm return Xk