问题
I have a signal spectrum PSD that looks like :
The frequency range of the PSD is np.linspace(0,2,500). I want to convert this spectrum into a time series of 600s . The code is shown below:
def spectrumToSeries(timeSeries,frequency,psdLoad):
'''
Function that gicen a PSD converts into a time series
'''
#
#Obtian interval frequency
df=frequency[2]-frequency[1]
#Obtian the spectrum amplitudes
amplitude=np.sqrt(2*np.array(psdLoad)*df)
#Pre allocation of matrices
epsilon=np.zeros((len(amplitude)))
randomSeries=np.zeros((len(amplitude)))
#Create time series from spectrum
#Generate random phases between [-2pi,2pi]
epsilon=-np.pi + 2*np.pi*np.random.randn(1,len(amplitude))
#Inverse Fourier
randomSeries=len(timeSeries)*np.real(np.fft.ifft(amplitude*np.exp(epsilon*1j*2*np.pi))));
return randomSeries
However my end result looks like:
timeSeries = spectrumToSeries(thrustBladed,param.frequency,analyticalThrustPSD[iwind])
The x axis is refering the number of points of the time series. However, the time series should be of 600s. Any help? Thanks
回答1:
The result of your function "spectrumToSeries" is the same length as the array you give in the np.fft.ifft. Because the ifft function returns an array of the same length as the input. So, because your initial psdLoad array has 500 elements, the "amplitude" array is 500 elements long too, and so as the randomSeries one, which is your function's result.
I don't really get the different inputs of your function. What is the first argument called timeSeries ? Is it an empty matrix of 600 elements awaiting for the result of the function ?
I am trying to compute time series from PSD myself so I'd love to see your function give a good result !
I think that if you want your time series to be 600 elements, you need to have a "frequency" and a "psdLoad" array of 600 elements. So what I am trying to do with my set of data is to fit my psdLoad with a function (psdLoad = f (frequency)). Then I can set the size of my arrays to the length of the timeseries I want at the end, and compute the ifft...
My own data is a record at 1Hz, over a day, so arrays of 86400 elements. I have to apply a filter to it, using a method with PSD. So I compute my PSD, which length is 129 elements, and once I have filtered it I want to end up with my filtered time series.
here is my code :
######################################################################"
## Computation of spectrum values : PSD & frequency ##
######################################################################"
psd_ampl0, freq = mlab.psd(Up13_7april, NFFT=256, Fs=1, detrend=mlab.detrend_linear, window=mlab.window_hanning, noverlap=0.5, sides='onesided')
################################################"
## Computation of the time series from the PSD ##
################################################"
def PSDToSeries(lenTimeSeries,freq,psdLoad):
'''
Function that gicen a PSD converts into a time series
'''
#
#Obtian interval frequency
df=freq[2]-freq[1]
print('df = ', df)
#Obtian the spectrum amplitudes
amplitude=(2*psdLoad*df)**0.5
#Pre allocation of matrices
epsilon=np.zeros((len(amplitude)))
randomSeries=np.zeros((len(amplitude)))
#Create time series from spectrum
#Generate random phases between [-2pi,2pi]
epsilon=-np.pi + 2*np.pi*np.random.randn(1,len(amplitude))
#Inverse Fourier
randomSeries=lenTimeSeries*np.real(np.fft.ifft(amplitude*np.exp(epsilon*1j*2*np.pi)));
return randomSeries
#-------------------------------------------------------------------------
#########################################################"
## Fitting a function on the PSD to add it more points ##
#########################################################"
#def fitting_function(freq,a,b,c,d,e,f):
#return a*(freq**5)+b*(freq**4)+c*(freq**3)+d*(freq**2)+e*freq+f
def fitting_function(freq,a,b,c):
return a*np.exp(freq*b)
# Look for the best fitting parameters of the choosen fitting function #
param_opt, pcov = optim.curve_fit(fitting_function,freq[1:],psd_ampl0[1:])
print('The best fitting parameters are : ',param_opt)
# Definition of the new PSD and frequency arrays extended to 86400 elements #
freq_extend = np.linspace(min(freq),max(freq), 86400)
psd_extend = fitting_function(freq_extend,param_opt[0], param_opt[1], param_opt[2])
#print(psd_allonge)
ts_length = Up13_7april.shape[0] #Length of the timeSeries I want to compute
print('ts_length = ', ts_length)
tsFromPSD = PSDToSeries(ts_length, freq_allonge, psd_allonge)
print('shape tsFromPSD : ', tsFromPSD.shape)
##################"
## Plot section ##
##################"
plt.figure(1)
plt.plot(freq[1:] ,psd_ampl0[1:],marker=',', ls='-',color='SkyBlue', label='original PSD')
plt.plot(freq_allonge, psd_allonge, marker=',', ls='-',color='DarkGreen', label='PSD rallonge')
plt.xlabel('Frequency [Hz]')
plt.ylabel('PSD of raw velocity module [(m/s)²/Hz]')
plt.grid(True)
plt.legend()
plt.figure(2)
plt.plot_date(time7april,Up13_7april, xdate=True, ydate=False, marker=',', ls='-', c='Grey', label='Original Signal')
plt.plot_date(time7april, tsFromPSD[0],xdate=True, ydate=False, marker=',', ls='-', label='After inverse PSD')
plt.suptitle('Original and Corrected time series for the 7th of April')
plt.grid(True)
plt.legend()
plt.show()
The array Up13_7april, is my initial time series, in this code I am just trying to compute the PSD and then come back to a time serie to compare the original signal and the final one. Here is the result :
[Sorry can't post any picture because I'm new to stackoverflow]
So my process is to find a function that fits the PSD. I use the Python scipy function called "optimize.curve_fit". It just gives you the best parameters to fit your data with a function that you provide. Once I have my parameters, I create new PSD and frequency arrays, of 86400 elements. And finally I use your "PSDToSeries" function to compute the timeSeries.
I'm quite happy with the result... I think I just need to find a better fit of my PSD :
[Sorry can't post any picture because I'm new to stackoverflow]
Any idea ?
来源:https://stackoverflow.com/questions/29095386/compute-time-series-from-psd-python