Advertisement
Guest User

Untitled

a guest
Jun 18th, 2019
74
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.42 KB | None | 0 0
  1. import numpy as np
  2. from scipy import signal
  3. import matplotlib.pyplot as plt
  4. np.random.seed(1234)
  5.  
  6. # Generate a test signal (a 2 Vrms) sine wave at 1000 Hz and a second one a 1500 Hz, corrupted by
  7. # 0.001 V**2/Hz of white noise sampled at 7.5 kHz
  8.  
  9. fs = 7.5e3
  10. N = 500
  11. amp = 2*np.sqrt(2)
  12. freq=1000
  13. noise_power = 0.001 * fs /2
  14. time = np.arange(N) / fs
  15. data = amp* np.sin(2*np.pi*freq*time) + 0.7*amp* np.sin(2*np.pi*1.5*freq*time)
  16. data += np.random.normal(scale=np.sqrt(noise_power),size=time.shape)
  17.  
  18. # Welch estimate parameters
  19. segment_size = np.int32(0.5*N) # Segment size = 50 % of data length
  20. overlap_fac = 0.5
  21. overlap_size = overlap_fac*segment_size
  22. fft_size = 512
  23. detrend = True # If true, removes signal mean
  24. scale_by_freq = True
  25.  
  26. # Frequency resolution
  27. fres = fs/segment_size
  28.  
  29. ## Welch function
  30. f, PSD_welch = signal.welch(data, fs,window='hann', nperseg=segment_size,noverlap=overlap_size,nfft=fft_size,return_onesided=True,detrend='constant', average='median')
  31.  
  32.  
  33. ## Own implementation
  34. # PSD size = N/2 + 1
  35. PSD_size = np.int32(fft_size/2)+1
  36.  
  37.  
  38. # Number of segments
  39. baseSegment_number = np.int32(len(data)/segment_size) # Number of initial segments
  40. total_segments = np.int32(baseSegment_number + ((1-overlap_fac)**(-1) - 1 ) * (baseSegment_number - 1)) # No. segments including overlap
  41. window = signal.hann(segment_size) # Hann window
  42.  
  43. if scale_by_freq:
  44. # Scale the spectrum by the norm of the window to compensate for
  45. # windowing loss; see Bendat & Piersol Sec 11.5.2.
  46. S2 = np.sum((window)**2)
  47. else:
  48. # In this case, preserve power in the segment, not amplitude
  49. S2 = (np.sum(window))**2
  50.  
  51. fft_segment = np.empty((total_segments,fft_size),dtype=np.complex64)
  52. for i in range(total_segments):
  53. offset_segment = np.int32(i* (1-overlap_fac)*segment_size)
  54. current_segment = data[offset_segment:offset_segment+segment_size]
  55. # Detrend (Remove mean value)
  56. if detrend :
  57. current_segment = current_segment - np.mean(current_segment)
  58. windowed_segment = np.multiply(current_segment,window)
  59. fft_segment[i] = np.fft.fft(windowed_segment,fft_size) # fft automatically pads if n<nfft
  60.  
  61. # Add FFTs of different segments
  62. fft_sum = np.zeros(fft_size,dtype=np.complex64)
  63. for segment in fft_segment:
  64. fft_sum += segment
  65.  
  66. # Signal manipulation factors
  67.  
  68. # Normalization including window effect on power
  69. powerDensity_normalization = 1/S2
  70. # Averaging decreases FFT variance
  71. powerDensity_averaging = 1/total_segments
  72. # Transformation from Hz.s to Hz spectrum
  73. if scale_by_freq:
  74. powerDensity_transformation = 1/fs
  75. else:
  76. powerDensity_transformation = 1
  77.  
  78. # Make oneSided estimate 1st -> N+1st element
  79. fft_WelchEstimate_oneSided = fft_sum[0:PSD_size]
  80.  
  81. # Convert FFT values to power density in U**2/Hz
  82. PSD_own = np.square(abs(fft_WelchEstimate_oneSided)) * powerDensity_averaging * powerDensity_normalization * powerDensity_transformation
  83. # Double frequencies except DC and Nyquist
  84. PSD_own[2:PSD_size-1] *= 2
  85. fft_freq = np.fft.fftfreq(fft_size,1/fs)
  86. freq = fft_freq[0:PSD_size]
  87. # Take absolute value of Nyquist frequency (negative using np.fft.fftfreq)
  88. freq[-1] = np.abs(freq[-1])
  89.  
  90. PSD_welch_dB = 10 * np.log10(PSD_welch) # Scale to dB
  91. PSD_own_dB = 10 * np.log10(PSD_own) # Scale to dB
  92.  
  93. plot = True
  94. ## Plots
  95. if plot:
  96.  
  97. plt.plot(f, PSD_welch,label='Welch function')
  98. plt.plot(freq,PSD_own,label='Own implementation')
  99. plt.ylim([0,0.15])
  100. plt.xlim([0, fs/2])
  101. plt.xlabel('frequency [Hz]')
  102. plt.ylabel('PSD [dB]')
  103. plt.legend()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement