def test_boxcar(self): w = windows.get_window('boxcar', 12) assert_array_equal(w, np.ones_like(w)) # window is a tuple of len 1 w = windows.get_window(('boxcar',), 16) assert_array_equal(w, np.ones_like(w))
def windowff(nslices,kind=None): """ Returns a window function to enforce quasi-preiodicity on an aperiodic signal. Window options are: BlackmanHarris, Hanning, Blackman, FlatTop, Welch, Tukey """ import scipy.signal.windows as w if kind is None: return np.ones(nslices) elif kind.lower() == 'tukey': return w.get_window((kind.lower(),0.1),nslices) else: return w.get_window(kind.lower(),nslices)
def dziel_na_segmenty(self, window, nperseg, input_length): if nperseg > input_length: nperseg = input_length win = get_window(window, nperseg) return win, nperseg
def window_filter(curve: 'Curve', window_size: int, window_type: ty.Union[str, abc.Callable] = 'hann', mode: str = 'reflect', cval: float = 0.0) -> np.ndarray: """Smoothes a curve using moving average filter with the given window [1]_ References ---------- .. [1] `The windows in scipy <https://docs.scipy.org/doc/scipy/reference/signal.windows.html#module-scipy.signal.windows>`_ """ if callable(window_type): try: window = window_type(window_size) except Exception as err: raise ValueError( f'Cannot create the window using {window_type}: {err}' ) from err else: window = windows.get_window(window_type, window_size, fftbins=False) window /= window.sum() return ndimage.convolve1d( curve.data, weights=window, mode=mode, cval=cval, axis=0, )
def test_rollingmean_detrend(series): x = series(np.arange(12 * 365.25), "tas") det = RollingMeanDetrend(group="time", win=29, min_periods=1) fx = det.fit(x) dx = fx.detrend(x) xt = fx.retrend(dx) np.testing.assert_array_almost_equal(dx.isel(time=slice(30, 3500)), 0) np.testing.assert_array_almost_equal(xt, x) # weights + grouping x = xr.DataArray( np.sin(2 * np.pi * np.arange(11 * 365) / 365), dims=("time", ), coords={ "time": xr.cftime_range("2010-01-01", periods=11 * 365, freq="D", calendar="noleap") }, ) w = windows.get_window("triang", 11, False) det = RollingMeanDetrend(group=Grouper("time.dayofyear", window=3), win=11, weights=w) fx = det.fit(x) assert fx.ds.trend.notnull().sum() == 365
def test_basic(self): M = 100 w = windows.kaiser_bessel_derived(M, beta=4.0) w2 = windows.get_window(('kaiser bessel derived', 4.0), M, fftbins=False) assert_allclose(w, w2) # Test for Princen-Bradley condition assert_allclose(w[:M // 2]**2 + w[-M // 2:]**2, 1.) # Test actual values from other implementations # M = 2: sqrt(2) / 2 # M = 4: 0.518562710536, 0.855039598640 # M = 6: 0.436168993154, 0.707106781187, 0.899864772847 # Ref:https://github.com/scipy/scipy/pull/4747#issuecomment-172849418 assert_allclose( windows.kaiser_bessel_derived(2, beta=np.pi / 2)[:1], np.sqrt(2) / 2) assert_allclose( windows.kaiser_bessel_derived(4, beta=np.pi / 2)[:2], [0.518562710536, 0.855039598640]) assert_allclose( windows.kaiser_bessel_derived(6, beta=np.pi / 2)[:3], [0.436168993154, 0.707106781187, 0.899864772847])
def test_array_as_window(self): # github issue 3603 osfactor = 128 sig = np.arange(128) win = windows.get_window(('kaiser', 8.0), osfactor // 2) with assert_raises(ValueError, match='must have the same length'): resample(sig, len(sig) * osfactor, window=win)
def __init__(self, window, source=None, **kw_get_win): if isinstance(window, str) and source: from scipy.signal.windows import get_window window = get_window(window, Nx=source.blocksize, **kw_get_win) else: from numpy import asarray window = asarray(window, dtype=float) self.window = window
def test_array_as_window(self): # github issue 3603 osfactor = 128 sig = np.arange(128) win = windows.get_window(('kaiser', 8.0), osfactor // 2) assert_raises(ValueError, resample, (sig, len(sig) * osfactor), {'window': win})
def compute_iq(spectra, fields_in_list, fields_out_list, window=None): """ Computes the IQ data from the spectra through an inverse Fourier transform Parameters ---------- spectra : Spectra radar object Object containing the spectra fields_in_list : list of str list of input spectra fields names fields_out_list : list of str list with the output IQ fields names obtained from the input fields window : string, tupple or None Parameters of the window used to obtain the spectra. The parameters are the ones corresponding to function scipy.signal.windows.get_window. If it is not None the inverse will be used to multiply the IQ data obtained by the IFFT Returns ------- radar : IQ radar object radar object containing the IQ fields """ radar = deepcopy(spectra) radar.fields = {} for field_name_in, field_name_out in zip(fields_in_list, fields_out_list): if field_name_out in ('IQ_hh_ADU', 'IQ_vv_ADU'): iq = np.ma.masked_all( (spectra.nrays, spectra.ngates, spectra.npulses_max), dtype=np.complex64) for ray, npuls in enumerate(spectra.npulses['data']): ray_data = spectra.fields[field_name_in]['data'][ ray, :, 0:npuls].filled(0.) iq[ray, :, 0:npuls] = np.fft.ifft(np.fft.ifftshift(ray_data, axes=-1), axis=-1) * npuls if window is not None: wind = get_window(window, npuls) wind = wind / np.sqrt(np.sum(np.power(wind, 2.)) / npuls) wind = np.broadcast_to(np.atleast_2d(wind), (spectra.ngates, npuls)) iq[ray, :, 0:npuls] /= wind else: iq = np.ma.masked_all( (spectra.nrays, spectra.ngates, spectra.npulses_max), dtype=np.float32) for ray, npuls in enumerate(spectra.npulses['data']): iq[ray, :, 0:npuls] = spectra.fields[field_name_in]['data'][ ray, :, 0:npuls] * npuls field_dict = get_metadata(field_name_out) field_dict['data'] = iq radar.fields.update({field_name_out: field_dict}) return radar
def get_offset_features(features_matlab_path, songName, data_out, target_smear_width, frame_len, context_len, audio_offset, pitch_shift, boundaries): """Load feature data files of offset hop durations and append only the boundary-neighborhood frames to the dataset. To improve data class imbalance. Parameters: -- songName (string): name of the data file data_out (dict): features for default hop duration target_smear_width (int): size of boundary-neighborhood (in seconds) frame_len (float): duration of a frame (in seconds) context_len (int): number of +-context frames audio_offset (float): offset value (in seconds) added to audio signal pitch_shift (int): pitch shift (in +/-semitones) boundaries (numpy array or list): set of boundary positions for the song (in seconds) Returns: -- numpy ndarray: feature data for frames in the neighborhood """ data_offset = sio.loadmat( os.path.join( features_matlab_path, songName + '_' + str(audio_offset) + '_' + str(pitch_shift))) data_offset = make_feature_subsets(data_offset, context_len) for bound in boundaries: bound = int(bound.strip()) - audio_offset bound_frame = int(bound / frame_len) data_out['rhythm'] = np.vstack( (data_out['rhythm'], data_offset['rhythm'][bound_frame - int(target_smear_width / 2):bound_frame + int(target_smear_width / 2), :])) data_out['mfcc'] = np.vstack( (data_out['mfcc'], data_offset['mfcc'][bound_frame - int(target_smear_width / 2):bound_frame + int(target_smear_width / 2), :])) data_out['timbre'] = np.vstack( (data_out['timbre'], data_offset['timbre'][bound_frame - int(target_smear_width / 2):bound_frame + int(target_smear_width / 2), :])) data_out['all'] = np.vstack( (data_out['all'], data_offset['all'][bound_frame - int(target_smear_width / 2):bound_frame + int(target_smear_width / 2), :])) data_out['labels'] = np.vstack( (data_out['labels'], np.atleast_2d(windows.get_window('boxcar', target_smear_width)).T)) return data_out
def compute_spectra(radar, fields_in_list, fields_out_list, window=None): """ Computes the spectra from IQ data through a Fourier transform Parameters ---------- radar : radar object Object containing the IQ data fields_in_list : list of str list of input IQ data fields names fields_out_list : list of str list with the output spectra fields names obtained from the input fields window : string, tupple or None Parameters of the window used to obtain the spectra. The parameters are the ones corresponding to function scipy.signal.windows.get_window. If None no window will be used Returns ------- spectra : spectra radar object radar object containing the spectra fields """ spectra = deepcopy(radar) spectra.fields = {} for field_name_in, field_name_out in zip(fields_in_list, fields_out_list): if field_name_in in ('IQ_hh_ADU', 'IQ_vv_ADU'): spectrum = np.ma.masked_all( (radar.nrays, radar.ngates, radar.npulses_max), dtype=np.complex64) for ray, npuls in enumerate(radar.npulses['data']): wind_data = radar.fields[field_name_in]['data'][ ray, :, 0:npuls].filled(0.) if window is not None: wind = get_window(window, npuls) wind = wind/np.sqrt(np.sum(np.power(wind, 2.))/npuls) wind = np.broadcast_to( np.atleast_2d(wind), (radar.ngates, npuls)) wind_data *= wind spectrum[ray, :, 0:npuls] = np.fft.fftshift( np.fft.fft(wind_data, axis=-1)/npuls, axes=-1) else: spectrum = np.ma.masked_all( (radar.nrays, radar.ngates, radar.npulses_max), dtype=np.float32) for ray, npuls in enumerate(radar.npulses['data']): spectrum[ray, :, 0:npuls] = radar.fields[field_name_in]['data'][ ray, :, 0:npuls]/npuls field_dict = get_metadata(field_name_out) field_dict['data'] = spectrum spectra.fields.update({field_name_out: field_dict}) return spectra
def filter_spectrum(intensity: float, window="hanning", sigma=0.5): """ Apply a specified window function to a signal. The window functions are taken from the `signal.windows` module of SciPy, so check what is available before throwing it into this function. The window function is convolved with the signal by taking the time- domain product, and doing the inverse FFT to get the convolved spectrum back. The one exception is the gaussian window - if a user specifies "gaussian" for the window function, the actual window function applied here is a half gaussian, i.e. a 1D gaussian blur. Parameters ---------- dataframe: pandas DataFrame Pandas dataframe containing the spectral information int_col: str, optional Column name to reference the signal window: str, optional Name of the window function as implemented in SciPy. sigma: Returns ------- new_y: array_like Numpy 1D array containing the convolved signal """ if window not in dir(windows): raise Exception("Specified window not available in SciPy.") data_length = len(intensity) if window == "gaussian": x = np.arange(data_length) window = lineshapes.gaussian( x, 1., 0., sigma ) else: window = windows.get_window(window, data_length) fft_y = np.fft.fft(intensity) # Convolve the signal with the window function new_y = np.fft.ifft( window * fft_y ) # Return only the real part of the FFT new_y = np.abs(new_y) return new_y
def forward(self, signal, ndct=512, numoverlap=256, window='boxcar', MFCC=False): signal = np.array(signal) if MFCC == False: win = get_window(window, ndct) listdct = [] for i in range(0, signal.shape[0] - ndct, numoverlap): listdct.append(dct(signal[i:i + ndct] * win)) else: listdct = mfcc(signal, samplerate=512, winlen=1, winstep=0.5, nfft=ndct) return np.array(listdct)
def fourier_transform(dipole, dt, omega, max_harmonic=30, zero_padding_factor=10, window='blackman', t_phase=0, time_axis=0): '''Handles the Fourier transform for HHG with all the extra subtleties. 'dipole' is the time-dependent dipole moment (as a numpy array) 'dt' is the time step 'omega' is the reference frequency, to measure the harmonics against 'max_harmonic' is the highest frequency harmonic to return 'zero_padding_factor' is the factor by which the signal duration is increased via zero padding 'window' is the name of the window to use (see scipy.signal for the options) 't_phase' is the time relative to which the phase of the harmonics is measured. Typically the peak of the laser pulse. 'axis' is used when 'dipole' is a multidimensional array to indicate which axis is time This function returns both the frequencies (in units of omega) and the (complex) hhg spectrum. ''' #Total duration of the signal, after zero padding num_samples = dipole.shape[time_axis] t_max = zero_padding_factor * dt * num_samples #Spacing in frequency domain dw = 2 * np.pi / t_max #Frequencies in units of omega freq = np.arange(0, max_harmonic, dw / omega) #Calculate the dipole acceleration in time domain by taking the second derivative. dipole = np.gradient(np.gradient(dipole, dt, axis=time_axis), dt, axis=time_axis) #Multiply by the window function dipole *= get_window(window, num_samples) #Compute the Fourier transform with zero-padding spec = np.fft.rfft(dipole, n=zero_padding_factor * num_samples, axis=time_axis) #Shift the phase to be measured relative to 't_phase'. spec = multiply_along_axis(spec, np.exp(1j * t_phase * freq * omega)) #Truncate the spectrum at the max harmonic, and also remove #the first point to avoid division by zero later return freq[1:], spec[1:len(freq)]
def save_feats_labels(songdata, audio_offsets=[], pitch_shifts=[]): """ Generate frame-level labels from GT boundary annotations (binary: 1 for boundary, 0 for non-boundary) and save the features and labels together Parameters: -- _songdata (pd DataFrame): data from GT annotations csv file """ for songNo in range(songdata.shape[0]): boundaries = songdata['Boundaries'][songNo].split(',') data = sio.loadmat( os.path.join(features_matlab_path, songdata['Concert name'][songNo])) data_out = utils.make_feature_subsets(data, context_len) n_frames = data_out['all'].shape[0] smear_win = windows.get_window('boxcar', target_smear_width) boundLabels = np.zeros(n_frames) for i_bound in range(1, len(boundaries)): boundary = int(boundaries[i_bound].strip()) boundLabels[boundary // frame_len - target_smear_width // 2:boundary // frame_len + target_smear_width // 2] = smear_win data_out['labels'] = np.atleast_2d(boundLabels).T for pitch_shift in pitch_shifts: for audio_offset in audio_offsets: if (pitch_shift == 0) & (audio_offset == 0): continue data_out = utils.get_offset_features( features_matlab_path, songdata['Concert name'][songNo], data_out, target_smear_width, frame_len, context_len, audio_offset, pitch_shift, boundaries) np.save( os.path.join(features_RF_path, songdata['Concert name'][songNo] + '.npy'), data_out) return
def main(fname, window): d = "" with open(fname, 'r') as f: d = f.read() header, data = ReadKeyValueDataFile(d, header_char='#', delim = ':') header = dict(header) x, y = get_adjusted_values(header, data) sampling_rate = x[1]-x[0] window_obj = windows.get_window(window, len(x)) x = np.array(x*window_obj) f = scipy.fft.fft(y) sp = scipy.fft.fftshift(f) freq = scipy.fft.fftshift(scipy.fft.fftfreq(len(x), sampling_rate)) plt.plot(freq/1e6, np.abs(sp)) plt.xlabel("Frequency (MHz)") yunit = header['YUNIT'].strip("\"'") plt.ylabel("Amplitude ({})".format(yunit)) plt.xlim([0, max(freq)/1e6]) plt.yscale("log") plt.show()
def test_kaiser_float(self): win1 = windows.get_window(7.2, 64) win2 = windows.kaiser(64, 7.2, False) assert_allclose(win1, win2)
def CreateSegments(self, Window, nperseg, inputLenght): if nperseg > inputLenght: nperseg = inputLenght Window = get_window(Window, nperseg) return Window, nperseg
Description: Make sure the windowing effects are well-understood Date: 3/9/2021 Author: Hunter Akins Institution: Scripps Institution of Oceanography, UC San Diego """ from scipy.signal.windows import get_window window_len = 1024 N_fft = 4 * 8096 hamm = get_window('hamming', window_len) transfer_func = np.fft.fft(hamm) freqs = np.fft.fftfreq(window_len, 1 / 1500) transfer_func = np.square(abs(transfer_func)) transfer_func /= np.max(transfer_func) trans_db = 10 * np.log10(transfer_func) plt.figure() plt.plot(freqs, trans_db) hamm_pad = np.pad(hamm, (0, N_fft - window_len), 'constant') transfer_func = np.fft.fft(hamm_pad) freqs = np.fft.fftfreq(N_fft, 1 / 1500)
def test_cheb_even(self): with suppress_warnings() as sup: sup.filter(UserWarning, "This window is not suitable") w = windows.get_window(('chebwin', 40), 54, fftbins=False) assert_array_almost_equal(w, cheb_even_true, decimal=4)
def gen_dataset(features_out_path, texwin_len, context_len, targ_smear_width, audio_offsets=[0], pitch_shifts=[0]): smear_win = windows.get_window('boxcar', targ_smear_width) dataset_ids = {} dataset_labels = {} print('\n----------\n') for i_song in range(songdata.shape[0]): print('Generating data for song no %d' % i_song) dataset_ids[str(i_song)] = [] dataset_labels[str(i_song)] = {} boundaries = songdata['Boundaries'][i_song].split(',') for i_pitch in pitch_shifts: if i_pitch == 0: filepath = os.path.join( audio_dir, songdata['Concert name'][i_song] + '.wav') else: filepath = os.path.join( audio_dir, 'pitch_shifted', songdata['Concert name'][i_song] + '_' + str(i_pitch) + '.wav') for offset in audio_offsets: audio, sr = librosa.load(filepath, sr=16000) audio = audio[int(sr * offset):] features = utils.get_frame_level_melgrams(audio) audio = [] labels = np.zeros(features.shape[0]) for boundary in boundaries: boundary = int(boundary.strip()) - offset labels[int(boundary / hop_len_sub) - targ_smear_width // 2:int(boundary / hop_len_sub) + targ_smear_width // 2] = smear_win dataset_ids_temp = [] #IDs of this version of the audio labels_temp = [ ] #retaining labels only in boundary neighborhood for i_frame in range(features.shape[0]): ID = '%d_%d_%f_%d' % (i_song, i_pitch, offset, i_frame) mel_specgram_frame = features[i_frame, ] if (((i_pitch == 0) & (offset == 0)) | (labels[i_frame] > 0)): np.save( os.path.join(features_out_path, ID), np.reshape(mel_specgram_frame, (mel_specgram_frame.shape[0], mel_specgram_frame.shape[1], 1))) dataset_ids_temp.append(ID) labels_temp.append(labels[i_frame]) dataset_ids[str(i_song)].extend(dataset_ids_temp) dataset_labels[str(i_song)].update( dict(zip(dataset_ids_temp, labels_temp))) return dataset_ids, dataset_labels
def test_get_pcen_settings(): settings = get_pcen_settings() assert type(settings) == dict assert 'sr' in settings assert isinstance(settings['sr'], Real) assert settings['sr'] > 0 assert 'fmin' in settings assert isinstance(settings['fmin'], Real) assert settings['fmin'] > 0 assert 'fmax' in settings assert isinstance(settings['fmax'], Real) assert settings['fmax'] > settings['fmin'] assert settings['sr'] / 2.0 >= settings['fmax'] assert 'hop_length' in settings assert isinstance(settings['hop_length'], int) assert settings['hop_length'] > 0 assert 'n_fft' in settings assert isinstance(settings['n_fft'], int) assert settings['n_fft'] > 0 assert 'n_mels' in settings assert isinstance(settings['n_mels'], int) assert settings['n_fft'] > settings['n_mels'] > 0 assert 'pcen_delta' in settings assert isinstance(settings['pcen_delta'], float) assert settings['pcen_delta'] > 0 assert 'pcen_time_constant' in settings assert isinstance(settings['pcen_time_constant'], float) assert settings['pcen_time_constant'] > 0 assert 'pcen_norm_exponent' in settings assert isinstance(settings['pcen_norm_exponent'], float) assert settings['pcen_norm_exponent'] > 0 assert 'pcen_power' in settings assert isinstance(settings['pcen_power'], float) assert settings['pcen_power'] > 0 assert 'top_freq_id' in settings assert isinstance(settings['top_freq_id'], int) assert settings['top_freq_id'] > 0 assert 'win_length' in settings assert isinstance(settings['win_length'], int) assert settings['win_length'] > 0 assert 'n_hops' in settings assert isinstance(settings['n_hops'], int) assert settings['n_hops'] > 0 assert 'window' in settings assert isinstance(settings['window'], string_types) # Make sure window is valid get_window(settings['window'], 5)
def test_dpss(self): win1 = windows.get_window(('dpss', 3), 64, fftbins=False) win2 = windows.dpss(64, 3) assert_array_almost_equal(win1, win2, decimal=4)
def tocovdata(self, timeseries): """ Return auto covariance function from data. Return ------- acf : CovData1D object with attributes: data : ACF vector length L+1 args : time lags length L+1 sigma : estimated large lag standard deviation of the estimate assuming x is a Gaussian process: if acf[k]=0 for all lags k>q then an approximation of the variance for large samples due to Bartlett var(acf[k])=1/N*(acf[0]**2+2*acf[1]**2+2*acf[2]**2+ ..+2*acf[q]**2) for k>q and where N=length(x). Special case is white noise where it equals acf[0]**2/N for k>0 norm : bool If false indicating that auto_cov is not normalized Example: -------- >>> import wafo.data >>> import wafo.objects as wo >>> x = wafo.data.sea() >>> ts = wo.mat2timeseries(x) >>> acf = ts.tocovdata(150) h = acf.plot() """ lag = self.lag window = self.window detrend = self.detrend try: x = timeseries.data.flatten('F') dt = timeseries.sampling_period() except Exception: x = timeseries[:, 1:].flatten('F') dt = sampling_period(timeseries[:, 0]) if self.dt is not None: dt = self.dt if self.tr is not None: x = self.tr.dat2gauss(x) n = len(x) indnan = np.isnan(x) if any(indnan): x = x - x[1 - indnan].mean() Ncens = n - indnan.sum() x[indnan] = 0. else: Ncens = n x = x - x.mean() if hasattr(detrend, '__call__'): x = detrend(x) nfft = 2 ** nextpow2(n) raw_periodogram = abs(fft(x, nfft)) ** 2 / Ncens # ifft = fft/nfft since raw_periodogram is real! auto_cov = np.real(fft(raw_periodogram)) / nfft if self.flag.startswith('unbiased'): # unbiased result, i.e. divide by n-abs(lag) auto_cov = auto_cov[:Ncens] * Ncens / np.arange(Ncens, 1, -1) if self.norm: auto_cov = auto_cov / auto_cov[0] if lag is None: lag = self._estimate_lag(auto_cov, Ncens) lag = min(lag, n - 2) if isinstance(window, str) or type(window) is tuple: win = get_window(window, 2 * lag - 1) else: win = np.asarray(window) auto_cov[:lag] = auto_cov[:lag] * win[lag - 1::] auto_cov[lag] = 0 lags = slice(0, lag + 1) t = np.linspace(0, lag * dt, lag + 1) acf = CovData1D(auto_cov[lags], t) acf.sigma = np.sqrt(np.r_[0, auto_cov[0] ** 2, auto_cov[0] ** 2 + 2 * np.cumsum(auto_cov[1:] ** 2)] / Ncens) acf.children = [PlotData(-2. * acf.sigma[lags], t), PlotData(2. * acf.sigma[lags], t)] acf.plot_args_children = ['r:'] acf.norm = self.norm return acf
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
def _spectral_helper(x, y, fs=1.0, window='hann', nperseg=256, noverlap=None, nfft=None, detrend='constant', return_onesided=True, scaling='spectrum', axis=-1, mode='psd'): """ Calculate various forms of windowed FFTs for PSD, CSD, etc. This is a helper function that implements the commonality between the psd, csd, and spectrogram functions. It is not designed to be called externally. The windows are not averaged over; the result from each window is returned. Parameters --------- x : array_like Array or sequence containing the data to be analyzed. y : array_like Array or sequence containing the data to be analyzed. If this is the same object in memoery as x (i.e. _spectral_helper(x, x, ...)), the extra computations are spared. fs : float, optional Sampling frequency of the time series. 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 'hann'. 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 cross spectral density ('density') where `Pxy` has units of V**2/Hz and computing the cross spectrum ('spectrum') where `Pxy` has units of V**2, if `x` and `y` are measured in V and fs is measured in Hz. Defaults to 'density' axis : int, optional Axis along which the periodogram is computed; the default is over the last axis (i.e. ``axis=-1``). mode : str, optional Defines what kind of return values are expected. Options are ['psd', 'complex', 'magnitude', 'angle', 'phase']. Returns ------- freqs : ndarray Array of sample frequencies. t : ndarray Array of times corresponding to each data segment result : ndarray Array of output data, contents dependent on *mode* kwarg. References ---------- .. [1] Stack Overflow, "Rolling window for 1D arrays in Numpy?", http://stackoverflow.com/a/6811241 .. [2] Stack Overflow, "Using strides for an efficient moving average filter", http://stackoverflow.com/a/4947453 Notes ----- Adapted from matplotlib.mlab .. versionadded:: 0.16.0 """ from scipy import fftpack from scipy.signal import signaltools from scipy.signal.windows import get_window if mode not in ['psd', 'complex', 'magnitude', 'angle', 'phase']: raise ValueError("Unknown value for mode %s, must be one of: " "'default', 'psd', 'complex', " "'magnitude', 'angle', 'phase'" % mode) # If x and y are the same object we can save ourselves some computation. same_data = y is x if not same_data and mode != 'psd': raise ValueError("x and y must be equal if mode is not 'psd'") axis = int(axis) # Ensure we have np.arrays, get outdtype x = np.asarray(x) if not same_data: y = np.asarray(y) outdtype = np.result_type(x,y,np.complex64) else: outdtype = np.result_type(x,np.complex64) if not same_data: # Check if we can broadcast the outer axes together xouter = list(x.shape) youter = list(y.shape) xouter.pop(axis) youter.pop(axis) try: outershape = np.broadcast(np.empty(xouter), np.empty(youter)).shape except ValueError: raise ValueError('x and y cannot be broadcast together.') if same_data: if x.size == 0: return np.empty(x.shape), np.empty(x.shape), np.empty(x.shape) else: if x.size == 0 or y.size == 0: outshape = outershape + (min([x.shape[axis], y.shape[axis]]),) emptyout = np.rollaxis(np.empty(outshape), -1, axis) return emptyout, emptyout, emptyout if x.ndim > 1: if axis != -1: x = np.rollaxis(x, axis, len(x.shape)) if not same_data and y.ndim > 1: y = np.rollaxis(y, axis, len(y.shape)) # Check if x and y are the same length, zero-pad if necessary if not same_data: if x.shape[-1] != y.shape[-1]: if x.shape[-1] < y.shape[-1]: pad_shape = list(x.shape) pad_shape[-1] = y.shape[-1] - x.shape[-1] x = np.concatenate((x, np.zeros(pad_shape)), -1) else: pad_shape = list(y.shape) pad_shape[-1] = x.shape[-1] - y.shape[-1] y = np.concatenate((y, np.zeros(pad_shape)), -1) # X and Y are same length now, can test nperseg with either if x.shape[-1] < nperseg: warnings.warn('nperseg = {0:d}, is greater than input length = {1:d}, ' 'using nperseg = {1:d}'.format(nperseg, x.shape[-1])) nperseg = x.shape[-1] nperseg = int(nperseg) if nperseg < 1: raise ValueError('nperseg must be a positive integer') if nfft is None: nfft = nperseg elif nfft < nperseg: raise ValueError('nfft must be greater than or equal to nperseg.') else: nfft = int(nfft) if noverlap is None: noverlap = nperseg//2 elif noverlap >= nperseg: raise ValueError('noverlap must be less than nperseg.') else: noverlap = int(noverlap) # Handle detrending and window functions if not detrend: def detrend_func(d): return d elif not hasattr(detrend, '__call__'): def detrend_func(d): return signaltools.detrend(d, type=detrend, axis=-1) elif axis != -1: # Wrap this function so that it receives a shape that it could # reasonably expect to receive. def detrend_func(d): d = np.rollaxis(d, -1, axis) d = detrend(d) return np.rollaxis(d, axis, len(d.shape)) else: detrend_func = detrend 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] != nperseg: raise ValueError('window must have length of nperseg') if np.result_type(win,np.complex64) != outdtype: win = win.astype(outdtype) if mode == 'psd': 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) else: scale = 1 if return_onesided is True: if np.iscomplexobj(x): sides = 'twosided' else: sides = 'onesided' if not same_data: if np.iscomplexobj(y): sides = 'twosided' else: sides = 'twosided' if sides == 'twosided': num_freqs = nfft elif sides == 'onesided': if nfft % 2: num_freqs = (nfft + 1)//2 else: num_freqs = nfft//2 + 1 # Perform the windowed FFTs result = _fft_helper(x, win, detrend_func, nperseg, noverlap, nfft) result = result[..., :num_freqs] freqs = fftpack.fftfreq(nfft, 1/fs)[:num_freqs] if not same_data: # All the same operations on the y data result_y = _fft_helper(y, win, detrend_func, nperseg, noverlap, nfft) result_y = result_y[..., :num_freqs] result = np.conjugate(result) * result_y elif mode == 'psd': result = np.conjugate(result) * result elif mode == 'magnitude': result = np.absolute(result) elif mode == 'angle' or mode == 'phase': result = np.angle(result) elif mode == 'complex': pass result *= scale if sides == 'onesided': if nfft % 2: result[...,1:] *= 2 else: # Last point is unpaired Nyquist freq point, don't double result[...,1:-1] *= 2 t = np.arange(nperseg/2, x.shape[-1] - nperseg/2 + 1, nperseg - noverlap)/float(fs) if sides != 'twosided' and not nfft % 2: # get the last value correctly, it is negative otherwise freqs[-1] *= -1 # we unwrap the phase here to handle the onesided vs. twosided case if mode == 'phase': result = np.unwrap(result, axis=-1) result = result.astype(outdtype) # All imaginary parts are zero anyways if same_data and mode != 'complex': result = result.real # Output is going to have new last axis for window index if axis != -1: # Specify as positive axis index if axis < 0: axis = len(result.shape)-1-axis # Roll frequency axis back to axis where the data came from result = np.rollaxis(result, -1, axis) else: # Make sure window/time index is last axis result = np.rollaxis(result, -1, -2) return freqs, t, result
def energy_correction(window, n_samples): window = windows.get_window(window, n_samples) return len(window) / np.sum(window**2)
def timeseries_to_spectrogram(timeseries: Timeseries, fft_size: int = 1024, n_samples: int = 1024, n_records: int = None, synchronization_offset: int = 0, overlap: float = 0.0, window: str = None): """ # TODO: Introduce overlap + windowing Args: n_records: synchronization_offset: timeseries: fft_size: n_samples: overlap: window: str Returns: """ if overlap >= 1: raise ValueError("Overlap percentage must be less than 100%") if overlap < 0: raise ValueError("Overlap percentage must be equal or greater than 0%") total_samples = n_samples + synchronization_offset overlap_samples = n_samples * (1 - overlap) if n_records is None: n_records = int( ((len(timeseries.data) - total_samples) // overlap_samples)) + 1 start_samples = [ int(i * total_samples * (1 - overlap)) for i in range(n_records) ] time_data = np.zeros((fft_size, n_records), dtype=float) # Reshape the timeseries data into bins of n_time_samples: input_data = np.reshape(np.array( list( zip(*(timeseries.data['amplitude'][i:i + total_samples] for i in start_samples)))), (total_samples, n_records), order='F') # input_data = np.reshape(timeseries.data['amplitude'][:(n_records * total_samples)], (total_samples, n_records), # order='F') time_data[:n_samples, :] = input_data[:n_samples, :] if window is None: window = np.ones(n_samples) else: window = windows.get_window(window, n_samples) # Apply window time_data *= np.transpose(np.tile(window, (time_data.shape[1], 1))) spectrogram_data = fft.fft(time_data / timeseries.sample_rate, fft_size, axis=0) t_res = timeseries.duration() / (int( ((len(timeseries.data) - total_samples) // overlap_samples)) + 1) f_res = timeseries.sample_rate / fft_size return Spectrogram(spectrogram_data, f_res, t_res)
def resample(x, up, down, npad=100, window='boxcar', n_jobs=1, verbose=None): """Resample the array x Operates along the last dimension of the array. Parameters ---------- x : n-d array Signal to resample. up : float Factor to upsample by. down : float Factor to downsample by. npad : integer Number of samples to use at the beginning and end for padding. window : string or tuple See scipy.signal.resample for description. n_jobs : int | str Number of jobs to run in parallel. Can be 'cuda' if scikits.cuda is installed properly and CUDA is initialized. verbose : bool, str, int, or None If not None, override default verbose level (see mne.verbose). Returns ------- xf : array x resampled. Notes ----- This uses (hopefully) intelligent edge padding and frequency-domain windowing improve scipy.signal.resample's resampling method, which we have adapted for our use here. Choices of npad and window have important consequences, and the default choices should work well for most natural signals. Resampling arguments are broken into "up" and "down" components for future compatibility in case we decide to use an upfirdn implementation. The current implementation is functionally equivalent to passing up=up/down and down=1. """ # make sure our arithmetic will work ratio = float(up) / down x, orig_shape = _prep_for_filtering(x, False)[:2] x_len = x.shape[1] if x_len > 0: # prep for resampling now orig_len = x_len + 2 * npad # length after padding new_len = ratio * orig_len # length after resampling padded signal to_remove = np.round(ratio * npad).astype(int) # figure out windowing function if window is not None: if callable(window): W = window(fftfreq(orig_len)) elif isinstance(window, np.ndarray) and \ window.shape == (orig_len,): W = window else: W = ifftshift(get_window(window, orig_len)) else: W = np.ones(orig_len) W *= (float(new_len) / float(orig_len)) W = W.astype(np.complex128) # figure out if we should use CUDA n_jobs, cuda_dict, W = setup_cuda_fft_resample(n_jobs, W, new_len) # do the resampling using an adaptation of scipy's FFT-based resample() # use of the 'flat' window is recommended for minimal ringing if n_jobs == 1: y = np.zeros((len(x), new_len - 2 * to_remove), dtype=x.dtype) for xi, x_ in enumerate(x): y[xi] = fft_resample(x_, W, new_len, npad, to_remove, cuda_dict) else: if not isinstance(n_jobs, int): raise ValueError('n_jobs must be an integer, or "cuda"') parallel, p_fun, _ = parallel_func(fft_resample, n_jobs) y = parallel(p_fun(x_, W, new_len, npad, to_remove, cuda_dict) for x_ in x) y = np.array(y) # Restore the original array shape (modified for resampling) orig_shape = list(orig_shape) orig_shape[-1] = y.shape[1] y.shape = tuple(orig_shape) else: warnings.warn('x has zero length along last axis, returning a copy of ' 'x') y = x.copy() return y
def _weight_data_matrix(data_matrix, window_type, data_transformation=None): window_length_in_samp = data_matrix[0].shape[0] window = get_window(window_type, window_length_in_samp, fftbins=False) if (data_transformation == 'group_delay'): window *= np.arange(window_length_in_samp) return data_matrix * window
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
# window_data = {} figs = {} figs_response = {} # # Generate window data # for window in window_names: window_name = str(window) try: # Generate signal data window_data[window_name] = windows.get_window(window, Nx) except ValueError as e: # Call the function itself if it exists if window in windows.__all__: window_data[window_name] = getattr(windows, window)(Nx) elif type(window) is tuple and window[0] in windows.__all__: window_data[window_name] = getattr(windows, window[0])(Nx, *window[1:]) else: print(f"{window} failed: {e}") continue # Generate frequency response
def tocovdata(self, timeseries): ''' Return auto covariance function from data. Return ------- R : CovData1D object with attributes: data : ACF vector length L+1 args : time lags length L+1 sigma : estimated large lag standard deviation of the estimate assuming x is a Gaussian process: if R(k)=0 for all lags k>q then an approximation of the variance for large samples due to Bartlett var(R(k))=1/N*(R(0)^2+2*R(1)^2+2*R(2)^2+ ..+2*R(q)^2) for k>q and where N=length(x). Special case is white noise where it equals R(0)^2/N for k>0 norm : bool If false indicating that R is not normalized Example: -------- >>> import wafo.data >>> import wafo.objects as wo >>> x = wafo.data.sea() >>> ts = wo.mat2timeseries(x) >>> acf = ts.tocovdata(150) >>> h = acf.plot() ''' lag = self.lag window = self.window detrend = self.detrend try: x = timeseries.data.flatten('F') dt = timeseries.sampling_period() except Exception: x = timeseries[:, 1:].flatten('F') dt = sampling_period(timeseries[:, 0]) if not (self.dt is None): dt = self.dt if not (self.tr is None): x = self.tr.dat2gauss(x) n = len(x) indnan = np.isnan(x) if any(indnan): x = x - x[1 - indnan].mean() Ncens = n - indnan.sum() x[indnan] = 0. else: Ncens = n x = x - x.mean() if hasattr(detrend, '__call__'): x = detrend(x) nfft = 2 ** nextpow2(n) Rper = abs(fft(x, nfft)) ** 2 / Ncens # Raw periodogram R = np.real(fft(Rper)) / nfft # ifft = fft/nfft since Rper is real! if self.flag.startswith('unbiased'): # unbiased result, i.e. divide by n-abs(lag) R = R[:Ncens] * Ncens / np.arange(Ncens, 1, -1) if self.norm: R = R / R[0] if lag is None: lag = self._estimate_lag(R, Ncens) lag = min(lag, n - 2) if isinstance(window, str) or type(window) is tuple: win = get_window(window, 2 * lag - 1) else: win = np.asarray(window) R[:lag] = R[:lag] * win[lag - 1::] R[lag] = 0 lags = slice(0, lag + 1) t = np.linspace(0, lag * dt, lag + 1) acf = CovData1D(R[lags], t) acf.sigma = np.sqrt(np.r_[0, R[0] ** 2, R[0] ** 2 + 2 * np.cumsum(R[1:] ** 2)] / Ncens) acf.children = [PlotData(-2. * acf.sigma[lags], t), PlotData(2. * acf.sigma[lags], t)] acf.plot_args_children = ['r:'] acf.norm = self.norm return acf