def _setNFFT(self, NFFT): #if NFFT is changed, we need to redo the padding if self.__NFFT == NFFT and self.__NFFT is not None: #print 'NFFT is the same, nothing to do' return new_nfft = None if NFFT == 'nextpow2': #print 'NFFT is based on nextpow2:', n = nextpow2(self.data.size) new_nfft = int(pow(2, n)) elif NFFT is None: #print 'NFFT set to data length', new_nfft = self.N elif isinstance(NFFT, int): #print 'NNFT set manually to', assert NFFT > 0, 'NFFT must be a positive integer' new_nfft = NFFT else: raise ValueError( "NFFT must be either None, positive integer or 'nextpow2'") #print new_nfft if self.__NFFT != new_nfft: self.__NFFT = new_nfft # Now that the NFFT has changed, we need to update the range self._range.N = self.__NFFT self.__sides = self._default_sides() self.modified = True
def _setNFFT(self, NFFT):#if NFFT is changed, we need to redo the padding if self.__NFFT == NFFT and self.__NFFT is not None: #print 'NFFT is the same, nothing to do' return new_nfft = None if NFFT == 'nextpow2': #print 'NFFT is based on nextpow2:', n = nextpow2(self.data.size) new_nfft = int(pow(2,n)) elif NFFT is None: #print 'NFFT set to data length', new_nfft = self.N elif isinstance(NFFT, int): #print 'NNFT set manually to', assert NFFT > 0, 'NFFT must be a positive integer' new_nfft = NFFT else: raise ValueError("NFFT must be either None, positive integer or 'nextpow2'") #print new_nfft if self.__NFFT != new_nfft: self.__NFFT = new_nfft # Now that the NFFT has changed, we need to update the range self._range.N = self.__NFFT self.__sides = self._default_sides() self.modified = True
def compute_spectrum_multitaper(x, dt, max_freq=300, NFFT=None): fs = 1. / (dt / 1000.) fmax = fs / 2. len_x = len(x) if NFFT is None: NFFT = 2**nextpow2(len_x) # fft more efficient if power of 2 freq_vec = np.linspace(0, fmax, NFFT / 2) a = pmtm(x, NFFT=NFFT, NW=2.5, method='eigen', show=False) power_pyr = np.mean(abs(a[0])**2 * a[1], axis=0)[:int(NFFT / 2)] / len_x if max_freq is not None: freq_vec = freq_vec[np.where(freq_vec <= max_freq)] power_pyr = power_pyr[np.where(freq_vec <= max_freq)] return freq_vec, power_pyr
def compute_spectrogram_multitaper(x, dt, step_size=1, window_size=2**13, NW=2.5, freq_max=None, freq_min=None): fs = 1. / (dt / 1000.) fmax = fs / 2. len_x = len(x) NFFT = 2**nextpow2(len_x) # fft more efficient if power of 2 if NFFT is None: NFFT = window_size * 2 assert len_x > window_size n_segs = int(np.floor(len_x / float(step_size))) + 1 freq_vec = np.linspace(0, fmax, NFFT / 2) time_vec = np.arange( n_segs) * dt * step_size # midpoint of respective window window_size_half = int(np.floor(window_size / 2.)) x = np.pad(x, window_size_half, mode='reflect') spectrogram = np.zeros((len(freq_vec), n_segs)) for i in range(n_segs): x_window = x[i * step_size:i * step_size + window_size] # symmetric x_window -= np.mean(x_window) a = pmtm(x_window, NFFT=NFFT, NW=NW, method='eigen', show=False) power_window = np.mean(abs(a[0])**2 * a[1], axis=0)[:int(NFFT / 2)] / window_size spectrogram[:, i] = power_window if freq_max is not None: spectrogram = spectrogram[np.where(freq_vec <= freq_max)[0], :] freq_vec = freq_vec[np.where(freq_vec <= freq_max)] if freq_min is not None: spectrogram = spectrogram[np.where(freq_vec > freq_min)[0], :] freq_vec = freq_vec[np.where(freq_vec > freq_min)] return spectrogram, freq_vec, time_vec
def pmtm(x, NW=None, k=None, NFFT=None, e=None, v=None, method='adapt', show=True): """Multitapering spectral estimation :param array x: the data :param float NW: The time half bandwidth parameter (typical values are 2.5,3,3.5,4). Must be provided otherwise the tapering windows and eigen values (outputs of dpss) must be provided :param int k: uses the first k Slepian sequences. If *k* is not provided, *k* is set to *NW*2*. :param NW :param e: the matrix containing the tapering windows :param v: the window concentrations (eigenvalues) :param str method: set how the eigenvalues are used. Must be in ['unity', 'adapt', 'eigen'] :param bool show: plot results Usually in spectral estimation the mean to reduce bias is to use tapering window. In order to reduce variance we need to average different spectrum. The problem is that we have only one set of data. Thus we need to decompose a set into several segments. Such method are well-known: simple daniell's periodogram, Welch's method and so on. The drawback of such methods is a loss of resolution since the segments used to compute the spectrum are smaller than the data set. The interest of multitapering method is to keep a good resolution while reducing bias and variance. How does it work? First we compute different simple periodogram with the whole data set (to keep good resolution) but each periodgram is computed with a different tapering windows. Then, we average all these spectrum. To avoid redundancy and bias due to the tapers mtm use special tapers. .. plot:: :width: 80% :include-source: from spectrum import * data = data_cosine(N=2048, A=0.1, sampling=1024, freq=200) # If you already have the DPSS windows [tapers, eigen] = dpss(2048, 2.5, 4) res = pmtm(data, e=tapers, v=eigen, show=False) # You do not need to compute the DPSS before end res = pmtm(data, NW=2.5, show=False) res = pmtm(data, NW=2.5, k=4, show=True) """ assert method in ['adapt','eigen','unity'] N = len(x) # if dpss not provided, compute them if e is None and v is None: if NW != None: [tapers, eigenvalues] = dpss(N, NW, k=k) else: raise ValueError("NW must be provided (e.g. 2.5, 3, 3.5, 4") elif e != None and v != None: eigenvalues = v[:] tapers = e[:] else: raise ValueError("if e provided, v must be provided as well and viceversa.") nwin = len(eigenvalues) # length of the eigen values vector to be used later # set the NFFT if NFFT==None: NFFT = max(256, 2**nextpow2(N)) # si nfft smaller thqn N, cut otherwise add wero. # compute if method in ['eigen', 'unity']: if method == 'unity': weights = np.ones((nwin, 1)) elif method == 'eigen': # The S_k spectrum can be weighted by the eigenvalues, as in Park et al. weights = np.array([_x/float(i+1) for i,_x in enumerate(eigenvalues)]) weights = weights.reshape(nwin,1) Sk = abs(np.fft.fft(np.multiply(tapers.transpose(), x), NFFT))**2 Sk = np.mean(Sk * weights, axis=0) elif method == 'adapt': # This version uses the equations from [2] (P&W pp 368-370). # Wrap the data modulo nfft if N > nfft sig2 = np.dot(x, x) / float(N) Sk = abs(np.fft.fft(np.multiply(tapers.transpose(), x), NFFT))**2 Sk = Sk.transpose() S = (Sk[:,0] + Sk[:,1]) / 2 # Initial spectrum estimate S = S.reshape(NFFT, 1) Stemp = np.zeros((NFFT,1)) S1 = np.zeros((NFFT,1)) # Set tolerance for acceptance of spectral estimate: tol = 0.0005 * sig2 / float(NFFT) i = 0 a = sig2 * (1 - eigenvalues) # converges very quickly but for safety; set i<100 while sum(np.abs(S-S1))/NFFT > tol and i<100: i = i + 1 # calculate weights b1 = np.multiply(S, np.ones((1,nwin))) b2 = np.multiply(S,eigenvalues.transpose()) + np.ones((NFFT,1))*eigenvalues.transpose() b = b1/b2 # calculate new spectral estimate wk=(b**2)*(np.ones((NFFT,1))*eigenvalues.transpose()) S1 = sum(wk.transpose()*Sk.transpose())/ sum(wk.transpose()) S1 = S1.reshape(NFFT, 1) Stemp = S1 S1 = S S = Stemp # swap S and S1 Sk = S #clf(); p.plot(); plot(arange(0,0.5,1./512),20*log10(res[0:256])) if show==True: semilogy(Sk) return Sk
def pmtm(x, NW=None, k=None, NFFT=None, e=None, v=None, method="adapt", show=True): """Multitapering spectral estimation :param array x: the data :param float NW: The time half bandwidth parameter (typical values are 2.5,3,3.5,4). Must be provided otherwise the tapering windows and eigen values (outputs of dpss) must be provided :param int k: uses the first k Slepian sequences. If *k* is not provided, *k* is set to *NW*2*. :param NW :param e: the matrix containing the tapering windows :param v: the window concentrations (eigenvalues) :param str method: set how the eigenvalues are used. Must be in ['unity', 'adapt', 'eigen'] :param bool show: plot results Usually in spectral estimation the mean to reduce bias is to use tapering window. In order to reduce variance we need to average different spectrum. The problem is that we have only one set of data. Thus we need to decompose a set into several segments. Such method are well-known: simple daniell's periodogram, Welch's method and so on. The drawback of such methods is a loss of resolution since the segments used to compute the spectrum are smaller than the data set. The interest of multitapering method is to keep a good resolution while reducing bias and variance. How does it work? First we compute different simple periodogram with the whole data set (to keep good resolution) but each periodgram is computed with a different tapering windows. Then, we average all these spectrum. To avoid redundancy and bias due to the tapers mtm use special tapers. .. plot:: :width: 80% :include-source: from spectrum import * data = data_cosine(N=2048, A=0.1, sampling=1024, freq=200) # If you already have the DPSS windows [tapers, eigen] = dpss(2048, 2.5, 4) res = pmtm(data, e=tapers, v=eigen, show=False) # You do not need to compute the DPSS before end res = pmtm(data, NW=2.5, show=False) res = pmtm(data, NW=2.5, k=4, show=True) """ assert method in ["adapt", "eigen", "unity"] N = len(x) # if dpss not provided, compute them if e is None and v is None: if NW != None: [tapers, eigenvalues] = dpss(N, NW, k=k) else: raise ValueError("NW must be provided (e.g. 2.5, 3, 3.5, 4") elif e != None and v != None: eigenvalues = v[:] tapers = e[:] else: raise ValueError("if e provided, v must be provided as well and viceversa.") nwin = len(eigenvalues) # length of the eigen values vector to be used later # set the NFFT if NFFT == None: NFFT = max(256, 2 ** nextpow2(N)) # si nfft smaller thqn N, cut otherwise add wero. # compute if method in ["eigen", "unity"]: if method == "unity": weights = np.ones((nwin, 1)) elif method == "eigen": # The S_k spectrum can be weighted by the eigenvalues, as in Park et al. weights = np.array([_x / float(i + 1) for i, _x in enumerate(eigenvalues)]) weights = weights.reshape(nwin, 1) Sk = abs(np.fft.fft(np.multiply(tapers.transpose(), x), NFFT)) ** 2 Sk = np.mean(Sk * weights, axis=0) elif method == "adapt": # This version uses the equations from [2] (P&W pp 368-370). # Wrap the data modulo nfft if N > nfft sig2 = np.dot(x, x) / float(N) Sk = abs(np.fft.fft(np.multiply(tapers.transpose(), x), NFFT)) ** 2 Sk = Sk.transpose() S = (Sk[:, 0] + Sk[:, 1]) / 2 # Initial spectrum estimate S = S.reshape(NFFT, 1) Stemp = np.zeros((NFFT, 1)) S1 = np.zeros((NFFT, 1)) # Set tolerance for acceptance of spectral estimate: tol = 0.0005 * sig2 / float(NFFT) i = 0 a = sig2 * (1 - eigenvalues) # converges very quickly but for safety; set i<100 while sum(np.abs(S - S1)) / NFFT > tol and i < 100: i = i + 1 # calculate weights b1 = np.multiply(S, np.ones((1, nwin))) b2 = np.multiply(S, eigenvalues.transpose()) + np.ones((NFFT, 1)) * eigenvalues.transpose() b = b1 / b2 # calculate new spectral estimate wk = (b ** 2) * (np.ones((NFFT, 1)) * eigenvalues.transpose()) S1 = sum(wk.transpose() * Sk.transpose()) / sum(wk.transpose()) S1 = S1.reshape(NFFT, 1) Stemp = S1 S1 = S S = Stemp # swap S and S1 Sk = S # clf(); p.plot(); plot(arange(0,0.5,1./512),20*log10(res[0:256])) if show == True: semilogy(Sk) return Sk
# method = 'linear' # idx_u = np.isnan(km.u) # km.u[idx_u] = interp1d(t[~idx_u], km.u[~idx_u], kind=method, fill_value='extrapolate') #plt.figure() #plt.plot(t,km.u) #plt.show(block=False) # Post-processing of the data km.ref_u = u[8,8,:] # referebce point, hub centre u component km.ref_v = v[8,8,:] # reference point, v omp km.ref_w = w[8,8,:] # ref point, w comp # Measure the spectral densities, coherence and decay factor km.win = 2**7 km.noverlap = km.win*3/4 km.Nfft = 2**nextpow2(km.ref_u.size) km.dt = np.nanmean(np.diff(t)) km.Fs = 1/km.dt km.T = t[-1] km.t = np.arange(km.dt,km.T,km.dt) km.N = u.size km.k = np.fix((km.N-km.noverlap)/(km.win-km.noverlap)) km.df = 1/km.T km.f = (np.arange(km.t.size)/2)*km.df km.kw = 2*math.pi*km.f/np.nanmean(u) km.Spectrum = 'twosided' km.W = np.fft.fft(np.hamming(km.win)) km.WW = km.W*km.W.conj() km.KMU = sum(km.WW)*km.k*(1/km.win)*km.Fs # Statistics of the turbulent field
def pmtm(x, NW=None, k=None, NFFT=None, e=None, v=None, method='adapt', show=False): """Multitapering spectral estimation :param array x: the data :param float NW: The time half bandwidth parameter (typical values are 2.5,3,3.5,4). Must be provided otherwise the tapering windows and eigen values (outputs of dpss) must be provided :param int k: uses the first k Slepian sequences. If *k* is not provided, *k* is set to *NW*2*. :param NW: :param e: the window concentrations (eigenvalues) :param v: the matrix containing the tapering windows :param str method: set how the eigenvalues are used when weighting the results. Must be in ['unity', 'adapt', 'eigen']. see below for details. :param bool show: plot results :return: Sk (complex), weights, eigenvalues Usually in spectral estimation the mean to reduce bias is to use tapering window. In order to reduce variance we need to average different spectrum. The problem is that we have only one set of data. Thus we need to decompose a set into several segments. Such method are well-known: simple daniell's periodogram, Welch's method and so on. The drawback of such methods is a loss of resolution since the segments used to compute the spectrum are smaller than the data set. The interest of multitapering method is to keep a good resolution while reducing bias and variance. How does it work? First we compute different simple periodogram with the whole data set (to keep good resolution) but each periodgram is computed with a different tapering windows. Then, we average all these spectrum. To avoid redundancy and bias due to the tapers mtm use special tapers. Method can be eigen, unity or adapt. If *unity*, weights are set to 1. If *eigen* are proportional to the eigen-values. If *adapt*, equations from [2] (P&W pp 368-370) are used. The output is made of 2 matrices called *Sk* and *weights*. The third item stored the eigenvalues. The two matrices have dimensions equal to the number of windows used multiplied by the number of input points. The first matrix stored the spectral results while the second stores the weights. Would you wish to plot the spectrum, you will have to take the means of the different windows and weight down the results before mean(Sk * weigths). Please see the code for details. .. plot:: :width: 80% :include-source: from spectrum import data_cosine, dpss, pmtm data = data_cosine(N=2048, A=0.1, sampling=1024, freq=200) # If you already have the DPSS windows [tapers, eigen] = dpss(2048, 2.5, 4) res = pmtm(data, e=eigen, v=tapers, show=False) # You do not need to compute the DPSS before end res = pmtm(data, NW=2.5, show=False) res = pmtm(data, NW=2.5, k=4, show=True) .. versionchanged:: 0.6.2 APN modified method to return each Sk as complex values, the eigenvalues and the weights """ assert method in ['adapt', 'eigen', 'unity'] N = len(x) # if dpss not provided, compute them if e is None and v is None: if NW is not None: [tapers, eigenvalues] = dpss(N, NW, k=k) else: raise ValueError("NW must be provided (e.g. 2.5, 3, 3.5, 4") elif e is not None and v is not None: eigenvalues = e[:] tapers = v[:] else: raise ValueError( "if e provided, v must be provided as well and viceversa.") nwin = len( eigenvalues) # length of the eigen values vector to be used later # set the NFFT if NFFT == None: NFFT = max(256, 2**nextpow2(N)) Sk_complex = np.fft.fft(np.multiply(tapers.transpose(), x), NFFT) Sk = abs(Sk_complex)**2 # si nfft smaller thqn N, cut otherwise add wero. # compute if method in ['eigen', 'unity']: if method == 'unity': weights = np.ones((nwin, 1)) elif method == 'eigen': # The S_k spectrum can be weighted by the eigenvalues, as in Park et al. weights = np.array( [_x / float(i + 1) for i, _x in enumerate(eigenvalues)]) weights = weights.reshape(nwin, 1) elif method == 'adapt': # This version uses the equations from [2] (P&W pp 368-370). # Wrap the data modulo nfft if N > nfft sig2 = np.dot(x, x) / float(N) Sk = abs(np.fft.fft(np.multiply(tapers.transpose(), x), NFFT))**2 Sk = Sk.transpose() S = (Sk[:, 0] + Sk[:, 1]) / 2 # Initial spectrum estimate S = S.reshape(NFFT, 1) Stemp = np.zeros((NFFT, 1)) S1 = np.zeros((NFFT, 1)) # Set tolerance for acceptance of spectral estimate: tol = 0.0005 * sig2 / float(NFFT) i = 0 a = sig2 * (1 - eigenvalues) wk = np.ones((NFFT, 1)) * eigenvalues.transpose() # converges very quickly but for safety; set i<100 while sum(np.abs(S - S1)) / NFFT > tol and i < 100: i = i + 1 # calculate weights b1 = np.multiply(S, np.ones((1, nwin))) b2 = np.multiply(S, eigenvalues.transpose()) + np.ones( (NFFT, 1)) * a.transpose() b = b1 / b2 # calculate new spectral estimate wk = (b**2) * (np.ones((NFFT, 1)) * eigenvalues.transpose()) S1 = sum(wk.transpose() * Sk.transpose()) / sum(wk.transpose()) S1 = S1.reshape(NFFT, 1) S, S1 = S1, S # swap S and S1 weights = wk if show is True: print("""To plot the spectrum please use Multitapering class instead of pmtm. Same syntax but more correct plot. This plotting functionality is kept for book-keeping but lacks sampling option, and amplitude is not correct.""") from pylab import semilogy if method == "adapt": Sk = np.mean(Sk * weights, axis=1) else: Sk = np.mean(Sk * weights, axis=0) semilogy(Sk) return Sk_complex, weights, eigenvalues