@@ -23,6 +23,7 from numpy import NaN | |||
|
23 | 23 | from scipy.optimize.optimize import OptimizeWarning |
|
24 | 24 | warnings.filterwarnings('ignore') |
|
25 | 25 | |
|
26 | import matplotlib.pyplot as plt | |
|
26 | 27 | |
|
27 | 28 | SPEED_OF_LIGHT = 299792458 |
|
28 | 29 | |
@@ -807,10 +808,10 class PrecipitationProc(Operation): | |||
|
807 | 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 | 813 | Input: |
|
813 |
self.dataOut.data_pre : SelfSpectra and CrossS |
|
|
814 | self.dataOut.data_pre : SelfSpectra and CrossSpectra data | |
|
814 | 815 | self.dataOut.groupList : Pairlist of channels |
|
815 | 816 | self.dataOut.ChanDist : Physical distance between receivers |
|
816 | 817 | |
@@ -823,15 +824,15 class FullSpectralAnalysis(Operation): | |||
|
823 | 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 | 829 | self.indice=int(numpy.random.rand()*1000) |
|
829 | 830 | |
|
830 | 831 | spc = dataOut.data_pre[0].copy() |
|
831 | 832 | cspc = dataOut.data_pre[1] |
|
832 | 833 | |
|
833 | """NOTA SE DEBE REMOVER EL RANGO DEL PULSO TX""" | |
|
834 | ||
|
834 | """Erick: NOTE THE RANGE OF THE PULSE TX MUST BE REMOVED""" | |
|
835 | ||
|
835 | 836 | SNRspc = spc.copy() |
|
836 | 837 | SNRspc[:,:,0:7]= numpy.NaN |
|
837 | 838 | |
@@ -842,6 +843,24 class FullSpectralAnalysis(Operation): | |||
|
842 | 843 | nProfiles = spc.shape[1] |
|
843 | 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 | 864 | pairsList = dataOut.groupList |
|
846 | 865 | if dataOut.ChanDist is not None : |
|
847 | 866 | ChanDist = dataOut.ChanDist |
@@ -850,15 +869,7 class FullSpectralAnalysis(Operation): | |||
|
850 | 869 | |
|
851 | 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 | 872 | data_SNR=numpy.zeros([nProfiles]) |
|
860 | ||
|
861 | data = dataOut.data_pre | |
|
862 | 873 | noise = dataOut.noise |
|
863 | 874 | |
|
864 | 875 | dataOut.data_SNR = (numpy.mean(SNRspc,axis=1)- noise[0]) / noise[0] |
@@ -871,48 +882,53 class FullSpectralAnalysis(Operation): | |||
|
871 | 882 | velocityX=[] |
|
872 | 883 | velocityY=[] |
|
873 | 884 | velocityV=[] |
|
874 | PhaseLine=[] | |
|
885 | PhaseLine=[] # unused afterwards | |
|
875 | 886 | |
|
876 | 887 | dbSNR = 10*numpy.log10(dataOut.data_SNR) |
|
877 | 888 | dbSNR = numpy.average(dbSNR,0) |
|
878 | 889 | |
|
890 | '''***********************************************WIND ESTIMATION**************************************''' | |
|
891 | ||
|
879 | 892 | for Height in range(nHeights): |
|
880 |
|
|
|
881 | [Vzon,Vmer,Vver, GaussCenter, PhaseSlope, FitGaussCSPC]= self.WindEstimation(spc, cspc, pairsList, ChanDist, Height, noise, dataOut.spc_range, dbSNR[Height], SNRlimit) | |
|
882 | PhaseLine = numpy.append(PhaseLine, PhaseSlope) | |
|
893 | ||
|
894 | if Height >= range_min and Height < range_max: | |
|
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.: | |
|
885 |
velocityX=numpy.append(velocityX, Vzon) |
|
|
886 | ||
|
905 | if abs(Vzon) < 100. and abs(Vzon) > 0. and abs(Vmer) < 100. and abs(Vmer) > 0.: | |
|
906 | velocityX=numpy.append(velocityX, Vzon) | |
|
907 | velocityY=numpy.append(velocityY, -Vmer) | |
|
908 | ||
|
887 | 909 | else: |
|
888 | 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 | 911 | velocityY=numpy.append(velocityY, numpy.NaN) |
|
895 | 912 | |
|
896 | 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 | 915 | else: |
|
899 | 916 | velocityV=numpy.append(velocityV, numpy.NaN) |
|
900 | ||
|
901 | 917 | |
|
902 | 918 | |
|
903 |
''' |
|
|
904 |
data_output[0] = numpy.array(velocityX) |
|
|
905 |
data_output[1] = numpy.array(velocityY) |
|
|
906 |
data_output[2] = velocityV |
|
|
919 | '''Change the numpy.array (velocityX) sign when trying to process BLTR data (Erick)''' | |
|
920 | data_output[0] = numpy.array(velocityX) | |
|
921 | data_output[1] = numpy.array(velocityY) | |
|
922 | data_output[2] = velocityV | |
|
907 | 923 | |
|
908 | xFrec=FrecRange[0:spc.shape[1]] | |
|
909 | 924 | |
|
910 | dataOut.data_output=data_output | |
|
911 | ||
|
925 | dataOut.data_output = data_output | |
|
926 | ||
|
912 | 927 | return dataOut |
|
913 | 928 | |
|
914 | ||
|
929 | ||
|
915 | 930 | def moving_average(self,x, N=2): |
|
931 | """ convolution for smoothenig data. note that last N-1 values are convolution with zeroes """ | |
|
916 | 932 | return numpy.convolve(x, numpy.ones((N,))/N)[(N-1):] |
|
917 | 933 | |
|
918 | 934 | def gaus(self,xSamples,Amp,Mu,Sigma): |
@@ -921,11 +937,17 class FullSpectralAnalysis(Operation): | |||
|
921 | 937 | |
|
922 | 938 | |
|
923 | 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 | 946 | yNorm = ySamples / Pot |
|
926 | Vr = numpy.nansum( yNorm * xSamples ) # Velocidad radial, mu, corrimiento doppler, primer momento | |
|
927 | Sigma2 = abs(numpy.nansum( yNorm * ( xSamples - Vr )**2 )) # Segundo Momento | |
|
928 | Desv = Sigma2**0.5 # Desv. Estandar, Ancho espectral | |
|
947 | x_range = (numpy.max(xSamples)-numpy.min(xSamples)) | |
|
948 | Vr = numpy.nansum( yNorm * xSamples )*x_range/len(xSamples) # Velocidad radial, mu, corrimiento doppler, primer momento | |
|
949 | Sigma2 = abs(numpy.nansum( yNorm * ( xSamples - Vr )**2 )) # Segundo Momento | |
|
950 | Desv = Sigma2**0.5 # Desv. Estandar, Ancho espectral | |
|
929 | 951 | |
|
930 | 952 | return numpy.array([Pot, Vr, Desv]) |
|
931 | 953 | |
@@ -1171,7 +1193,366 class FullSpectralAnalysis(Operation): | |||
|
1171 | 1193 | |
|
1172 | 1194 | |
|
1173 | 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 | 1556 | class SpectralMoments(Operation): |
|
1176 | 1557 | |
|
1177 | 1558 | ''' |
General Comments 0
You need to be logged in to leave comments.
Login now