Perform Descrete Fourier Tranfsom on 1D Noise Data. Allows you to calculate the Noise Density and the RMS Noise over a given bandwidth.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
6.2 KiB

  1. """
  2. This Code can calculate the Discrete Fourier Transformation (DFT) of a Noise Signal of different formats calculate the
  3. RMS Noise level over a given bandwidth bandwidth.
  4. __author__ = "Lenny Hoenen"
  5. __email__ = "l.hoenen@posteo.de", "lennart.hoenen@stud.uni-heidelberg.de"
  6. """
  7. import numpy as np
  8. import matplotlib.pyplot as plt
  9. from scipy.fft import rfft, rfftfreq
  10. from scipy.integrate import simps
  11. import scipy.io.wavfile as wavfile
  12. import json
  13. def Average_Fourier_Uniform(Data, Samplingrate):
  14. """
  15. Calculate the average FFT of multiple real, one dimensional signals with a given samplingrate and samplesize N.
  16. Returns the frequency range xf and the Linear Spectral Density of the signal over that range yf.
  17. Data is of the shape M x N, where M is the number of measurements to average and N is the Number of Data points in
  18. each measurement.
  19. The designation "Uniform" indicates, that no window function is used for the DFT. For Time Series consisting purely
  20. of noise, this should give the optimal results.
  21. """
  22. N = len(Data[0])
  23. T = 1/Samplingrate
  24. yf_av = []
  25. for i in Data:
  26. yf = rfft(i)
  27. yf = (2 * np.abs(yf)**2) / (Samplingrate * N) #Calculates the Power Spectral Density of one measurement/signal see Lenny's Thesis 3.2.4
  28. yf_av.append(yf)
  29. yf_av = np.sqrt(np.average(yf_av, axis=0)) #Average and convert from Power Spectral Density to Linear Spectral Density see Lenny's Thesis 3.2.4
  30. xf = rfftfreq(N, T)
  31. return xf, yf_av
  32. def Calc_Power_Noise_RMS(Frequencies, LSD, maxFreq):
  33. """
  34. Calculate the RMS Noise Power over a specified frequency range.
  35. As the impedance of the coils acts as a low pass filter, the current and thus the magnetic field cannot follow
  36. arbitrarily large frequencies. The Osci might measure high freq noise in the monitoring voltage, but this will hardly
  37. exist in the magnetic field. Therefore, one might want to limit the bandwidth to calculate the RMS noise in the current
  38. and thus in the magnetic field. The below functions allow to select a maximum Frequency (maxFreq) and calculate the RMS
  39. noise accordingly. LSD is the Linear Spectral Density as given by Average_Fourier_Uniform.
  40. For Discussions of the stability of a magnetic field, the Voltage Noise Density and its RMS value will be most
  41. meaningful. The result of the function Calc_Voltage_noise_RMS() is therefore more meaningful.
  42. See Lenny's Thesis 3.4
  43. The desired max. frequency might not be part of the list of discrete frequencies in the space of the DFT. Therefore
  44. a closest match is found and selected.
  45. """
  46. res = max(Frequencies)
  47. index = len(Frequencies)
  48. for i in range(len(Frequencies)):
  49. if res > np.abs(maxFreq - Frequencies[i]):
  50. res = np.abs(maxFreq - Frequencies[i])
  51. index = i
  52. print("You chose the maximum Frequency ", maxFreq, "Hz. The closest matching frequency contained in the DFT is ", Frequencies[index], "Hz with index ", index, ".")
  53. PowerNoiseRMS = simps(LSD[0:index]**2, Frequencies[0:index])
  54. return PowerNoiseRMS
  55. def Calc_Voltage_Noise_RMS(Frequencies, LSD, maxFreq):
  56. """
  57. See explanation of Calc_Power_Noise_RMS() and Lenny's Thesis 3.4.
  58. """
  59. PowerNoiseRMS = Calc_Power_Noise_RMS(Frequencies, LSD, maxFreq)
  60. VoltageNoiseRMS = np.sqrt(PowerNoiseRMS)
  61. return VoltageNoiseRMS
  62. """
  63. HERE IS JUST SOME DIFFERENT WAYS TO OPEN OSCI DATA:
  64. #OPEN wav files (wav is quite efficient and easy to handle for noise analysis)
  65. Samplingrate, Data = wavfile.read("Z:/Directory/File.wav")
  66. Data = Data * AbsoluteVoltageRange / NumberofBits
  67. With the Handyscope HS6Diff, the minimum Voltage Range was +/-200mV. Therefore the AbsoluteVoltageRange is 400mV. The
  68. highest resolution is 16Bit. Therefore the NumberofBits is 2**16
  69. #OPEN JSON files
  70. f = open("Z:/Directory/File.json")
  71. jdata = json.load(f)[0]
  72. joutput = jdata["outputs"]
  73. JDATA = joutput[0]["data"]
  74. f.close()
  75. JDATA = np.array(JDATA)
  76. #OPEN CSV files
  77. data = np.genfromtxt("Z:/Directory/file.csv", skip_header=9)
  78. data = data.T
  79. """
  80. """
  81. EXAMPLE:
  82. """
  83. samplingrateFinal, dataFinal = wavfile.read("Z:/Users/Lenny/Power Supply Mk.2/Noise Measurements/NoiseDensityHHCoil2ndPatchedChannelBatteryInput.wav")
  84. dataFinal = dataFinal*0.4/2**16 #Correct conversion from bit values to physical data, for the used Voltae range and precision.
  85. dataFinalshaped = np.reshape(dataFinal, [5,4000000]) #Shape one long continuous measurement into 5 shorter measurements to be averaged
  86. samplingrateprototype, dataprototype = wavfile.read("Z:/Users/Lenny/Power Supply Mk.2/Noise Measurements/NoiseDensityHHCoilPrototypeBatteryInput.wav")
  87. dataprototype = dataprototype*0.4/2**16
  88. dataprototypeshaped = np.reshape(dataprototype, [5,4000000])
  89. samplingratebackground, databackground = wavfile.read("Z:/Users/Lenny/Power Supply Mk.2/Noise Measurements/NoiseDensityHHCoilsFinalPowerSupplyWithPIBatteryInputBUTEVERYTHINGTURNEDOFF.wav")
  90. databackground = databackground*0.4/2**16
  91. databackgroundshaped = np.reshape(databackground, [5,4000000])
  92. x_Final_av, y_Final_av = Average_Fourier_Uniform(dataFinalshaped, samplingrateFinal)
  93. x_prototype_av, y_prototype_av = Average_Fourier_Uniform(dataprototypeshaped, samplingrateprototype)
  94. x_background_av, y_background_av = Average_Fourier_Uniform(databackgroundshaped, samplingratebackground)
  95. print(Calc_Voltage_Noise_RMS(x_Final_av, y_Final_av, 30000))
  96. print(Calc_Voltage_Noise_RMS(x_prototype_av, y_prototype_av, 30000))
  97. print(Calc_Voltage_Noise_RMS(x_background_av, y_background_av, 30000))
  98. plt.rcParams["figure.figsize"] = [7,5]
  99. plt.rcParams["font.size"] = 12
  100. plt.figure(2, figsize=[12,7])
  101. plt.loglog(x_background_av, y_background_av, linewidth=1, label="Background Noise in Lab", alpha=0.7)
  102. plt.loglog(x_prototype_av, y_prototype_av, linewidth=1, label="Noise Prototype in carton box", alpha=0.7)
  103. plt.loglog(x_Final_av, y_Final_av, linewidth=1, label="Noise Final Powersupply in carton box", alpha=0.7)
  104. #plt.loglog(x_DC1V_av, y_DC1V_av, linewidth=1, label="Noise with FreqGen and Shielding", alpha=0.7)
  105. plt.legend()
  106. plt.grid(which="major")
  107. plt.grid(which="minor", alpha=0.2)
  108. plt.xlabel("Frequency [Hz]")
  109. plt.ylabel(r"Voltage Noise Spectrum [$V/ \sqrt{Hz}$]")
  110. plt.title("5 Measurements of 4M Samples at 6.52MHz averaged")
  111. plt.show()
  112. plt.close()