@@ -23,6 +23,7 from numpy import NaN | |||||
23 | from scipy.optimize.optimize import OptimizeWarning |
|
23 | from scipy.optimize.optimize import OptimizeWarning | |
24 | warnings.filterwarnings('ignore') |
|
24 | warnings.filterwarnings('ignore') | |
25 |
|
25 | |||
|
26 | import matplotlib.pyplot as plt | |||
26 |
|
27 | |||
27 | SPEED_OF_LIGHT = 299792458 |
|
28 | SPEED_OF_LIGHT = 299792458 | |
28 |
|
29 | |||
@@ -807,10 +808,10 class PrecipitationProc(Operation): | |||||
807 | class FullSpectralAnalysis(Operation): |
|
808 | class FullSpectralAnalysis(Operation): | |
808 |
|
809 | |||
809 | """ |
|
810 | """ | |
810 |
Function that implements Full Spectral Anal |
|
811 | Function that implements Full Spectral Analysis technique. | |
811 |
|
812 | |||
812 | Input: |
|
813 | Input: | |
813 |
self.dataOut.data_pre : SelfSpectra and CrossS |
|
814 | self.dataOut.data_pre : SelfSpectra and CrossSpectra data | |
814 | self.dataOut.groupList : Pairlist of channels |
|
815 | self.dataOut.groupList : Pairlist of channels | |
815 | self.dataOut.ChanDist : Physical distance between receivers |
|
816 | self.dataOut.ChanDist : Physical distance between receivers | |
816 |
|
817 | |||
@@ -823,15 +824,15 class FullSpectralAnalysis(Operation): | |||||
823 | Parameters affected: Winds, height range, SNR |
|
824 | Parameters affected: Winds, height range, SNR | |
824 |
|
825 | |||
825 | """ |
|
826 | """ | |
826 | def run(self, dataOut, Xi01=None, Xi02=None, Xi12=None, Eta01=None, Eta02=None, Eta12=None, SNRlimit=7): |
|
827 | def run(self, dataOut, Xi01=None, Xi02=None, Xi12=None, Eta01=None, Eta02=None, Eta12=None, SNRlimit=7, minheight=None, maxheight=None): | |
827 |
|
828 | |||
828 | self.indice=int(numpy.random.rand()*1000) |
|
829 | self.indice=int(numpy.random.rand()*1000) | |
829 |
|
830 | |||
830 | spc = dataOut.data_pre[0].copy() |
|
831 | spc = dataOut.data_pre[0].copy() | |
831 | cspc = dataOut.data_pre[1] |
|
832 | cspc = dataOut.data_pre[1] | |
832 |
|
833 | |||
833 | """NOTA SE DEBE REMOVER EL RANGO DEL PULSO TX""" |
|
834 | """Erick: NOTE THE RANGE OF THE PULSE TX MUST BE REMOVED""" | |
834 |
|
835 | |||
835 | SNRspc = spc.copy() |
|
836 | SNRspc = spc.copy() | |
836 | SNRspc[:,:,0:7]= numpy.NaN |
|
837 | SNRspc[:,:,0:7]= numpy.NaN | |
837 |
|
838 | |||
@@ -842,6 +843,24 class FullSpectralAnalysis(Operation): | |||||
842 | nProfiles = spc.shape[1] |
|
843 | nProfiles = spc.shape[1] | |
843 | nHeights = spc.shape[2] |
|
844 | nHeights = spc.shape[2] | |
844 |
|
845 | |||
|
846 | # first_height = 0.75 #km (ref: data header 20170822) | |||
|
847 | # resolution_height = 0.075 #km | |||
|
848 | ''' | |||
|
849 | finding height range. check this when radar parameters are changed! | |||
|
850 | ''' | |||
|
851 | if maxheight is not None: | |||
|
852 | # range_max = math.ceil((maxheight - first_height) / resolution_height) # theoretical | |||
|
853 | range_max = math.ceil(13.26 * maxheight - 3) # empirical, works better | |||
|
854 | else: | |||
|
855 | range_max = nHeights | |||
|
856 | if minheight is not None: | |||
|
857 | # range_min = int((minheight - first_height) / resolution_height) # theoretical | |||
|
858 | range_min = int(13.26 * minheight - 5) # empirical, works better | |||
|
859 | if range_min < 0: | |||
|
860 | range_min = 0 | |||
|
861 | else: | |||
|
862 | range_min = 0 | |||
|
863 | ||||
845 | pairsList = dataOut.groupList |
|
864 | pairsList = dataOut.groupList | |
846 | if dataOut.ChanDist is not None : |
|
865 | if dataOut.ChanDist is not None : | |
847 | ChanDist = dataOut.ChanDist |
|
866 | ChanDist = dataOut.ChanDist | |
@@ -850,15 +869,7 class FullSpectralAnalysis(Operation): | |||||
850 |
|
869 | |||
851 | FrecRange = dataOut.spc_range[0] |
|
870 | FrecRange = dataOut.spc_range[0] | |
852 |
|
871 | |||
853 | ySamples=numpy.ones([nChannel,nProfiles]) |
|
|||
854 | phase=numpy.ones([nChannel,nProfiles]) |
|
|||
855 | CSPCSamples=numpy.ones([nChannel,nProfiles],dtype=numpy.complex_) |
|
|||
856 | coherence=numpy.ones([nChannel,nProfiles]) |
|
|||
857 | PhaseSlope=numpy.ones(nChannel) |
|
|||
858 | PhaseInter=numpy.ones(nChannel) |
|
|||
859 | data_SNR=numpy.zeros([nProfiles]) |
|
872 | data_SNR=numpy.zeros([nProfiles]) | |
860 |
|
||||
861 | data = dataOut.data_pre |
|
|||
862 | noise = dataOut.noise |
|
873 | noise = dataOut.noise | |
863 |
|
874 | |||
864 | dataOut.data_SNR = (numpy.mean(SNRspc,axis=1)- noise[0]) / noise[0] |
|
875 | dataOut.data_SNR = (numpy.mean(SNRspc,axis=1)- noise[0]) / noise[0] | |
@@ -871,48 +882,53 class FullSpectralAnalysis(Operation): | |||||
871 | velocityX=[] |
|
882 | velocityX=[] | |
872 | velocityY=[] |
|
883 | velocityY=[] | |
873 | velocityV=[] |
|
884 | velocityV=[] | |
874 | PhaseLine=[] |
|
885 | PhaseLine=[] # unused afterwards | |
875 |
|
886 | |||
876 | dbSNR = 10*numpy.log10(dataOut.data_SNR) |
|
887 | dbSNR = 10*numpy.log10(dataOut.data_SNR) | |
877 | dbSNR = numpy.average(dbSNR,0) |
|
888 | dbSNR = numpy.average(dbSNR,0) | |
878 |
|
889 | |||
|
890 | '''***********************************************WIND ESTIMATION**************************************''' | |||
|
891 | ||||
879 | for Height in range(nHeights): |
|
892 | for Height in range(nHeights): | |
880 |
|
|
893 | ||
881 | [Vzon,Vmer,Vver, GaussCenter, PhaseSlope, FitGaussCSPC]= self.WindEstimation(spc, cspc, pairsList, ChanDist, Height, noise, dataOut.spc_range, dbSNR[Height], SNRlimit) |
|
894 | if Height >= range_min and Height < range_max: | |
882 | PhaseLine = numpy.append(PhaseLine, PhaseSlope) |
|
895 | # [Vzon,Vmer,Vver, GaussCenter, PhaseSlope, FitGaussCSPC] = self.WindEstimation(spc, cspc, pairsList, ChanDist, Height, noise, dataOut.spc_range, dbSNR[Height], SNRlimit) | |
|
896 | ||||
|
897 | # error_code unused, yet maybe useful for future analysis. | |||
|
898 | # Test | |||
|
899 | [Vzon,Vmer,Vver, error_code] = self.TestWindEstimation(spc[:,:,Height], cspc[:,:,Height], pairsList, ChanDist, Height, noise, dataOut.spc_range, dbSNR[Height], SNRlimit) | |||
|
900 | ||||
|
901 | else: | |||
|
902 | Vzon,Vmer,Vver = 0., 0., numpy.NaN | |||
|
903 | ||||
883 |
|
904 | |||
884 | if abs(Vzon)<100. and abs(Vzon)> 0.: |
|
905 | if abs(Vzon) < 100. and abs(Vzon) > 0. and abs(Vmer) < 100. and abs(Vmer) > 0.: | |
885 |
velocityX=numpy.append(velocityX, Vzon) |
|
906 | velocityX=numpy.append(velocityX, Vzon) | |
886 |
|
907 | velocityY=numpy.append(velocityY, -Vmer) | ||
|
908 | ||||
887 | else: |
|
909 | else: | |
888 | velocityX=numpy.append(velocityX, numpy.NaN) |
|
910 | velocityX=numpy.append(velocityX, numpy.NaN) | |
889 |
|
||||
890 | if abs(Vmer)<100. and abs(Vmer) > 0.: |
|
|||
891 | velocityY=numpy.append(velocityY, -Vmer)#Vang |
|
|||
892 |
|
||||
893 | else: |
|
|||
894 | velocityY=numpy.append(velocityY, numpy.NaN) |
|
911 | velocityY=numpy.append(velocityY, numpy.NaN) | |
895 |
|
912 | |||
896 | if dbSNR[Height] > SNRlimit: |
|
913 | if dbSNR[Height] > SNRlimit: | |
897 |
velocityV=numpy.append(velocityV, -Vver) |
|
914 | velocityV=numpy.append(velocityV, -Vver) # reason for this minus sign -> convention? (taken from Ericks version) | |
898 | else: |
|
915 | else: | |
899 | velocityV=numpy.append(velocityV, numpy.NaN) |
|
916 | velocityV=numpy.append(velocityV, numpy.NaN) | |
900 |
|
||||
901 |
|
917 | |||
902 |
|
918 | |||
903 |
''' |
|
919 | '''Change the numpy.array (velocityX) sign when trying to process BLTR data (Erick)''' | |
904 |
data_output[0] = numpy.array(velocityX) |
|
920 | data_output[0] = numpy.array(velocityX) | |
905 |
data_output[1] = numpy.array(velocityY) |
|
921 | data_output[1] = numpy.array(velocityY) | |
906 |
data_output[2] = velocityV |
|
922 | data_output[2] = velocityV | |
907 |
|
923 | |||
908 | xFrec=FrecRange[0:spc.shape[1]] |
|
|||
909 |
|
924 | |||
910 | dataOut.data_output=data_output |
|
925 | dataOut.data_output = data_output | |
911 |
|
926 | |||
912 | return dataOut |
|
927 | return dataOut | |
913 |
|
928 | |||
914 |
|
929 | |||
915 | def moving_average(self,x, N=2): |
|
930 | def moving_average(self,x, N=2): | |
|
931 | """ convolution for smoothenig data. note that last N-1 values are convolution with zeroes """ | |||
916 | return numpy.convolve(x, numpy.ones((N,))/N)[(N-1):] |
|
932 | return numpy.convolve(x, numpy.ones((N,))/N)[(N-1):] | |
917 |
|
933 | |||
918 | def gaus(self,xSamples,Amp,Mu,Sigma): |
|
934 | def gaus(self,xSamples,Amp,Mu,Sigma): | |
@@ -921,11 +937,17 class FullSpectralAnalysis(Operation): | |||||
921 |
|
937 | |||
922 |
|
938 | |||
923 | def Moments(self, ySamples, xSamples): |
|
939 | def Moments(self, ySamples, xSamples): | |
924 | Pot = numpy.nansum( ySamples ) # Potencia, momento 0 |
|
940 | '''*** | |
|
941 | Variables corresponding to moments of distribution. | |||
|
942 | Also used as initial coefficients for curve_fit. | |||
|
943 | Vr was corrected. Only a velocity when x is velocity, of course. | |||
|
944 | ***''' | |||
|
945 | Pot = numpy.nansum( ySamples ) # Potencia, momento 0 | |||
925 | yNorm = ySamples / Pot |
|
946 | yNorm = ySamples / Pot | |
926 | Vr = numpy.nansum( yNorm * xSamples ) # Velocidad radial, mu, corrimiento doppler, primer momento |
|
947 | x_range = (numpy.max(xSamples)-numpy.min(xSamples)) | |
927 | Sigma2 = abs(numpy.nansum( yNorm * ( xSamples - Vr )**2 )) # Segundo Momento |
|
948 | Vr = numpy.nansum( yNorm * xSamples )*x_range/len(xSamples) # Velocidad radial, mu, corrimiento doppler, primer momento | |
928 | Desv = Sigma2**0.5 # Desv. Estandar, Ancho espectral |
|
949 | Sigma2 = abs(numpy.nansum( yNorm * ( xSamples - Vr )**2 )) # Segundo Momento | |
|
950 | Desv = Sigma2**0.5 # Desv. Estandar, Ancho espectral | |||
929 |
|
951 | |||
930 | return numpy.array([Pot, Vr, Desv]) |
|
952 | return numpy.array([Pot, Vr, Desv]) | |
931 |
|
953 | |||
@@ -1171,7 +1193,366 class FullSpectralAnalysis(Operation): | |||||
1171 |
|
1193 | |||
1172 |
|
1194 | |||
1173 | return Vzon, Vmer, Vver, GaussCenter, PhaseSlope, FitGaussCSPC |
|
1195 | return Vzon, Vmer, Vver, GaussCenter, PhaseSlope, FitGaussCSPC | |
|
1196 | ||||
|
1197 | ||||
|
1198 | ||||
|
1199 | def StopWindEstimation(self, error_code): | |||
|
1200 | ''' | |||
|
1201 | the wind calculation and returns zeros | |||
|
1202 | ''' | |||
|
1203 | Vzon = 0 | |||
|
1204 | Vmer = 0 | |||
|
1205 | Vver = numpy.nan | |||
|
1206 | return Vzon, Vmer, Vver, error_code | |||
|
1207 | ||||
|
1208 | def AntiAliasing(self, interval, maxstep): | |||
|
1209 | """ | |||
|
1210 | function to prevent errors from aliased values when computing phaseslope | |||
|
1211 | """ | |||
|
1212 | antialiased = numpy.zeros(len(interval))*0.0 | |||
|
1213 | copyinterval = interval.copy() | |||
|
1214 | ||||
|
1215 | antialiased[0] = copyinterval[0] | |||
|
1216 | ||||
|
1217 | for i in range(1,len(antialiased)): | |||
|
1218 | ||||
|
1219 | step = interval[i] - interval[i-1] | |||
|
1220 | ||||
|
1221 | if step > maxstep: | |||
|
1222 | copyinterval -= 2*numpy.pi | |||
|
1223 | antialiased[i] = copyinterval[i] | |||
|
1224 | ||||
|
1225 | elif step < maxstep*(-1): | |||
|
1226 | copyinterval += 2*numpy.pi | |||
|
1227 | antialiased[i] = copyinterval[i] | |||
|
1228 | ||||
|
1229 | else: | |||
|
1230 | antialiased[i] = copyinterval[i].copy() | |||
|
1231 | ||||
|
1232 | return antialiased | |||
|
1233 | ||||
|
1234 | ||||
|
1235 | ||||
|
1236 | def TestWindEstimation(self, spc, cspc, pairsList, ChanDist, Height, noise, AbbsisaRange, dbSNR, SNRlimit): | |||
|
1237 | """ | |||
|
1238 | Function that Calculates Zonal, Meridional and Vertical wind velocities. | |||
|
1239 | Initial Version by E. Bocanegra updated by J. Zibell until Nov. 2019. | |||
|
1240 | ||||
|
1241 | Input: | |||
|
1242 | spc, cspc : self spectra and cross spectra data. In Briggs notation something like S_i*(S_i)_conj, (S_j)_conj respectively. | |||
|
1243 | pairsList : Pairlist of channels | |||
|
1244 | ChanDist : array of xi_ij and eta_ij | |||
|
1245 | Height : height at which data is processed | |||
|
1246 | noise : noise in [channels] format for specific height | |||
|
1247 | Abbsisarange : range of the frequencies or velocities | |||
|
1248 | dbSNR, SNRlimit : signal to noise ratio in db, lower limit | |||
|
1249 | ||||
|
1250 | Output: | |||
|
1251 | Vzon, Vmer, Vver : wind velocities | |||
|
1252 | error_code : int that states where code is terminated | |||
|
1253 | ||||
|
1254 | 0 : no error detected | |||
|
1255 | 1 : Gaussian of mean spc exceeds widthlimit | |||
|
1256 | 2 : no Gaussian of mean spc found | |||
|
1257 | 3 : SNR to low or velocity to high -> prec. e.g. | |||
|
1258 | 4 : at least one Gaussian of cspc exceeds widthlimit | |||
|
1259 | 5 : zero out of three cspc Gaussian fits converged | |||
|
1260 | 6 : phase slope fit could not be found | |||
|
1261 | 7 : arrays used to fit phase have different length | |||
|
1262 | 8 : frequency range is either too short (len <= 5) or very long (> 30% of cspc) | |||
|
1263 | ||||
|
1264 | """ | |||
|
1265 | ||||
|
1266 | error_code = 0 | |||
|
1267 | ||||
|
1268 | ||||
|
1269 | SPC_Samples = numpy.ones([spc.shape[0],spc.shape[1]]) # for normalized spc values for one height | |||
|
1270 | phase = numpy.ones([spc.shape[0],spc.shape[1]]) # phase between channels | |||
|
1271 | CSPC_Samples = numpy.ones([spc.shape[0],spc.shape[1]],dtype=numpy.complex_) # for normalized cspc values | |||
|
1272 | PhaseSlope = numpy.zeros(spc.shape[0]) # slope of the phases, channelwise | |||
|
1273 | PhaseInter = numpy.ones(spc.shape[0]) # intercept to the slope of the phases, channelwise | |||
|
1274 | xFrec = AbbsisaRange[0][0:spc.shape[1]] # frequency range | |||
|
1275 | xVel = AbbsisaRange[2][0:spc.shape[1]] # velocity range | |||
|
1276 | SPCav = numpy.average(spc, axis=0)-numpy.average(noise) # spc[0]-noise[0] | |||
|
1277 | ||||
|
1278 | SPCmoments_vel = self.Moments(SPCav, xVel ) # SPCmoments_vel[1] corresponds to vertical velocity and is used to determine if signal corresponds to wind (if .. <3) | |||
|
1279 | CSPCmoments = [] | |||
|
1280 | ||||
|
1281 | ||||
|
1282 | '''Getting Eij and Nij''' | |||
|
1283 | ||||
|
1284 | Xi01, Xi02, Xi12 = ChanDist[:,0] | |||
|
1285 | Eta01, Eta02, Eta12 = ChanDist[:,1] | |||
|
1286 | ||||
|
1287 | # update nov 19 | |||
|
1288 | widthlimit = 7 # maximum width in Hz of the gaussian, empirically determined. Anything above 10 is unrealistic, often values between 1 and 5 correspond to proper fits. | |||
|
1289 | ||||
|
1290 | '''************************* SPC is normalized ********************************''' | |||
|
1291 | ||||
|
1292 | spc_norm = spc.copy() # need copy() because untouched spc is needed for normalization of cspc below | |||
|
1293 | spc_norm = numpy.where(numpy.isfinite(spc_norm), spc_norm, numpy.NAN) | |||
|
1294 | ||||
|
1295 | for i in range(spc.shape[0]): | |||
|
1296 | ||||
|
1297 | spc_sub = spc_norm[i,:] - noise[i] # spc not smoothed here or in previous version. | |||
|
1298 | ||||
|
1299 | Factor_Norm = 2*numpy.max(xFrec) / numpy.count_nonzero(~numpy.isnan(spc_sub)) # usually = Freq range / nfft | |||
|
1300 | normalized_spc = spc_sub / (numpy.nansum(numpy.abs(spc_sub)) * Factor_Norm) | |||
|
1301 | ||||
|
1302 | xSamples = xFrec # the frequency range is taken | |||
|
1303 | SPC_Samples[i] = normalized_spc # Normalized SPC values are taken | |||
|
1304 | ||||
|
1305 | '''********************** FITTING MEAN SPC GAUSSIAN **********************''' | |||
|
1306 | ||||
|
1307 | """ the gaussian of the mean: first subtract noise, then normalize. this is legal because | |||
|
1308 | you only fit the curve and don't need the absolute value of height for calculation, | |||
|
1309 | only for estimation of width. for normalization of cross spectra, you need initial, | |||
|
1310 | unnormalized self-spectra With noise. | |||
|
1311 | ||||
|
1312 | Technically, you don't even need to normalize the self-spectra, as you only need the | |||
|
1313 | width of the peak. However, it was left this way. Note that the normalization has a flaw: | |||
|
1314 | due to subtraction of the noise, some values are below zero. Raw "spc" values should be | |||
|
1315 | >= 0, as it is the modulus squared of the signals (complex * it's conjugate) | |||
|
1316 | """ | |||
|
1317 | ||||
|
1318 | SPCMean = numpy.average(SPC_Samples, axis=0) | |||
|
1319 | ||||
|
1320 | popt = [1e-10,0,1e-10] | |||
|
1321 | SPCMoments = self.Moments(SPCMean, xSamples) | |||
|
1322 | ||||
|
1323 | if dbSNR > SNRlimit and numpy.abs(SPCmoments_vel[1]) < 3: | |||
|
1324 | try: | |||
|
1325 | popt,pcov = curve_fit(self.gaus,xSamples,SPCMean,p0=SPCMoments)#, bounds=(-numpy.inf, [numpy.inf, numpy.inf, 10])). Setting bounds does not make the code faster but only keeps the fit from finding the minimum. | |||
|
1326 | if popt[2] > widthlimit: # CONDITION | |||
|
1327 | return self.StopWindEstimation(error_code = 1) | |||
|
1328 | ||||
|
1329 | FitGauss = self.gaus(xSamples,*popt) | |||
|
1330 | ||||
|
1331 | except :#RuntimeError: | |||
|
1332 | return self.StopWindEstimation(error_code = 2) | |||
|
1333 | ||||
|
1334 | else: | |||
|
1335 | return self.StopWindEstimation(error_code = 3) | |||
|
1336 | ||||
|
1337 | ||||
|
1338 | ||||
|
1339 | '''***************************** CSPC Normalization ************************* | |||
|
1340 | new section: | |||
|
1341 | The Spc spectra are used to normalize the crossspectra. Peaks from precipitation | |||
|
1342 | influence the norm which is not desired. First, a range is identified where the | |||
|
1343 | wind peak is estimated -> sum_wind is sum of those frequencies. Next, the area | |||
|
1344 | around it gets cut off and values replaced by mean determined by the boundary | |||
|
1345 | data -> sum_noise (spc is not normalized here, thats why the noise is important) | |||
|
1346 | ||||
|
1347 | The sums are then added and multiplied by range/datapoints, because you need | |||
|
1348 | an integral and not a sum for normalization. | |||
|
1349 | ||||
|
1350 | A norm is found according to Briggs 92. | |||
|
1351 | ''' | |||
|
1352 | ||||
|
1353 | radarWavelength = 0.6741 # meters | |||
|
1354 | count_limit_freq = numpy.abs(popt[1]) + widthlimit # Hz, m/s can be also used if velocity is desired abscissa. | |||
|
1355 | # count_limit_freq = numpy.max(xFrec) | |||
|
1356 | ||||
|
1357 | channel_integrals = numpy.zeros(3) | |||
|
1358 | ||||
|
1359 | for i in range(spc.shape[0]): | |||
|
1360 | ''' | |||
|
1361 | find the point in array corresponding to count_limit frequency. | |||
|
1362 | sum over all frequencies in the range around zero Hz @ math.ceil(N_freq/2) | |||
|
1363 | ''' | |||
|
1364 | N_freq = numpy.count_nonzero(~numpy.isnan(spc[i,:])) | |||
|
1365 | count_limit_int = int(math.ceil( count_limit_freq / numpy.max(xFrec) * (N_freq / 2) )) # gives integer point | |||
|
1366 | sum_wind = numpy.nansum( spc[i, (math.ceil(N_freq/2) - count_limit_int) : (math.ceil(N_freq / 2) + count_limit_int)] ) #N_freq/2 is where frequency (velocity) is zero, i.e. middle of spectrum. | |||
|
1367 | sum_noise = (numpy.mean(spc[i, :4]) + numpy.mean(spc[i, -6:-2]))/2.0 * (N_freq - 2*count_limit_int) | |||
|
1368 | channel_integrals[i] = (sum_noise + sum_wind) * (2*numpy.max(xFrec) / N_freq) | |||
|
1369 | ||||
|
1370 | ||||
|
1371 | cross_integrals_peak = numpy.zeros(3) | |||
|
1372 | # cross_integrals_totalrange = numpy.zeros(3) | |||
|
1373 | ||||
|
1374 | for i in range(spc.shape[0]): | |||
|
1375 | ||||
|
1376 | cspc_norm = cspc[i,:].copy() # cspc not smoothed here or in previous version | |||
|
1377 | ||||
|
1378 | chan_index0 = pairsList[i][0] | |||
|
1379 | chan_index1 = pairsList[i][1] | |||
|
1380 | ||||
|
1381 | cross_integrals_peak[i] = channel_integrals[chan_index0]*channel_integrals[chan_index1] | |||
|
1382 | normalized_cspc = cspc_norm / numpy.sqrt(cross_integrals_peak[i]) | |||
|
1383 | CSPC_Samples[i] = normalized_cspc | |||
|
1384 | ||||
|
1385 | ''' Finding cross integrals without subtracting any peaks:''' | |||
|
1386 | # FactorNorm0 = 2*numpy.max(xFrec) / numpy.count_nonzero(~numpy.isnan(spc[chan_index0,:])) | |||
|
1387 | # FactorNorm1 = 2*numpy.max(xFrec) / numpy.count_nonzero(~numpy.isnan(spc[chan_index1,:])) | |||
|
1388 | # cross_integrals_totalrange[i] = (numpy.nansum(spc[chan_index0,:])) * FactorNorm0 * (numpy.nansum(spc[chan_index1,:])) * FactorNorm1 | |||
|
1389 | # normalized_cspc = cspc_norm / numpy.sqrt(cross_integrals_totalrange[i]) | |||
|
1390 | # CSPC_Samples[i] = normalized_cspc | |||
|
1391 | ||||
|
1392 | ||||
|
1393 | phase[i] = numpy.arctan2(CSPC_Samples[i].imag, CSPC_Samples[i].real) | |||
|
1394 | ||||
|
1395 | ||||
|
1396 | CSPCmoments = numpy.vstack([self.Moments(numpy.abs(CSPC_Samples[0]), xSamples), | |||
|
1397 | self.Moments(numpy.abs(CSPC_Samples[1]), xSamples), | |||
|
1398 | self.Moments(numpy.abs(CSPC_Samples[2]), xSamples)]) | |||
|
1399 | ||||
|
1400 | ||||
|
1401 | '''***Sorting out NaN entries***''' | |||
|
1402 | CSPCMask01 = numpy.abs(CSPC_Samples[0]) | |||
|
1403 | CSPCMask02 = numpy.abs(CSPC_Samples[1]) | |||
|
1404 | CSPCMask12 = numpy.abs(CSPC_Samples[2]) | |||
|
1405 | ||||
|
1406 | mask01 = ~numpy.isnan(CSPCMask01) | |||
|
1407 | mask02 = ~numpy.isnan(CSPCMask02) | |||
|
1408 | mask12 = ~numpy.isnan(CSPCMask12) | |||
|
1409 | ||||
|
1410 | CSPCMask01 = CSPCMask01[mask01] | |||
|
1411 | CSPCMask02 = CSPCMask02[mask02] | |||
|
1412 | CSPCMask12 = CSPCMask12[mask12] | |||
|
1413 | ||||
1174 |
|
1414 | |||
|
1415 | popt01, popt02, popt12 = [1e-10,1e-10,1e-10], [1e-10,1e-10,1e-10] ,[1e-10,1e-10,1e-10] | |||
|
1416 | FitGauss01, FitGauss02, FitGauss12 = numpy.empty(len(xSamples))*0, numpy.empty(len(xSamples))*0, numpy.empty(len(xSamples))*0 | |||
|
1417 | ||||
|
1418 | '''*******************************FIT GAUSS CSPC************************************''' | |||
|
1419 | ||||
|
1420 | try: | |||
|
1421 | popt01,pcov = curve_fit(self.gaus,xSamples[mask01],numpy.abs(CSPCMask01),p0=CSPCmoments[0]) | |||
|
1422 | if popt01[2] > widthlimit: # CONDITION | |||
|
1423 | return self.StopWindEstimation(error_code = 4) | |||
|
1424 | ||||
|
1425 | popt02,pcov = curve_fit(self.gaus,xSamples[mask02],numpy.abs(CSPCMask02),p0=CSPCmoments[1]) | |||
|
1426 | if popt02[2] > widthlimit: # CONDITION | |||
|
1427 | return self.StopWindEstimation(error_code = 4) | |||
|
1428 | ||||
|
1429 | popt12,pcov = curve_fit(self.gaus,xSamples[mask12],numpy.abs(CSPCMask12),p0=CSPCmoments[2]) | |||
|
1430 | if popt12[2] > widthlimit: # CONDITION | |||
|
1431 | return self.StopWindEstimation(error_code = 4) | |||
|
1432 | ||||
|
1433 | FitGauss01 = self.gaus(xSamples, *popt01) | |||
|
1434 | FitGauss02 = self.gaus(xSamples, *popt02) | |||
|
1435 | FitGauss12 = self.gaus(xSamples, *popt12) | |||
|
1436 | ||||
|
1437 | except: | |||
|
1438 | return self.StopWindEstimation(error_code = 5) | |||
|
1439 | ||||
|
1440 | ||||
|
1441 | '''************* Getting Fij ***************''' | |||
|
1442 | ||||
|
1443 | ||||
|
1444 | #Punto en Eje X de la Gaussiana donde se encuentra el centro -- x-axis point of the gaussian where the center is located | |||
|
1445 | # -> PointGauCenter | |||
|
1446 | GaussCenter = popt[1] | |||
|
1447 | ClosestCenter = xSamples[numpy.abs(xSamples-GaussCenter).argmin()] | |||
|
1448 | PointGauCenter = numpy.where(xSamples==ClosestCenter)[0][0] | |||
|
1449 | ||||
|
1450 | #Punto e^-1 hubicado en la Gaussiana -- point where e^-1 is located in the gaussian | |||
|
1451 | PeMinus1 = numpy.max(FitGauss) * numpy.exp(-1) | |||
|
1452 | FijClosest = FitGauss[numpy.abs(FitGauss-PeMinus1).argmin()] # El punto mas cercano a "Peminus1" dentro de "FitGauss" | |||
|
1453 | PointFij = numpy.where(FitGauss==FijClosest)[0][0] | |||
|
1454 | ||||
|
1455 | Fij = numpy.abs(xSamples[PointFij] - xSamples[PointGauCenter]) | |||
|
1456 | ||||
|
1457 | '''********** Taking frequency ranges from mean SPCs **********''' | |||
|
1458 | ||||
|
1459 | #GaussCenter = popt[1] #Primer momento 01 | |||
|
1460 | GauWidth = popt[2] * 3/2 #Ancho de banda de Gau01 -- Bandwidth of Gau01 TODO why *3/2? | |||
|
1461 | Range = numpy.empty(2) | |||
|
1462 | Range[0] = GaussCenter - GauWidth | |||
|
1463 | Range[1] = GaussCenter + GauWidth | |||
|
1464 | #Punto en Eje X de la Gaussiana donde se encuentra ancho de banda (min:max) -- Point in x-axis where the bandwidth is located (min:max) | |||
|
1465 | ClosRangeMin = xSamples[numpy.abs(xSamples-Range[0]).argmin()] | |||
|
1466 | ClosRangeMax = xSamples[numpy.abs(xSamples-Range[1]).argmin()] | |||
|
1467 | ||||
|
1468 | PointRangeMin = numpy.where(xSamples==ClosRangeMin)[0][0] | |||
|
1469 | PointRangeMax = numpy.where(xSamples==ClosRangeMax)[0][0] | |||
|
1470 | ||||
|
1471 | Range = numpy.array([ PointRangeMin, PointRangeMax ]) | |||
|
1472 | ||||
|
1473 | FrecRange = xFrec[ Range[0] : Range[1] ] | |||
|
1474 | ||||
|
1475 | ||||
|
1476 | '''************************** Getting Phase Slope ***************************''' | |||
|
1477 | ||||
|
1478 | for i in range(1,3): # Changed to only compute two | |||
|
1479 | ||||
|
1480 | if len(FrecRange) > 5 and len(FrecRange) < spc.shape[1] * 0.3: | |||
|
1481 | # PhaseRange=self.moving_average(phase[i,Range[0]:Range[1]],N=1) #used before to smooth phase with N=3 | |||
|
1482 | PhaseRange = phase[i,Range[0]:Range[1]].copy() | |||
|
1483 | ||||
|
1484 | mask = ~numpy.isnan(FrecRange) & ~numpy.isnan(PhaseRange) | |||
|
1485 | ||||
|
1486 | ||||
|
1487 | if len(FrecRange) == len(PhaseRange): | |||
|
1488 | ||||
|
1489 | try: | |||
|
1490 | slope, intercept, _, _, _ = stats.linregress(FrecRange[mask], self.AntiAliasing(PhaseRange[mask], 4.5)) | |||
|
1491 | PhaseSlope[i] = slope | |||
|
1492 | PhaseInter[i] = intercept | |||
|
1493 | ||||
|
1494 | except: | |||
|
1495 | return self.StopWindEstimation(error_code = 6) | |||
|
1496 | ||||
|
1497 | else: | |||
|
1498 | return self.StopWindEstimation(error_code = 7) | |||
|
1499 | ||||
|
1500 | else: | |||
|
1501 | return self.StopWindEstimation(error_code = 8) | |||
|
1502 | ||||
|
1503 | ||||
|
1504 | ||||
|
1505 | '''*** Constants A-H correspond to the convention as in Briggs and Vincent 1992 ***''' | |||
|
1506 | ||||
|
1507 | '''Getting constant C''' | |||
|
1508 | cC=(Fij*numpy.pi)**2 | |||
|
1509 | ||||
|
1510 | '''****** Getting constants F and G ******''' | |||
|
1511 | MijEijNij = numpy.array([[Xi02,Eta02], [Xi12,Eta12]]) | |||
|
1512 | MijResult0 = (-PhaseSlope[1] * cC) / (2*numpy.pi) | |||
|
1513 | MijResult1 = (-PhaseSlope[2] * cC) / (2*numpy.pi) | |||
|
1514 | MijResults = numpy.array([MijResult0,MijResult1]) | |||
|
1515 | (cF,cG) = numpy.linalg.solve(MijEijNij, MijResults) | |||
|
1516 | ||||
|
1517 | '''****** Getting constants A, B and H ******''' | |||
|
1518 | W01 = numpy.nanmax( FitGauss01 ) | |||
|
1519 | W02 = numpy.nanmax( FitGauss02 ) | |||
|
1520 | W12 = numpy.nanmax( FitGauss12 ) | |||
|
1521 | ||||
|
1522 | WijResult0 = ((cF * Xi01 + cG * Eta01)**2)/cC - numpy.log(W01 / numpy.sqrt(numpy.pi / cC)) | |||
|
1523 | WijResult1 = ((cF * Xi02 + cG * Eta02)**2)/cC - numpy.log(W02 / numpy.sqrt(numpy.pi / cC)) | |||
|
1524 | WijResult2 = ((cF * Xi12 + cG * Eta12)**2)/cC - numpy.log(W12 / numpy.sqrt(numpy.pi / cC)) | |||
|
1525 | ||||
|
1526 | WijResults = numpy.array([WijResult0, WijResult1, WijResult2]) | |||
|
1527 | ||||
|
1528 | WijEijNij = numpy.array([ [Xi01**2, Eta01**2, 2*Xi01*Eta01] , [Xi02**2, Eta02**2, 2*Xi02*Eta02] , [Xi12**2, Eta12**2, 2*Xi12*Eta12] ]) | |||
|
1529 | (cA,cB,cH) = numpy.linalg.solve(WijEijNij, WijResults) | |||
|
1530 | ||||
|
1531 | VxVy = numpy.array([[cA,cH],[cH,cB]]) | |||
|
1532 | VxVyResults = numpy.array([-cF,-cG]) | |||
|
1533 | (Vx,Vy) = numpy.linalg.solve(VxVy, VxVyResults) | |||
|
1534 | ||||
|
1535 | Vzon = Vy | |||
|
1536 | Vmer = Vx | |||
|
1537 | ||||
|
1538 | # Vmag=numpy.sqrt(Vzon**2+Vmer**2) # unused | |||
|
1539 | # Vang=numpy.arctan2(Vmer,Vzon) # unused | |||
|
1540 | ||||
|
1541 | ||||
|
1542 | ''' using frequency as abscissa. Due to three channels, the offzenith angle is zero | |||
|
1543 | and Vrad equal to Vver. formula taken from Briggs 92, figure 4. | |||
|
1544 | ''' | |||
|
1545 | if numpy.abs( popt[1] ) < 3.5 and len(FrecRange) > 4: | |||
|
1546 | Vver = 0.5 * radarWavelength * popt[1] * 100 # *100 to get cm (/s) | |||
|
1547 | else: | |||
|
1548 | Vver = numpy.NaN | |||
|
1549 | ||||
|
1550 | error_code = 0 | |||
|
1551 | ||||
|
1552 | return Vzon, Vmer, Vver, error_code | |||
|
1553 | ||||
|
1554 | ||||
|
1555 | ||||
1175 | class SpectralMoments(Operation): |
|
1556 | class SpectralMoments(Operation): | |
1176 |
|
1557 | |||
1177 | ''' |
|
1558 | ''' |
General Comments 0
You need to be logged in to leave comments.
Login now