##// END OF EJS Templates
Changes to meteor detection and phase correction because of relocation of antenna
Julio Valdez -
r819:c63b6bff3798
parent child
Show More
@@ -1,2181 +1,2185
1 1 import numpy
2 2 import math
3 3 from scipy import optimize
4 4 from scipy import interpolate
5 5 from scipy import signal
6 6 from scipy import stats
7 7 import re
8 8 import datetime
9 9 import copy
10 10 import sys
11 11 import importlib
12 12 import itertools
13 13
14 14 from jroproc_base import ProcessingUnit, Operation
15 15 from schainpy.model.data.jrodata import Parameters
16 16
17 17
18 18 class ParametersProc(ProcessingUnit):
19 19
20 20 nSeconds = None
21 21
22 22 def __init__(self):
23 23 ProcessingUnit.__init__(self)
24 24
25 25 # self.objectDict = {}
26 26 self.buffer = None
27 27 self.firstdatatime = None
28 28 self.profIndex = 0
29 29 self.dataOut = Parameters()
30 30
31 31 def __updateObjFromInput(self):
32 32
33 33 self.dataOut.inputUnit = self.dataIn.type
34 34
35 35 self.dataOut.timeZone = self.dataIn.timeZone
36 36 self.dataOut.dstFlag = self.dataIn.dstFlag
37 37 self.dataOut.errorCount = self.dataIn.errorCount
38 38 self.dataOut.useLocalTime = self.dataIn.useLocalTime
39 39
40 40 self.dataOut.radarControllerHeaderObj = self.dataIn.radarControllerHeaderObj.copy()
41 41 self.dataOut.systemHeaderObj = self.dataIn.systemHeaderObj.copy()
42 42 self.dataOut.channelList = self.dataIn.channelList
43 43 self.dataOut.heightList = self.dataIn.heightList
44 44 self.dataOut.dtype = numpy.dtype([('real','<f4'),('imag','<f4')])
45 45 # self.dataOut.nHeights = self.dataIn.nHeights
46 46 # self.dataOut.nChannels = self.dataIn.nChannels
47 47 self.dataOut.nBaud = self.dataIn.nBaud
48 48 self.dataOut.nCode = self.dataIn.nCode
49 49 self.dataOut.code = self.dataIn.code
50 50 # self.dataOut.nProfiles = self.dataOut.nFFTPoints
51 51 self.dataOut.flagDiscontinuousBlock = self.dataIn.flagDiscontinuousBlock
52 52 self.dataOut.utctime = self.firstdatatime
53 53 self.dataOut.flagDecodeData = self.dataIn.flagDecodeData #asumo q la data esta decodificada
54 54 self.dataOut.flagDeflipData = self.dataIn.flagDeflipData #asumo q la data esta sin flip
55 55 # self.dataOut.nCohInt = self.dataIn.nCohInt
56 56 # self.dataOut.nIncohInt = 1
57 57 self.dataOut.ippSeconds = self.dataIn.ippSeconds
58 58 # self.dataOut.windowOfFilter = self.dataIn.windowOfFilter
59 59 self.dataOut.timeInterval = self.dataIn.timeInterval
60 60 self.dataOut.heightList = self.dataIn.getHeiRange()
61 61 self.dataOut.frequency = self.dataIn.frequency
62 62
63 63 def run(self, nSeconds = 100, nProfiles = None):
64 64
65 65
66 66
67 67 if self.firstdatatime == None:
68 68 self.firstdatatime = self.dataIn.utctime
69 69
70 70 #---------------------- Voltage Data ---------------------------
71 71
72 72 if self.dataIn.type == "Voltage":
73 73 self.dataOut.flagNoData = True
74 74
75 75
76 76 if self.buffer == None:
77 77 self.nSeconds = nSeconds
78 78 self.nProfiles= int(numpy.floor(nSeconds/(self.dataIn.ippSeconds*self.dataIn.nCohInt)))
79 79
80 80 self.buffer = numpy.zeros((self.dataIn.nChannels,
81 81 self.nProfiles,
82 82 self.dataIn.nHeights),
83 83 dtype='complex')
84 84
85 85 if self.profIndex == 7990:
86 86 a = 1
87 87
88 88 self.buffer[:,self.profIndex,:] = self.dataIn.data.copy()
89 89 self.profIndex += 1
90 90
91 91 if self.profIndex == self.nProfiles:
92 92
93 93 self.__updateObjFromInput()
94 94 self.dataOut.data_pre = self.buffer.copy()
95 95 self.dataOut.paramInterval = nSeconds
96 96 self.dataOut.flagNoData = False
97 97
98 98 self.buffer = None
99 99 self.firstdatatime = None
100 100 self.profIndex = 0
101 101 return
102 102
103 103 #---------------------- Spectra Data ---------------------------
104 104
105 105 if self.dataIn.type == "Spectra":
106 106 self.dataOut.data_pre = self.dataIn.data_spc.copy()
107 107 self.dataOut.abscissaList = self.dataIn.getVelRange(1)
108 108 self.dataOut.noise = self.dataIn.getNoise()
109 109 self.dataOut.normFactor = self.dataIn.normFactor
110 110 self.dataOut.groupList = self.dataIn.pairsList
111 111 self.dataOut.flagNoData = False
112 112
113 113 #---------------------- Correlation Data ---------------------------
114 114
115 115 if self.dataIn.type == "Correlation":
116 116 lagRRange = self.dataIn.lagR
117 117 indR = numpy.where(lagRRange == 0)[0][0]
118 118
119 119 self.dataOut.data_pre = self.dataIn.data_corr.copy()[:,:,indR,:]
120 120 self.dataOut.abscissaList = self.dataIn.getLagTRange(1)
121 121 self.dataOut.noise = self.dataIn.noise
122 122 self.dataOut.normFactor = self.dataIn.normFactor
123 123 self.dataOut.data_SNR = self.dataIn.SNR
124 124 self.dataOut.groupList = self.dataIn.pairsList
125 125 self.dataOut.flagNoData = False
126 126
127 127 #---------------------- Correlation Data ---------------------------
128 128
129 129 if self.dataIn.type == "Parameters":
130 130 self.dataOut.copy(self.dataIn)
131 131 self.dataOut.flagNoData = False
132 132
133 133 return True
134 134
135 135 self.__updateObjFromInput()
136 136 self.firstdatatime = None
137 137 self.dataOut.utctimeInit = self.dataIn.utctime
138 138 self.dataOut.paramInterval = self.dataIn.timeInterval
139 139
140 140 #------------------- Get Moments ----------------------------------
141 141 def GetMoments(self, channelList = None):
142 142 '''
143 143 Function GetMoments()
144 144
145 145 Input:
146 146 channelList : simple channel list to select e.g. [2,3,7]
147 147 self.dataOut.data_pre
148 148 self.dataOut.abscissaList
149 149 self.dataOut.noise
150 150
151 151 Affected:
152 152 self.dataOut.data_param
153 153 self.dataOut.data_SNR
154 154
155 155 '''
156 156 data = self.dataOut.data_pre
157 157 absc = self.dataOut.abscissaList[:-1]
158 158 noise = self.dataOut.noise
159 159
160 160 data_param = numpy.zeros((data.shape[0], 4, data.shape[2]))
161 161
162 162 if channelList== None:
163 163 channelList = self.dataIn.channelList
164 164 self.dataOut.channelList = channelList
165 165
166 166 for ind in channelList:
167 167 data_param[ind,:,:] = self.__calculateMoments(data[ind,:,:], absc, noise[ind])
168 168
169 169 self.dataOut.data_param = data_param[:,1:,:]
170 170 self.dataOut.data_SNR = data_param[:,0]
171 171 return
172 172
173 173 def __calculateMoments(self, oldspec, oldfreq, n0, nicoh = None, graph = None, smooth = None, type1 = None, fwindow = None, snrth = None, dc = None, aliasing = None, oldfd = None, wwauto = None):
174 174
175 175 if (nicoh == None): nicoh = 1
176 176 if (graph == None): graph = 0
177 177 if (smooth == None): smooth = 0
178 178 elif (self.smooth < 3): smooth = 0
179 179
180 180 if (type1 == None): type1 = 0
181 181 if (fwindow == None): fwindow = numpy.zeros(oldfreq.size) + 1
182 182 if (snrth == None): snrth = -3
183 183 if (dc == None): dc = 0
184 184 if (aliasing == None): aliasing = 0
185 185 if (oldfd == None): oldfd = 0
186 186 if (wwauto == None): wwauto = 0
187 187
188 188 if (n0 < 1.e-20): n0 = 1.e-20
189 189
190 190 freq = oldfreq
191 191 vec_power = numpy.zeros(oldspec.shape[1])
192 192 vec_fd = numpy.zeros(oldspec.shape[1])
193 193 vec_w = numpy.zeros(oldspec.shape[1])
194 194 vec_snr = numpy.zeros(oldspec.shape[1])
195 195
196 196 for ind in range(oldspec.shape[1]):
197 197
198 198 spec = oldspec[:,ind]
199 199 aux = spec*fwindow
200 200 max_spec = aux.max()
201 201 m = list(aux).index(max_spec)
202 202
203 203 #Smooth
204 204 if (smooth == 0): spec2 = spec
205 205 else: spec2 = scipy.ndimage.filters.uniform_filter1d(spec,size=smooth)
206 206
207 207 # Calculo de Momentos
208 208 bb = spec2[range(m,spec2.size)]
209 209 bb = (bb<n0).nonzero()
210 210 bb = bb[0]
211 211
212 212 ss = spec2[range(0,m + 1)]
213 213 ss = (ss<n0).nonzero()
214 214 ss = ss[0]
215 215
216 216 if (bb.size == 0):
217 217 bb0 = spec.size - 1 - m
218 218 else:
219 219 bb0 = bb[0] - 1
220 220 if (bb0 < 0):
221 221 bb0 = 0
222 222
223 223 if (ss.size == 0): ss1 = 1
224 224 else: ss1 = max(ss) + 1
225 225
226 226 if (ss1 > m): ss1 = m
227 227
228 228 valid = numpy.asarray(range(int(m + bb0 - ss1 + 1))) + ss1
229 229 power = ((spec2[valid] - n0)*fwindow[valid]).sum()
230 230 fd = ((spec2[valid]- n0)*freq[valid]*fwindow[valid]).sum()/power
231 231 w = math.sqrt(((spec2[valid] - n0)*fwindow[valid]*(freq[valid]- fd)**2).sum()/power)
232 232 snr = (spec2.mean()-n0)/n0
233 233
234 234 if (snr < 1.e-20) :
235 235 snr = 1.e-20
236 236
237 237 vec_power[ind] = power
238 238 vec_fd[ind] = fd
239 239 vec_w[ind] = w
240 240 vec_snr[ind] = snr
241 241
242 242 moments = numpy.vstack((vec_snr, vec_power, vec_fd, vec_w))
243 243 return moments
244 244
245 245 #------------------ Get SA Parameters --------------------------
246 246
247 247 def GetSAParameters(self):
248 248 pairslist = self.dataOut.groupList
249 249 num_pairs = len(pairslist)
250 250
251 251 vel = self.dataOut.abscissaList
252 252 spectra = self.dataOut.data_pre
253 253 cspectra = self.dataIn.data_cspc
254 254 delta_v = vel[1] - vel[0]
255 255
256 256 #Calculating the power spectrum
257 257 spc_pow = numpy.sum(spectra, 3)*delta_v
258 258 #Normalizing Spectra
259 259 norm_spectra = spectra/spc_pow
260 260 #Calculating the norm_spectra at peak
261 261 max_spectra = numpy.max(norm_spectra, 3)
262 262
263 263 #Normalizing Cross Spectra
264 264 norm_cspectra = numpy.zeros(cspectra.shape)
265 265
266 266 for i in range(num_chan):
267 267 norm_cspectra[i,:,:] = cspectra[i,:,:]/numpy.sqrt(spc_pow[pairslist[i][0],:]*spc_pow[pairslist[i][1],:])
268 268
269 269 max_cspectra = numpy.max(norm_cspectra,2)
270 270 max_cspectra_index = numpy.argmax(norm_cspectra, 2)
271 271
272 272 for i in range(num_pairs):
273 273 cspc_par[i,:,:] = __calculateMoments(norm_cspectra)
274 274 #------------------- Get Lags ----------------------------------
275 275
276 276 def GetLags(self):
277 277 '''
278 278 Function GetMoments()
279 279
280 280 Input:
281 281 self.dataOut.data_pre
282 282 self.dataOut.abscissaList
283 283 self.dataOut.noise
284 284 self.dataOut.normFactor
285 285 self.dataOut.data_SNR
286 286 self.dataOut.groupList
287 287 self.dataOut.nChannels
288 288
289 289 Affected:
290 290 self.dataOut.data_param
291 291
292 292 '''
293 293
294 294 data = self.dataOut.data_pre
295 295 normFactor = self.dataOut.normFactor
296 296 nHeights = self.dataOut.nHeights
297 297 absc = self.dataOut.abscissaList[:-1]
298 298 noise = self.dataOut.noise
299 299 SNR = self.dataOut.data_SNR
300 300 pairsList = self.dataOut.groupList
301 301 nChannels = self.dataOut.nChannels
302 302 pairsAutoCorr, pairsCrossCorr = self.__getPairsAutoCorr(pairsList, nChannels)
303 303 self.dataOut.data_param = numpy.zeros((len(pairsCrossCorr)*2 + 1, nHeights))
304 304
305 305 dataNorm = numpy.abs(data)
306 306 for l in range(len(pairsList)):
307 307 dataNorm[l,:,:] = dataNorm[l,:,:]/normFactor[l,:]
308 308
309 309 self.dataOut.data_param[:-1,:] = self.__calculateTaus(dataNorm, pairsCrossCorr, pairsAutoCorr, absc)
310 310 self.dataOut.data_param[-1,:] = self.__calculateLag1Phase(data, pairsAutoCorr, absc)
311 311 return
312 312
313 313 def __getPairsAutoCorr(self, pairsList, nChannels):
314 314
315 315 pairsAutoCorr = numpy.zeros(nChannels, dtype = 'int')*numpy.nan
316 316
317 317 for l in range(len(pairsList)):
318 318 firstChannel = pairsList[l][0]
319 319 secondChannel = pairsList[l][1]
320 320
321 321 #Obteniendo pares de Autocorrelacion
322 322 if firstChannel == secondChannel:
323 323 pairsAutoCorr[firstChannel] = int(l)
324 324
325 325 pairsAutoCorr = pairsAutoCorr.astype(int)
326 326
327 327 pairsCrossCorr = range(len(pairsList))
328 328 pairsCrossCorr = numpy.delete(pairsCrossCorr,pairsAutoCorr)
329 329
330 330 return pairsAutoCorr, pairsCrossCorr
331 331
332 332 def __calculateTaus(self, data, pairsCrossCorr, pairsAutoCorr, lagTRange):
333 333
334 334 Pt0 = data.shape[1]/2
335 335 #Funcion de Autocorrelacion
336 336 dataAutoCorr = stats.nanmean(data[pairsAutoCorr,:,:], axis = 0)
337 337
338 338 #Obtencion Indice de TauCross
339 339 indCross = data[pairsCrossCorr,:,:].argmax(axis = 1)
340 340 #Obtencion Indice de TauAuto
341 341 indAuto = numpy.zeros(indCross.shape,dtype = 'int')
342 342 CCValue = data[pairsCrossCorr,Pt0,:]
343 343 for i in range(pairsCrossCorr.size):
344 344 indAuto[i,:] = numpy.abs(dataAutoCorr - CCValue[i,:]).argmin(axis = 0)
345 345
346 346 #Obtencion de TauCross y TauAuto
347 347 tauCross = lagTRange[indCross]
348 348 tauAuto = lagTRange[indAuto]
349 349
350 350 Nan1, Nan2 = numpy.where(tauCross == lagTRange[0])
351 351
352 352 tauCross[Nan1,Nan2] = numpy.nan
353 353 tauAuto[Nan1,Nan2] = numpy.nan
354 354 tau = numpy.vstack((tauCross,tauAuto))
355 355
356 356 return tau
357 357
358 358 def __calculateLag1Phase(self, data, pairs, lagTRange):
359 359 data1 = stats.nanmean(data[pairs,:,:], axis = 0)
360 360 lag1 = numpy.where(lagTRange == 0)[0][0] + 1
361 361
362 362 phase = numpy.angle(data1[lag1,:])
363 363
364 364 return phase
365 365 #------------------- Detect Meteors ------------------------------
366 366
367 367 def MeteorDetection(self, hei_ref = None, tauindex = 0,
368 predefinedPhaseShifts = None, centerReceiverIndex = 2,
368 phaseOffsets = None,
369 369 cohDetection = False, cohDet_timeStep = 1, cohDet_thresh = 25,
370 370 noise_timeStep = 4, noise_multiple = 4,
371 371 multDet_timeLimit = 1, multDet_rangeLimit = 3,
372 372 phaseThresh = 20, SNRThresh = 8,
373 hmin = 50, hmax=150, azimuth = 0) :
373 hmin = 50, hmax=150, azimuth = 0,
374 channel25X = 0, channel20X = 4, channelCentX = 2,
375 channel25Y = 1, channel20Y = 3, channelCentY = 2) :
374 376
375 377 '''
376 378 Function DetectMeteors()
377 379 Project developed with paper:
378 380 HOLDSWORTH ET AL. 2004
379 381
380 382 Input:
381 383 self.dataOut.data_pre
382 384
383 385 centerReceiverIndex: From the channels, which is the center receiver
384 386
385 387 hei_ref: Height reference for the Beacon signal extraction
386 388 tauindex:
387 389 predefinedPhaseShifts: Predefined phase offset for the voltge signals
388 390
389 391 cohDetection: Whether to user Coherent detection or not
390 392 cohDet_timeStep: Coherent Detection calculation time step
391 393 cohDet_thresh: Coherent Detection phase threshold to correct phases
392 394
393 395 noise_timeStep: Noise calculation time step
394 396 noise_multiple: Noise multiple to define signal threshold
395 397
396 398 multDet_timeLimit: Multiple Detection Removal time limit in seconds
397 399 multDet_rangeLimit: Multiple Detection Removal range limit in km
398 400
399 401 phaseThresh: Maximum phase difference between receiver to be consider a meteor
400 402 SNRThresh: Minimum SNR threshold of the meteor signal to be consider a meteor
401 403
402 404 hmin: Minimum Height of the meteor to use it in the further wind estimations
403 405 hmax: Maximum Height of the meteor to use it in the further wind estimations
404 406 azimuth: Azimuth angle correction
405 407
406 408 Affected:
407 409 self.dataOut.data_param
408 410
409 411 Rejection Criteria (Errors):
410 412 0: No error; analysis OK
411 413 1: SNR < SNR threshold
412 414 2: angle of arrival (AOA) ambiguously determined
413 415 3: AOA estimate not feasible
414 416 4: Large difference in AOAs obtained from different antenna baselines
415 417 5: echo at start or end of time series
416 418 6: echo less than 5 examples long; too short for analysis
417 419 7: echo rise exceeds 0.3s
418 420 8: echo decay time less than twice rise time
419 421 9: large power level before echo
420 422 10: large power level after echo
421 423 11: poor fit to amplitude for estimation of decay time
422 424 12: poor fit to CCF phase variation for estimation of radial drift velocity
423 425 13: height unresolvable echo: not valid height within 70 to 110 km
424 426 14: height ambiguous echo: more then one possible height within 70 to 110 km
425 427 15: radial drift velocity or projected horizontal velocity exceeds 200 m/s
426 428 16: oscilatory echo, indicating event most likely not an underdense echo
427 429
428 430 17: phase difference in meteor Reestimation
429 431
430 432 Data Storage:
431 433 Meteors for Wind Estimation (8):
432 434 Utc Time | Range Height
433 435 Azimuth Zenith errorCosDir
434 436 VelRad errorVelRad
435 437 Phase0 Phase1 Phase2 Phase3
436 438 TypeError
437 439
438 440 '''
439 441 #Get Beacon signal
440 442 newheis = numpy.where(self.dataOut.heightList>self.dataOut.radarControllerHeaderObj.Taus[tauindex])
441 443
442 444 if hei_ref != None:
443 445 newheis = numpy.where(self.dataOut.heightList>hei_ref)
444 446
445 447 heiRang = self.dataOut.getHeiRange()
446 448 #Pairs List
447 449 '''
448 450 Cambiar este pairslist
449 451 '''
450 pairslist = []
451 nChannel = self.dataOut.nChannels
452 for i in range(nChannel):
453 if i != centerReceiverIndex:
454 pairslist.append((centerReceiverIndex,i))
452 pairslist = [(channelCentX, channel25X),(channelCentX, channel20X),(channelCentY,channel25Y),(channelCentY, channel20Y)]
453
454 # nChannel = self.dataOut.nChannels
455 # for i in range(nChannel):
456 # if i != centerReceiverIndex:
457 # pairslist.append((centerReceiverIndex,i))
455 458
456 459 #****************REMOVING HARDWARE PHASE DIFFERENCES***************
457 460 # see if the user put in pre defined phase shifts
458 461 voltsPShift = self.dataOut.data_pre.copy()
459 462
460 if predefinedPhaseShifts != None:
461 hardwarePhaseShifts = numpy.array(predefinedPhaseShifts)*numpy.pi/180
462
463 # elif beaconPhaseShifts:
464 # #get hardware phase shifts using beacon signal
465 # hardwarePhaseShifts = self.__getHardwarePhaseDiff(self.dataOut.data_pre, pairslist, newheis, 10)
466 # hardwarePhaseShifts = numpy.insert(hardwarePhaseShifts,centerReceiverIndex,0)
467
468 else:
469 hardwarePhaseShifts = numpy.zeros(5)
470
471
472 voltsPShift = numpy.zeros((self.dataOut.data_pre.shape[0],self.dataOut.data_pre.shape[1],self.dataOut.data_pre.shape[2]), dtype = 'complex')
473 for i in range(self.dataOut.data_pre.shape[0]):
474 voltsPShift[i,:,:] = self.__shiftPhase(self.dataOut.data_pre[i,:,:], hardwarePhaseShifts[i])
475
463 # if predefinedPhaseShifts != None:
464 # hardwarePhaseShifts = numpy.array(predefinedPhaseShifts)*numpy.pi/180
465 #
466 # # elif beaconPhaseShifts:
467 # # #get hardware phase shifts using beacon signal
468 # # hardwarePhaseShifts = self.__getHardwarePhaseDiff(self.dataOut.data_pre, pairslist, newheis, 10)
469 # # hardwarePhaseShifts = numpy.insert(hardwarePhaseShifts,centerReceiverIndex,0)
470 #
471 # else:
472 # hardwarePhaseShifts = numpy.zeros(5)
473 #
474 # voltsPShift = numpy.zeros((self.dataOut.data_pre.shape[0],self.dataOut.data_pre.shape[1],self.dataOut.data_pre.shape[2]), dtype = 'complex')
475 # for i in range(self.dataOut.data_pre.shape[0]):
476 # voltsPShift[i,:,:] = self.__shiftPhase(self.dataOut.data_pre[i,:,:], hardwarePhaseShifts[i])
476 477
477 478 #******************END OF REMOVING HARDWARE PHASE DIFFERENCES*********
478 479
479 480 #Remove DC
480 481 voltsDC = numpy.mean(voltsPShift,1)
481 482 voltsDC = numpy.mean(voltsDC,1)
482 483 for i in range(voltsDC.shape[0]):
483 484 voltsPShift[i] = voltsPShift[i] - voltsDC[i]
484 485
485 486 #Don't considerate last heights, theyre used to calculate Hardware Phase Shift
486 487 voltsPShift = voltsPShift[:,:,:newheis[0][0]]
487 488
488 489 #************ FIND POWER OF DATA W/COH OR NON COH DETECTION (3.4) **********
489 490 #Coherent Detection
490 491 if cohDetection:
491 492 #use coherent detection to get the net power
492 493 cohDet_thresh = cohDet_thresh*numpy.pi/180
493 494 voltsPShift = self.__coherentDetection(voltsPShift, cohDet_timeStep, self.dataOut.timeInterval, pairslist, cohDet_thresh)
494 495
495 496 #Non-coherent detection!
496 497 powerNet = numpy.nansum(numpy.abs(voltsPShift[:,:,:])**2,0)
497 498 #********** END OF COH/NON-COH POWER CALCULATION**********************
498 499
499 500 #********** FIND THE NOISE LEVEL AND POSSIBLE METEORS ****************
500 501 #Get noise
501 502 noise, noise1 = self.__getNoise(powerNet, noise_timeStep, self.dataOut.timeInterval)
502 503 # noise = self.getNoise1(powerNet, noise_timeStep, self.dataOut.timeInterval)
503 504 #Get signal threshold
504 505 signalThresh = noise_multiple*noise
505 506 #Meteor echoes detection
506 507 listMeteors = self.__findMeteors(powerNet, signalThresh)
507 508 #******* END OF NOISE LEVEL AND POSSIBLE METEORS CACULATION **********
508 509
509 510 #************** REMOVE MULTIPLE DETECTIONS (3.5) ***************************
510 511 #Parameters
511 512 heiRange = self.dataOut.getHeiRange()
512 513 rangeInterval = heiRange[1] - heiRange[0]
513 514 rangeLimit = multDet_rangeLimit/rangeInterval
514 515 timeLimit = multDet_timeLimit/self.dataOut.timeInterval
515 516 #Multiple detection removals
516 517 listMeteors1 = self.__removeMultipleDetections(listMeteors, rangeLimit, timeLimit)
517 518 #************ END OF REMOVE MULTIPLE DETECTIONS **********************
518 519
519 520 #********************* METEOR REESTIMATION (3.7, 3.8, 3.9, 3.10) ********************
520 521 #Parameters
521 522 phaseThresh = phaseThresh*numpy.pi/180
522 523 thresh = [phaseThresh, noise_multiple, SNRThresh]
523 524 #Meteor reestimation (Errors N 1, 6, 12, 17)
524 525 listMeteors2, listMeteorsPower, listMeteorsVolts = self.__meteorReestimation(listMeteors1, voltsPShift, pairslist, thresh, noise, self.dataOut.timeInterval, self.dataOut.frequency)
525 526 # listMeteors2, listMeteorsPower, listMeteorsVolts = self.meteorReestimation3(listMeteors2, listMeteorsPower, listMeteorsVolts, voltsPShift, pairslist, thresh, noise)
526 527 #Estimation of decay times (Errors N 7, 8, 11)
527 528 listMeteors3 = self.__estimateDecayTime(listMeteors2, listMeteorsPower, self.dataOut.timeInterval, self.dataOut.frequency)
528 529 #******************* END OF METEOR REESTIMATION *******************
529 530
530 531 #********************* METEOR PARAMETERS CALCULATION (3.11, 3.12, 3.13) **************************
531 532 #Calculating Radial Velocity (Error N 15)
532 533 radialStdThresh = 10
533 534 listMeteors4 = self.__getRadialVelocity(listMeteors3, listMeteorsVolts, radialStdThresh, pairslist, self.dataOut.timeInterval)
534 535
535 536 if len(listMeteors4) > 0:
536 537 #Setting New Array
537 538 date = self.dataOut.utctime
538 539 arrayParameters = self.__setNewArrays(listMeteors4, date, heiRang)
539 540
541 #Correcting phase offset
542 if phaseOffsets != None:
543 phaseOffsets = numpy.array(phaseOffsets)*numpy.pi/180
544 arrayParameters[:,8:12] = numpy.unwrap(arrayParameters[:,8:12] + phaseOffsets)
545
546 #Second Pairslist
540 547 pairsList = []
541 pairx = (0,3)
542 pairy = (1,2)
548 pairx = (0,1)
549 pairy = (2,3)
543 550 pairsList.append(pairx)
544 551 pairsList.append(pairy)
545 552
546 553 meteorOps = MeteorOperations()
547 554 jph = numpy.array([0,0,0,0])
548 555 h = (hmin,hmax)
549 556 arrayParameters = meteorOps.getMeteorParams(arrayParameters, azimuth, h, pairsList, jph)
550 557
551 558 # #Calculate AOA (Error N 3, 4)
552 559 # #JONES ET AL. 1998
553 560 # error = arrayParameters[:,-1]
554 561 # AOAthresh = numpy.pi/8
555 562 # phases = -arrayParameters[:,9:13]
556 563 # arrayParameters[:,4:7], arrayParameters[:,-1] = meteorOps.getAOA(phases, pairsList, error, AOAthresh, azimuth)
557 564 #
558 565 # #Calculate Heights (Error N 13 and 14)
559 566 # error = arrayParameters[:,-1]
560 567 # Ranges = arrayParameters[:,2]
561 568 # zenith = arrayParameters[:,5]
562 569 # arrayParameters[:,3], arrayParameters[:,-1] = meteorOps.getHeights(Ranges, zenith, error, hmin, hmax)
563 570 # error = arrayParameters[:,-1]
564 571 #********************* END OF PARAMETERS CALCULATION **************************
565 572
566 573 #***************************+ PASS DATA TO NEXT STEP **********************
567 574 # arrayFinal = arrayParameters.reshape((1,arrayParameters.shape[0],arrayParameters.shape[1]))
568 575 self.dataOut.data_param = arrayParameters
569 576
570 577 if arrayParameters == None:
571 578 self.dataOut.flagNoData = True
572 579
573 580 return
574 581
575 582 def CorrectMeteorPhases(self, phaseOffsets, hmin = 50, hmax = 150, azimuth = 45):
576 583
577 584 arrayParameters = self.dataOut.data_param
578 585 pairsList = []
579 586 pairx = (0,3)
580 587 pairy = (1,2)
581 588 pairsList.append(pairx)
582 589 pairsList.append(pairy)
583 590 jph = numpy.zeros(4)
584 591
585 592 phaseOffsets = numpy.array(phaseOffsets)*numpy.pi/180
586 593 arrayParameters[:,8:12] = numpy.unwrap(arrayParameters[:,8:12] + phaseOffsets)
587 594
588 595 meteorOps = MeteorOperations()
589 596 h = (hmin,hmax)
590 597
591 598 arrayParameters = meteorOps.getMeteorParams(arrayParameters, azimuth, h, pairsList, jph)
592 599 self.dataOut.data_param = arrayParameters
593 600 return
594 601
595 602 def __getHardwarePhaseDiff(self, voltage0, pairslist, newheis, n):
596 603
597 604 minIndex = min(newheis[0])
598 605 maxIndex = max(newheis[0])
599 606
600 607 voltage = voltage0[:,:,minIndex:maxIndex+1]
601 608 nLength = voltage.shape[1]/n
602 609 nMin = 0
603 610 nMax = 0
604 611 phaseOffset = numpy.zeros((len(pairslist),n))
605 612
606 613 for i in range(n):
607 614 nMax += nLength
608 615 phaseCCF = -numpy.angle(self.__calculateCCF(voltage[:,nMin:nMax,:], pairslist, [0]))
609 616 phaseCCF = numpy.mean(phaseCCF, axis = 2)
610 617 phaseOffset[:,i] = phaseCCF.transpose()
611 618 nMin = nMax
612 619 # phaseDiff, phaseArrival = self.estimatePhaseDifference(voltage, pairslist)
613 620
614 621 #Remove Outliers
615 622 factor = 2
616 623 wt = phaseOffset - signal.medfilt(phaseOffset,(1,5))
617 624 dw = numpy.std(wt,axis = 1)
618 625 dw = dw.reshape((dw.size,1))
619 626 ind = numpy.where(numpy.logical_or(wt>dw*factor,wt<-dw*factor))
620 627 phaseOffset[ind] = numpy.nan
621 628 phaseOffset = stats.nanmean(phaseOffset, axis=1)
622 629
623 630 return phaseOffset
624 631
625 632 def __shiftPhase(self, data, phaseShift):
626 633 #this will shift the phase of a complex number
627 634 dataShifted = numpy.abs(data) * numpy.exp((numpy.angle(data)+phaseShift)*1j)
628 635 return dataShifted
629 636
630 637 def __estimatePhaseDifference(self, array, pairslist):
631 638 nChannel = array.shape[0]
632 639 nHeights = array.shape[2]
633 640 numPairs = len(pairslist)
634 641 # phaseCCF = numpy.zeros((nChannel, 5, nHeights))
635 642 phaseCCF = numpy.angle(self.__calculateCCF(array, pairslist, [-2,-1,0,1,2]))
636 643
637 644 #Correct phases
638 645 derPhaseCCF = phaseCCF[:,1:,:] - phaseCCF[:,0:-1,:]
639 646 indDer = numpy.where(numpy.abs(derPhaseCCF) > numpy.pi)
640 647
641 648 if indDer[0].shape[0] > 0:
642 649 for i in range(indDer[0].shape[0]):
643 650 signo = -numpy.sign(derPhaseCCF[indDer[0][i],indDer[1][i],indDer[2][i]])
644 651 phaseCCF[indDer[0][i],indDer[1][i]+1:,:] += signo*2*numpy.pi
645 652
646 653 # for j in range(numSides):
647 654 # phaseCCFAux = self.calculateCCF(arrayCenter, arraySides[j,:,:], [-2,1,0,1,2])
648 655 # phaseCCF[j,:,:] = numpy.angle(phaseCCFAux)
649 656 #
650 657 #Linear
651 658 phaseInt = numpy.zeros((numPairs,1))
652 659 angAllCCF = phaseCCF[:,[0,1,3,4],0]
653 660 for j in range(numPairs):
654 661 fit = stats.linregress([-2,-1,1,2],angAllCCF[j,:])
655 662 phaseInt[j] = fit[1]
656 663 #Phase Differences
657 664 phaseDiff = phaseInt - phaseCCF[:,2,:]
658 665 phaseArrival = phaseInt.reshape(phaseInt.size)
659 666
660 667 #Dealias
661 668 indAlias = numpy.where(phaseArrival > numpy.pi)
662 669 phaseArrival[indAlias] -= 2*numpy.pi
663 670 indAlias = numpy.where(phaseArrival < -numpy.pi)
664 671 phaseArrival[indAlias] += 2*numpy.pi
665 672
666 673 return phaseDiff, phaseArrival
667 674
668 675 def __coherentDetection(self, volts, timeSegment, timeInterval, pairslist, thresh):
669 676 #this function will run the coherent detection used in Holdworth et al. 2004 and return the net power
670 677 #find the phase shifts of each channel over 1 second intervals
671 678 #only look at ranges below the beacon signal
672 679 numProfPerBlock = numpy.ceil(timeSegment/timeInterval)
673 680 numBlocks = int(volts.shape[1]/numProfPerBlock)
674 681 numHeights = volts.shape[2]
675 682 nChannel = volts.shape[0]
676 683 voltsCohDet = volts.copy()
677 684
678 685 pairsarray = numpy.array(pairslist)
679 686 indSides = pairsarray[:,1]
680 687 # indSides = numpy.array(range(nChannel))
681 688 # indSides = numpy.delete(indSides, indCenter)
682 689 #
683 690 # listCenter = numpy.array_split(volts[indCenter,:,:], numBlocks, 0)
684 691 listBlocks = numpy.array_split(volts, numBlocks, 1)
685 692
686 693 startInd = 0
687 694 endInd = 0
688 695
689 696 for i in range(numBlocks):
690 697 startInd = endInd
691 698 endInd = endInd + listBlocks[i].shape[1]
692 699
693 700 arrayBlock = listBlocks[i]
694 701 # arrayBlockCenter = listCenter[i]
695 702
696 703 #Estimate the Phase Difference
697 704 phaseDiff, aux = self.__estimatePhaseDifference(arrayBlock, pairslist)
698 705 #Phase Difference RMS
699 706 arrayPhaseRMS = numpy.abs(phaseDiff)
700 707 phaseRMSaux = numpy.sum(arrayPhaseRMS < thresh,0)
701 708 indPhase = numpy.where(phaseRMSaux==4)
702 709 #Shifting
703 710 if indPhase[0].shape[0] > 0:
704 711 for j in range(indSides.size):
705 712 arrayBlock[indSides[j],:,indPhase] = self.__shiftPhase(arrayBlock[indSides[j],:,indPhase], phaseDiff[j,indPhase].transpose())
706 713 voltsCohDet[:,startInd:endInd,:] = arrayBlock
707 714
708 715 return voltsCohDet
709 716
710 717 def __calculateCCF(self, volts, pairslist ,laglist):
711 718
712 719 nHeights = volts.shape[2]
713 720 nPoints = volts.shape[1]
714 721 voltsCCF = numpy.zeros((len(pairslist), len(laglist), nHeights),dtype = 'complex')
715 722
716 723 for i in range(len(pairslist)):
717 724 volts1 = volts[pairslist[i][0]]
718 725 volts2 = volts[pairslist[i][1]]
719 726
720 727 for t in range(len(laglist)):
721 728 idxT = laglist[t]
722 729 if idxT >= 0:
723 730 vStacked = numpy.vstack((volts2[idxT:,:],
724 731 numpy.zeros((idxT, nHeights),dtype='complex')))
725 732 else:
726 733 vStacked = numpy.vstack((numpy.zeros((-idxT, nHeights),dtype='complex'),
727 734 volts2[:(nPoints + idxT),:]))
728 735 voltsCCF[i,t,:] = numpy.sum((numpy.conjugate(volts1)*vStacked),axis=0)
729 736
730 737 vStacked = None
731 738 return voltsCCF
732 739
733 740 def __getNoise(self, power, timeSegment, timeInterval):
734 741 numProfPerBlock = numpy.ceil(timeSegment/timeInterval)
735 742 numBlocks = int(power.shape[0]/numProfPerBlock)
736 743 numHeights = power.shape[1]
737 744
738 745 listPower = numpy.array_split(power, numBlocks, 0)
739 746 noise = numpy.zeros((power.shape[0], power.shape[1]))
740 747 noise1 = numpy.zeros((power.shape[0], power.shape[1]))
741 748
742 749 startInd = 0
743 750 endInd = 0
744 751
745 752 for i in range(numBlocks): #split por canal
746 753 startInd = endInd
747 754 endInd = endInd + listPower[i].shape[0]
748 755
749 756 arrayBlock = listPower[i]
750 757 noiseAux = numpy.mean(arrayBlock, 0)
751 758 # noiseAux = numpy.median(noiseAux)
752 759 # noiseAux = numpy.mean(arrayBlock)
753 760 noise[startInd:endInd,:] = noise[startInd:endInd,:] + noiseAux
754 761
755 762 noiseAux1 = numpy.mean(arrayBlock)
756 763 noise1[startInd:endInd,:] = noise1[startInd:endInd,:] + noiseAux1
757 764
758 765 return noise, noise1
759 766
760 767 def __findMeteors(self, power, thresh):
761 768 nProf = power.shape[0]
762 769 nHeights = power.shape[1]
763 770 listMeteors = []
764 771
765 772 for i in range(nHeights):
766 773 powerAux = power[:,i]
767 774 threshAux = thresh[:,i]
768 775
769 776 indUPthresh = numpy.where(powerAux > threshAux)[0]
770 777 indDNthresh = numpy.where(powerAux <= threshAux)[0]
771 778
772 779 j = 0
773 780
774 781 while (j < indUPthresh.size - 2):
775 782 if (indUPthresh[j + 2] == indUPthresh[j] + 2):
776 783 indDNAux = numpy.where(indDNthresh > indUPthresh[j])
777 784 indDNthresh = indDNthresh[indDNAux]
778 785
779 786 if (indDNthresh.size > 0):
780 787 indEnd = indDNthresh[0] - 1
781 788 indInit = indUPthresh[j]
782 789
783 790 meteor = powerAux[indInit:indEnd + 1]
784 791 indPeak = meteor.argmax() + indInit
785 792 FLA = sum(numpy.conj(meteor)*numpy.hstack((meteor[1:],0)))
786 793
787 794 listMeteors.append(numpy.array([i,indInit,indPeak,indEnd,FLA])) #CHEQUEAR!!!!!
788 795 j = numpy.where(indUPthresh == indEnd)[0] + 1
789 796 else: j+=1
790 797 else: j+=1
791 798
792 799 return listMeteors
793 800
794 801 def __removeMultipleDetections(self,listMeteors, rangeLimit, timeLimit):
795 802
796 803 arrayMeteors = numpy.asarray(listMeteors)
797 804 listMeteors1 = []
798 805
799 806 while arrayMeteors.shape[0] > 0:
800 807 FLAs = arrayMeteors[:,4]
801 808 maxFLA = FLAs.argmax()
802 809 listMeteors1.append(arrayMeteors[maxFLA,:])
803 810
804 811 MeteorInitTime = arrayMeteors[maxFLA,1]
805 812 MeteorEndTime = arrayMeteors[maxFLA,3]
806 813 MeteorHeight = arrayMeteors[maxFLA,0]
807 814
808 815 #Check neighborhood
809 816 maxHeightIndex = MeteorHeight + rangeLimit
810 817 minHeightIndex = MeteorHeight - rangeLimit
811 818 minTimeIndex = MeteorInitTime - timeLimit
812 819 maxTimeIndex = MeteorEndTime + timeLimit
813 820
814 821 #Check Heights
815 822 indHeight = numpy.logical_and(arrayMeteors[:,0] >= minHeightIndex, arrayMeteors[:,0] <= maxHeightIndex)
816 823 indTime = numpy.logical_and(arrayMeteors[:,3] >= minTimeIndex, arrayMeteors[:,1] <= maxTimeIndex)
817 824 indBoth = numpy.where(numpy.logical_and(indTime,indHeight))
818 825
819 826 arrayMeteors = numpy.delete(arrayMeteors, indBoth, axis = 0)
820 827
821 828 return listMeteors1
822 829
823 830 def __meteorReestimation(self, listMeteors, volts, pairslist, thresh, noise, timeInterval,frequency):
824 831 numHeights = volts.shape[2]
825 832 nChannel = volts.shape[0]
826 833
827 834 thresholdPhase = thresh[0]
828 835 thresholdNoise = thresh[1]
829 836 thresholdDB = float(thresh[2])
830 837
831 838 thresholdDB1 = 10**(thresholdDB/10)
832 839 pairsarray = numpy.array(pairslist)
833 840 indSides = pairsarray[:,1]
834 841
835 842 pairslist1 = list(pairslist)
836 843 pairslist1.append((0,1))
837 844 pairslist1.append((3,4))
838 845
839 846 listMeteors1 = []
840 847 listPowerSeries = []
841 848 listVoltageSeries = []
842 849 #volts has the war data
843 850
844 851 if frequency == 30e6:
845 852 timeLag = 45*10**-3
846 853 else:
847 854 timeLag = 15*10**-3
848 855 lag = numpy.ceil(timeLag/timeInterval)
849 856
850 857 for i in range(len(listMeteors)):
851 858
852 859 ###################### 3.6 - 3.7 PARAMETERS REESTIMATION #########################
853 860 meteorAux = numpy.zeros(16)
854 861
855 862 #Loading meteor Data (mHeight, mStart, mPeak, mEnd)
856 863 mHeight = listMeteors[i][0]
857 864 mStart = listMeteors[i][1]
858 865 mPeak = listMeteors[i][2]
859 866 mEnd = listMeteors[i][3]
860 867
861 868 #get the volt data between the start and end times of the meteor
862 869 meteorVolts = volts[:,mStart:mEnd+1,mHeight]
863 870 meteorVolts = meteorVolts.reshape(meteorVolts.shape[0], meteorVolts.shape[1], 1)
864 871
865 872 #3.6. Phase Difference estimation
866 873 phaseDiff, aux = self.__estimatePhaseDifference(meteorVolts, pairslist)
867 874
868 875 #3.7. Phase difference removal & meteor start, peak and end times reestimated
869 876 #meteorVolts0.- all Channels, all Profiles
870 877 meteorVolts0 = volts[:,:,mHeight]
871 878 meteorThresh = noise[:,mHeight]*thresholdNoise
872 879 meteorNoise = noise[:,mHeight]
873 880 meteorVolts0[indSides,:] = self.__shiftPhase(meteorVolts0[indSides,:], phaseDiff) #Phase Shifting
874 881 powerNet0 = numpy.nansum(numpy.abs(meteorVolts0)**2, axis = 0) #Power
875 882
876 883 #Times reestimation
877 884 mStart1 = numpy.where(powerNet0[:mPeak] < meteorThresh[:mPeak])[0]
878 885 if mStart1.size > 0:
879 886 mStart1 = mStart1[-1] + 1
880 887
881 888 else:
882 889 mStart1 = mPeak
883 890
884 891 mEnd1 = numpy.where(powerNet0[mPeak:] < meteorThresh[mPeak:])[0][0] + mPeak - 1
885 892 mEndDecayTime1 = numpy.where(powerNet0[mPeak:] < meteorNoise[mPeak:])[0]
886 893 if mEndDecayTime1.size == 0:
887 894 mEndDecayTime1 = powerNet0.size
888 895 else:
889 896 mEndDecayTime1 = mEndDecayTime1[0] + mPeak - 1
890 897 # mPeak1 = meteorVolts0[mStart1:mEnd1 + 1].argmax()
891 898
892 899 #meteorVolts1.- all Channels, from start to end
893 900 meteorVolts1 = meteorVolts0[:,mStart1:mEnd1 + 1]
894 901 meteorVolts2 = meteorVolts0[:,mPeak + lag:mEnd1 + 1]
895 902 if meteorVolts2.shape[1] == 0:
896 903 meteorVolts2 = meteorVolts0[:,mPeak:mEnd1 + 1]
897 904 meteorVolts1 = meteorVolts1.reshape(meteorVolts1.shape[0], meteorVolts1.shape[1], 1)
898 905 meteorVolts2 = meteorVolts2.reshape(meteorVolts2.shape[0], meteorVolts2.shape[1], 1)
899 906 ##################### END PARAMETERS REESTIMATION #########################
900 907
901 908 ##################### 3.8 PHASE DIFFERENCE REESTIMATION ########################
902 909 # if mEnd1 - mStart1 > 4: #Error Number 6: echo less than 5 samples long; too short for analysis
903 910 if meteorVolts2.shape[1] > 0:
904 911 #Phase Difference re-estimation
905 912 phaseDiff1, phaseDiffint = self.__estimatePhaseDifference(meteorVolts2, pairslist1) #Phase Difference Estimation
906 913 # phaseDiff1, phaseDiffint = self.estimatePhaseDifference(meteorVolts2, pairslist)
907 914 meteorVolts2 = meteorVolts2.reshape(meteorVolts2.shape[0], meteorVolts2.shape[1])
908 915 phaseDiff11 = numpy.reshape(phaseDiff1, (phaseDiff1.shape[0],1))
909 916 meteorVolts2[indSides,:] = self.__shiftPhase(meteorVolts2[indSides,:], phaseDiff11[0:4]) #Phase Shifting
910 917
911 918 #Phase Difference RMS
912 919 phaseRMS1 = numpy.sqrt(numpy.mean(numpy.square(phaseDiff1)))
913 920 powerNet1 = numpy.nansum(numpy.abs(meteorVolts1[:,:])**2,0)
914 921 #Data from Meteor
915 922 mPeak1 = powerNet1.argmax() + mStart1
916 923 mPeakPower1 = powerNet1.max()
917 924 noiseAux = sum(noise[mStart1:mEnd1 + 1,mHeight])
918 925 mSNR1 = (sum(powerNet1)-noiseAux)/noiseAux
919 926 Meteor1 = numpy.array([mHeight, mStart1, mPeak1, mEnd1, mPeakPower1, mSNR1, phaseRMS1])
920 927 Meteor1 = numpy.hstack((Meteor1,phaseDiffint))
921 928 PowerSeries = powerNet0[mStart1:mEndDecayTime1 + 1]
922 929 #Vectorize
923 930 meteorAux[0:7] = [mHeight, mStart1, mPeak1, mEnd1, mPeakPower1, mSNR1, phaseRMS1]
924 931 meteorAux[7:11] = phaseDiffint[0:4]
925 932
926 933 #Rejection Criterions
927 934 if phaseRMS1 > thresholdPhase: #Error Number 17: Phase variation
928 935 meteorAux[-1] = 17
929 936 elif mSNR1 < thresholdDB1: #Error Number 1: SNR < threshold dB
930 937 meteorAux[-1] = 1
931 938
932 939
933 940 else:
934 941 meteorAux[0:4] = [mHeight, mStart, mPeak, mEnd]
935 942 meteorAux[-1] = 6 #Error Number 6: echo less than 5 samples long; too short for analysis
936 943 PowerSeries = 0
937 944
938 945 listMeteors1.append(meteorAux)
939 946 listPowerSeries.append(PowerSeries)
940 947 listVoltageSeries.append(meteorVolts1)
941 948
942 949 return listMeteors1, listPowerSeries, listVoltageSeries
943 950
944 951 def __estimateDecayTime(self, listMeteors, listPower, timeInterval, frequency):
945 952
946 953 threshError = 10
947 954 #Depending if it is 30 or 50 MHz
948 955 if frequency == 30e6:
949 956 timeLag = 45*10**-3
950 957 else:
951 958 timeLag = 15*10**-3
952 959 lag = numpy.ceil(timeLag/timeInterval)
953 960
954 961 listMeteors1 = []
955 962
956 963 for i in range(len(listMeteors)):
957 964 meteorPower = listPower[i]
958 965 meteorAux = listMeteors[i]
959 966
960 967 if meteorAux[-1] == 0:
961 968
962 969 try:
963 970 indmax = meteorPower.argmax()
964 971 indlag = indmax + lag
965 972
966 973 y = meteorPower[indlag:]
967 974 x = numpy.arange(0, y.size)*timeLag
968 975
969 976 #first guess
970 977 a = y[0]
971 978 tau = timeLag
972 979 #exponential fit
973 980 popt, pcov = optimize.curve_fit(self.__exponential_function, x, y, p0 = [a, tau])
974 981 y1 = self.__exponential_function(x, *popt)
975 982 #error estimation
976 983 error = sum((y - y1)**2)/(numpy.var(y)*(y.size - popt.size))
977 984
978 985 decayTime = popt[1]
979 986 riseTime = indmax*timeInterval
980 987 meteorAux[11:13] = [decayTime, error]
981 988
982 989 #Table items 7, 8 and 11
983 990 if (riseTime > 0.3): #Number 7: Echo rise exceeds 0.3s
984 991 meteorAux[-1] = 7
985 992 elif (decayTime < 2*riseTime) : #Number 8: Echo decay time less than than twice rise time
986 993 meteorAux[-1] = 8
987 994 if (error > threshError): #Number 11: Poor fit to amplitude for estimation of decay time
988 995 meteorAux[-1] = 11
989 996
990 997
991 998 except:
992 999 meteorAux[-1] = 11
993 1000
994 1001
995 1002 listMeteors1.append(meteorAux)
996 1003
997 1004 return listMeteors1
998 1005
999 1006 #Exponential Function
1000 1007
1001 1008 def __exponential_function(self, x, a, tau):
1002 1009 y = a*numpy.exp(-x/tau)
1003 1010 return y
1004 1011
1005 1012 def __getRadialVelocity(self, listMeteors, listVolts, radialStdThresh, pairslist, timeInterval):
1006 1013
1007 1014 pairslist1 = list(pairslist)
1008 1015 pairslist1.append((0,1))
1009 1016 pairslist1.append((3,4))
1010 1017 numPairs = len(pairslist1)
1011 1018 #Time Lag
1012 1019 timeLag = 45*10**-3
1013 1020 c = 3e8
1014 1021 lag = numpy.ceil(timeLag/timeInterval)
1015 1022 freq = 30e6
1016 1023
1017 1024 listMeteors1 = []
1018 1025
1019 1026 for i in range(len(listMeteors)):
1020 1027 meteorAux = listMeteors[i]
1021 1028 if meteorAux[-1] == 0:
1022 1029 mStart = listMeteors[i][1]
1023 1030 mPeak = listMeteors[i][2]
1024 1031 mLag = mPeak - mStart + lag
1025 1032
1026 1033 #get the volt data between the start and end times of the meteor
1027 1034 meteorVolts = listVolts[i]
1028 1035 meteorVolts = meteorVolts.reshape(meteorVolts.shape[0], meteorVolts.shape[1], 1)
1029 1036
1030 1037 #Get CCF
1031 1038 allCCFs = self.__calculateCCF(meteorVolts, pairslist1, [-2,-1,0,1,2])
1032 1039
1033 1040 #Method 2
1034 1041 slopes = numpy.zeros(numPairs)
1035 1042 time = numpy.array([-2,-1,1,2])*timeInterval
1036 1043 angAllCCF = numpy.angle(allCCFs[:,[0,1,3,4],0])
1037 1044
1038 1045 #Correct phases
1039 1046 derPhaseCCF = angAllCCF[:,1:] - angAllCCF[:,0:-1]
1040 1047 indDer = numpy.where(numpy.abs(derPhaseCCF) > numpy.pi)
1041 1048
1042 1049 if indDer[0].shape[0] > 0:
1043 1050 for i in range(indDer[0].shape[0]):
1044 1051 signo = -numpy.sign(derPhaseCCF[indDer[0][i],indDer[1][i]])
1045 1052 angAllCCF[indDer[0][i],indDer[1][i]+1:] += signo*2*numpy.pi
1046 1053
1047 1054 # fit = scipy.stats.linregress(numpy.array([-2,-1,1,2])*timeInterval, numpy.array([phaseLagN2s[i],phaseLagN1s[i],phaseLag1s[i],phaseLag2s[i]]))
1048 1055 for j in range(numPairs):
1049 1056 fit = stats.linregress(time, angAllCCF[j,:])
1050 1057 slopes[j] = fit[0]
1051 1058
1052 1059 #Remove Outlier
1053 1060 # indOut = numpy.argmax(numpy.abs(slopes - numpy.mean(slopes)))
1054 1061 # slopes = numpy.delete(slopes,indOut)
1055 1062 # indOut = numpy.argmax(numpy.abs(slopes - numpy.mean(slopes)))
1056 1063 # slopes = numpy.delete(slopes,indOut)
1057 1064
1058 1065 radialVelocity = -numpy.mean(slopes)*(0.25/numpy.pi)*(c/freq)
1059 1066 radialError = numpy.std(slopes)*(0.25/numpy.pi)*(c/freq)
1060 1067 meteorAux[-2] = radialError
1061 1068 meteorAux[-3] = radialVelocity
1062 1069
1063 1070 #Setting Error
1064 1071 #Number 15: Radial Drift velocity or projected horizontal velocity exceeds 200 m/s
1065 1072 if numpy.abs(radialVelocity) > 200:
1066 1073 meteorAux[-1] = 15
1067 1074 #Number 12: Poor fit to CCF variation for estimation of radial drift velocity
1068 1075 elif radialError > radialStdThresh:
1069 1076 meteorAux[-1] = 12
1070 1077
1071 1078 listMeteors1.append(meteorAux)
1072 1079 return listMeteors1
1073 1080
1074 1081 def __setNewArrays(self, listMeteors, date, heiRang):
1075 1082
1076 1083 #New arrays
1077 1084 arrayMeteors = numpy.array(listMeteors)
1078 1085 arrayParameters = numpy.zeros((len(listMeteors), 13))
1079 1086
1080 1087 #Date inclusion
1081 1088 # date = re.findall(r'\((.*?)\)', date)
1082 1089 # date = date[0].split(',')
1083 1090 # date = map(int, date)
1084 1091 #
1085 1092 # if len(date)<6:
1086 1093 # date.append(0)
1087 1094 #
1088 1095 # date = [date[0]*10000 + date[1]*100 + date[2], date[3]*10000 + date[4]*100 + date[5]]
1089 1096 # arrayDate = numpy.tile(date, (len(listMeteors), 1))
1090 1097 arrayDate = numpy.tile(date, (len(listMeteors)))
1091 1098
1092 1099 #Meteor array
1093 1100 # arrayMeteors[:,0] = heiRang[arrayMeteors[:,0].astype(int)]
1094 1101 # arrayMeteors = numpy.hstack((arrayDate, arrayMeteors))
1095 1102
1096 1103 #Parameters Array
1097 1104 arrayParameters[:,0] = arrayDate #Date
1098 1105 arrayParameters[:,1] = heiRang[arrayMeteors[:,0].astype(int)] #Range
1099 1106 arrayParameters[:,6:8] = arrayMeteors[:,-3:-1] #Radial velocity and its error
1100 1107 arrayParameters[:,8:12] = arrayMeteors[:,7:11] #Phases
1101 1108 arrayParameters[:,-1] = arrayMeteors[:,-1] #Error
1102 1109
1103 1110
1104 1111 return arrayParameters
1105 1112
1106 1113 # def __getAOA(self, phases, pairsList, error, AOAthresh, azimuth):
1107 1114 #
1108 1115 # arrayAOA = numpy.zeros((phases.shape[0],3))
1109 1116 # cosdir0, cosdir = self.__getDirectionCosines(phases, pairsList)
1110 1117 #
1111 1118 # arrayAOA[:,:2] = self.__calculateAOA(cosdir, azimuth)
1112 1119 # cosDirError = numpy.sum(numpy.abs(cosdir0 - cosdir), axis = 1)
1113 1120 # arrayAOA[:,2] = cosDirError
1114 1121 #
1115 1122 # azimuthAngle = arrayAOA[:,0]
1116 1123 # zenithAngle = arrayAOA[:,1]
1117 1124 #
1118 1125 # #Setting Error
1119 1126 # #Number 3: AOA not fesible
1120 1127 # indInvalid = numpy.where(numpy.logical_and((numpy.logical_or(numpy.isnan(zenithAngle), numpy.isnan(azimuthAngle))),error == 0))[0]
1121 1128 # error[indInvalid] = 3
1122 1129 # #Number 4: Large difference in AOAs obtained from different antenna baselines
1123 1130 # indInvalid = numpy.where(numpy.logical_and(cosDirError > AOAthresh,error == 0))[0]
1124 1131 # error[indInvalid] = 4
1125 1132 # return arrayAOA, error
1126 1133 #
1127 1134 # def __getDirectionCosines(self, arrayPhase, pairsList):
1128 1135 #
1129 1136 # #Initializing some variables
1130 1137 # ang_aux = numpy.array([-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8])*2*numpy.pi
1131 1138 # ang_aux = ang_aux.reshape(1,ang_aux.size)
1132 1139 #
1133 1140 # cosdir = numpy.zeros((arrayPhase.shape[0],2))
1134 1141 # cosdir0 = numpy.zeros((arrayPhase.shape[0],2))
1135 1142 #
1136 1143 #
1137 1144 # for i in range(2):
1138 1145 # #First Estimation
1139 1146 # phi0_aux = arrayPhase[:,pairsList[i][0]] + arrayPhase[:,pairsList[i][1]]
1140 1147 # #Dealias
1141 1148 # indcsi = numpy.where(phi0_aux > numpy.pi)
1142 1149 # phi0_aux[indcsi] -= 2*numpy.pi
1143 1150 # indcsi = numpy.where(phi0_aux < -numpy.pi)
1144 1151 # phi0_aux[indcsi] += 2*numpy.pi
1145 1152 # #Direction Cosine 0
1146 1153 # cosdir0[:,i] = -(phi0_aux)/(2*numpy.pi*0.5)
1147 1154 #
1148 1155 # #Most-Accurate Second Estimation
1149 1156 # phi1_aux = arrayPhase[:,pairsList[i][0]] - arrayPhase[:,pairsList[i][1]]
1150 1157 # phi1_aux = phi1_aux.reshape(phi1_aux.size,1)
1151 1158 # #Direction Cosine 1
1152 1159 # cosdir1 = -(phi1_aux + ang_aux)/(2*numpy.pi*4.5)
1153 1160 #
1154 1161 # #Searching the correct Direction Cosine
1155 1162 # cosdir0_aux = cosdir0[:,i]
1156 1163 # cosdir0_aux = cosdir0_aux.reshape(cosdir0_aux.size,1)
1157 1164 # #Minimum Distance
1158 1165 # cosDiff = (cosdir1 - cosdir0_aux)**2
1159 1166 # indcos = cosDiff.argmin(axis = 1)
1160 1167 # #Saving Value obtained
1161 1168 # cosdir[:,i] = cosdir1[numpy.arange(len(indcos)),indcos]
1162 1169 #
1163 1170 # return cosdir0, cosdir
1164 1171 #
1165 1172 # def __calculateAOA(self, cosdir, azimuth):
1166 1173 # cosdirX = cosdir[:,0]
1167 1174 # cosdirY = cosdir[:,1]
1168 1175 #
1169 1176 # zenithAngle = numpy.arccos(numpy.sqrt(1 - cosdirX**2 - cosdirY**2))*180/numpy.pi
1170 1177 # azimuthAngle = numpy.arctan2(cosdirX,cosdirY)*180/numpy.pi + azimuth #0 deg north, 90 deg east
1171 1178 # angles = numpy.vstack((azimuthAngle, zenithAngle)).transpose()
1172 1179 #
1173 1180 # return angles
1174 1181 #
1175 1182 # def __getHeights(self, Ranges, zenith, error, minHeight, maxHeight):
1176 1183 #
1177 1184 # Ramb = 375 #Ramb = c/(2*PRF)
1178 1185 # Re = 6371 #Earth Radius
1179 1186 # heights = numpy.zeros(Ranges.shape)
1180 1187 #
1181 1188 # R_aux = numpy.array([0,1,2])*Ramb
1182 1189 # R_aux = R_aux.reshape(1,R_aux.size)
1183 1190 #
1184 1191 # Ranges = Ranges.reshape(Ranges.size,1)
1185 1192 #
1186 1193 # Ri = Ranges + R_aux
1187 1194 # hi = numpy.sqrt(Re**2 + Ri**2 + (2*Re*numpy.cos(zenith*numpy.pi/180)*Ri.transpose()).transpose()) - Re
1188 1195 #
1189 1196 # #Check if there is a height between 70 and 110 km
1190 1197 # h_bool = numpy.sum(numpy.logical_and(hi > minHeight, hi < maxHeight), axis = 1)
1191 1198 # ind_h = numpy.where(h_bool == 1)[0]
1192 1199 #
1193 1200 # hCorr = hi[ind_h, :]
1194 1201 # ind_hCorr = numpy.where(numpy.logical_and(hi > minHeight, hi < maxHeight))
1195 1202 #
1196 1203 # hCorr = hi[ind_hCorr]
1197 1204 # heights[ind_h] = hCorr
1198 1205 #
1199 1206 # #Setting Error
1200 1207 # #Number 13: Height unresolvable echo: not valid height within 70 to 110 km
1201 1208 # #Number 14: Height ambiguous echo: more than one possible height within 70 to 110 km
1202 1209 #
1203 1210 # indInvalid2 = numpy.where(numpy.logical_and(h_bool > 1, error == 0))[0]
1204 1211 # error[indInvalid2] = 14
1205 1212 # indInvalid1 = numpy.where(numpy.logical_and(h_bool == 0, error == 0))[0]
1206 1213 # error[indInvalid1] = 13
1207 1214 #
1208 1215 # return heights, error
1209 1216
1210 1217 def SpectralFitting(self, getSNR = True, path=None, file=None, groupList=None):
1211 1218
1212 1219 '''
1213 1220 Function GetMoments()
1214 1221
1215 1222 Input:
1216 1223 Output:
1217 1224 Variables modified:
1218 1225 '''
1219 1226 if path != None:
1220 1227 sys.path.append(path)
1221 1228 self.dataOut.library = importlib.import_module(file)
1222 1229
1223 1230 #To be inserted as a parameter
1224 1231 groupArray = numpy.array(groupList)
1225 1232 # groupArray = numpy.array([[0,1],[2,3]])
1226 1233 self.dataOut.groupList = groupArray
1227 1234
1228 1235 nGroups = groupArray.shape[0]
1229 1236 nChannels = self.dataIn.nChannels
1230 1237 nHeights=self.dataIn.heightList.size
1231 1238
1232 1239 #Parameters Array
1233 1240 self.dataOut.data_param = None
1234 1241
1235 1242 #Set constants
1236 1243 constants = self.dataOut.library.setConstants(self.dataIn)
1237 1244 self.dataOut.constants = constants
1238 1245 M = self.dataIn.normFactor
1239 1246 N = self.dataIn.nFFTPoints
1240 1247 ippSeconds = self.dataIn.ippSeconds
1241 1248 K = self.dataIn.nIncohInt
1242 1249 pairsArray = numpy.array(self.dataIn.pairsList)
1243 1250
1244 1251 #List of possible combinations
1245 1252 listComb = itertools.combinations(numpy.arange(groupArray.shape[1]),2)
1246 1253 indCross = numpy.zeros(len(list(listComb)), dtype = 'int')
1247 1254
1248 1255 if getSNR:
1249 1256 listChannels = groupArray.reshape((groupArray.size))
1250 1257 listChannels.sort()
1251 1258 noise = self.dataIn.getNoise()
1252 1259 self.dataOut.data_SNR = self.__getSNR(self.dataIn.data_spc[listChannels,:,:], noise[listChannels])
1253 1260
1254 1261 for i in range(nGroups):
1255 1262 coord = groupArray[i,:]
1256 1263
1257 1264 #Input data array
1258 1265 data = self.dataIn.data_spc[coord,:,:]/(M*N)
1259 1266 data = data.reshape((data.shape[0]*data.shape[1],data.shape[2]))
1260 1267
1261 1268 #Cross Spectra data array for Covariance Matrixes
1262 1269 ind = 0
1263 1270 for pairs in listComb:
1264 1271 pairsSel = numpy.array([coord[x],coord[y]])
1265 1272 indCross[ind] = int(numpy.where(numpy.all(pairsArray == pairsSel, axis = 1))[0][0])
1266 1273 ind += 1
1267 1274 dataCross = self.dataIn.data_cspc[indCross,:,:]/(M*N)
1268 1275 dataCross = dataCross**2/K
1269 1276
1270 1277 for h in range(nHeights):
1271 1278 # print self.dataOut.heightList[h]
1272 1279
1273 1280 #Input
1274 1281 d = data[:,h]
1275 1282
1276 1283 #Covariance Matrix
1277 1284 D = numpy.diag(d**2/K)
1278 1285 ind = 0
1279 1286 for pairs in listComb:
1280 1287 #Coordinates in Covariance Matrix
1281 1288 x = pairs[0]
1282 1289 y = pairs[1]
1283 1290 #Channel Index
1284 1291 S12 = dataCross[ind,:,h]
1285 1292 D12 = numpy.diag(S12)
1286 1293 #Completing Covariance Matrix with Cross Spectras
1287 1294 D[x*N:(x+1)*N,y*N:(y+1)*N] = D12
1288 1295 D[y*N:(y+1)*N,x*N:(x+1)*N] = D12
1289 1296 ind += 1
1290 1297 Dinv=numpy.linalg.inv(D)
1291 1298 L=numpy.linalg.cholesky(Dinv)
1292 1299 LT=L.T
1293 1300
1294 1301 dp = numpy.dot(LT,d)
1295 1302
1296 1303 #Initial values
1297 1304 data_spc = self.dataIn.data_spc[coord,:,h]
1298 1305
1299 1306 if (h>0)and(error1[3]<5):
1300 1307 p0 = self.dataOut.data_param[i,:,h-1]
1301 1308 else:
1302 1309 p0 = numpy.array(self.dataOut.library.initialValuesFunction(data_spc, constants, i))
1303 1310
1304 1311 try:
1305 1312 #Least Squares
1306 1313 minp,covp,infodict,mesg,ier = optimize.leastsq(self.__residFunction,p0,args=(dp,LT,constants),full_output=True)
1307 1314 # minp,covp = optimize.leastsq(self.__residFunction,p0,args=(dp,LT,constants))
1308 1315 #Chi square error
1309 1316 error0 = numpy.sum(infodict['fvec']**2)/(2*N)
1310 1317 #Error with Jacobian
1311 1318 error1 = self.dataOut.library.errorFunction(minp,constants,LT)
1312 1319 except:
1313 1320 minp = p0*numpy.nan
1314 1321 error0 = numpy.nan
1315 1322 error1 = p0*numpy.nan
1316 1323
1317 1324 #Save
1318 1325 if self.dataOut.data_param == None:
1319 1326 self.dataOut.data_param = numpy.zeros((nGroups, p0.size, nHeights))*numpy.nan
1320 1327 self.dataOut.data_error = numpy.zeros((nGroups, p0.size + 1, nHeights))*numpy.nan
1321 1328
1322 1329 self.dataOut.data_error[i,:,h] = numpy.hstack((error0,error1))
1323 1330 self.dataOut.data_param[i,:,h] = minp
1324 1331 return
1325 1332
1326 1333 def __residFunction(self, p, dp, LT, constants):
1327 1334
1328 1335 fm = self.dataOut.library.modelFunction(p, constants)
1329 1336 fmp=numpy.dot(LT,fm)
1330 1337
1331 1338 return dp-fmp
1332 1339
1333 1340 def __getSNR(self, z, noise):
1334 1341
1335 1342 avg = numpy.average(z, axis=1)
1336 1343 SNR = (avg.T-noise)/noise
1337 1344 SNR = SNR.T
1338 1345 return SNR
1339 1346
1340 1347 def __chisq(p,chindex,hindex):
1341 1348 #similar to Resid but calculates CHI**2
1342 1349 [LT,d,fm]=setupLTdfm(p,chindex,hindex)
1343 1350 dp=numpy.dot(LT,d)
1344 1351 fmp=numpy.dot(LT,fm)
1345 1352 chisq=numpy.dot((dp-fmp).T,(dp-fmp))
1346 1353 return chisq
1347 1354
1348 1355
1349 1356 class WindProfiler(Operation):
1350 1357
1351 1358 __isConfig = False
1352 1359
1353 1360 __initime = None
1354 1361 __lastdatatime = None
1355 1362 __integrationtime = None
1356 1363
1357 1364 __buffer = None
1358 1365
1359 1366 __dataReady = False
1360 1367
1361 1368 __firstdata = None
1362 1369
1363 1370 n = None
1364 1371
1365 1372 def __init__(self):
1366 1373 Operation.__init__(self)
1367 1374
1368 1375 def __calculateCosDir(self, elev, azim):
1369 1376 zen = (90 - elev)*numpy.pi/180
1370 1377 azim = azim*numpy.pi/180
1371 1378 cosDirX = numpy.sqrt((1-numpy.cos(zen)**2)/((1+numpy.tan(azim)**2)))
1372 1379 cosDirY = numpy.sqrt(1-numpy.cos(zen)**2-cosDirX**2)
1373 1380
1374 1381 signX = numpy.sign(numpy.cos(azim))
1375 1382 signY = numpy.sign(numpy.sin(azim))
1376 1383
1377 1384 cosDirX = numpy.copysign(cosDirX, signX)
1378 1385 cosDirY = numpy.copysign(cosDirY, signY)
1379 1386 return cosDirX, cosDirY
1380 1387
1381 1388 def __calculateAngles(self, theta_x, theta_y, azimuth):
1382 1389
1383 1390 dir_cosw = numpy.sqrt(1-theta_x**2-theta_y**2)
1384 1391 zenith_arr = numpy.arccos(dir_cosw)
1385 1392 azimuth_arr = numpy.arctan2(theta_x,theta_y) + azimuth*math.pi/180
1386 1393
1387 1394 dir_cosu = numpy.sin(azimuth_arr)*numpy.sin(zenith_arr)
1388 1395 dir_cosv = numpy.cos(azimuth_arr)*numpy.sin(zenith_arr)
1389 1396
1390 1397 return azimuth_arr, zenith_arr, dir_cosu, dir_cosv, dir_cosw
1391 1398
1392 1399 def __calculateMatA(self, dir_cosu, dir_cosv, dir_cosw, horOnly):
1393 1400
1394 1401 #
1395 1402 if horOnly:
1396 1403 A = numpy.c_[dir_cosu,dir_cosv]
1397 1404 else:
1398 1405 A = numpy.c_[dir_cosu,dir_cosv,dir_cosw]
1399 1406 A = numpy.asmatrix(A)
1400 1407 A1 = numpy.linalg.inv(A.transpose()*A)*A.transpose()
1401 1408
1402 1409 return A1
1403 1410
1404 1411 def __correctValues(self, heiRang, phi, velRadial, SNR):
1405 1412 listPhi = phi.tolist()
1406 1413 maxid = listPhi.index(max(listPhi))
1407 1414 minid = listPhi.index(min(listPhi))
1408 1415
1409 1416 rango = range(len(phi))
1410 1417 # rango = numpy.delete(rango,maxid)
1411 1418
1412 1419 heiRang1 = heiRang*math.cos(phi[maxid])
1413 1420 heiRangAux = heiRang*math.cos(phi[minid])
1414 1421 indOut = (heiRang1 < heiRangAux[0]).nonzero()
1415 1422 heiRang1 = numpy.delete(heiRang1,indOut)
1416 1423
1417 1424 velRadial1 = numpy.zeros([len(phi),len(heiRang1)])
1418 1425 SNR1 = numpy.zeros([len(phi),len(heiRang1)])
1419 1426
1420 1427 for i in rango:
1421 1428 x = heiRang*math.cos(phi[i])
1422 1429 y1 = velRadial[i,:]
1423 1430 f1 = interpolate.interp1d(x,y1,kind = 'cubic')
1424 1431
1425 1432 x1 = heiRang1
1426 1433 y11 = f1(x1)
1427 1434
1428 1435 y2 = SNR[i,:]
1429 1436 f2 = interpolate.interp1d(x,y2,kind = 'cubic')
1430 1437 y21 = f2(x1)
1431 1438
1432 1439 velRadial1[i,:] = y11
1433 1440 SNR1[i,:] = y21
1434 1441
1435 1442 return heiRang1, velRadial1, SNR1
1436 1443
1437 1444 def __calculateVelUVW(self, A, velRadial):
1438 1445
1439 1446 #Operacion Matricial
1440 1447 # velUVW = numpy.zeros((velRadial.shape[1],3))
1441 1448 # for ind in range(velRadial.shape[1]):
1442 1449 # velUVW[ind,:] = numpy.dot(A,velRadial[:,ind])
1443 1450 # velUVW = velUVW.transpose()
1444 1451 velUVW = numpy.zeros((A.shape[0],velRadial.shape[1]))
1445 1452 velUVW[:,:] = numpy.dot(A,velRadial)
1446 1453
1447 1454
1448 1455 return velUVW
1449 1456
1450 1457 def techniqueDBS(self, velRadial0, dirCosx, disrCosy, azimuth, correct, horizontalOnly, heiRang, SNR0):
1451 1458 """
1452 1459 Function that implements Doppler Beam Swinging (DBS) technique.
1453 1460
1454 1461 Input: Radial velocities, Direction cosines (x and y) of the Beam, Antenna azimuth,
1455 1462 Direction correction (if necessary), Ranges and SNR
1456 1463
1457 1464 Output: Winds estimation (Zonal, Meridional and Vertical)
1458 1465
1459 1466 Parameters affected: Winds, height range, SNR
1460 1467 """
1461 1468 azimuth_arr, zenith_arr, dir_cosu, dir_cosv, dir_cosw = self.__calculateAngles(dirCosx, disrCosy, azimuth)
1462 1469 heiRang1, velRadial1, SNR1 = self.__correctValues(heiRang, zenith_arr, correct*velRadial0, SNR0)
1463 1470 A = self.__calculateMatA(dir_cosu, dir_cosv, dir_cosw, horizontalOnly)
1464 1471
1465 1472 #Calculo de Componentes de la velocidad con DBS
1466 1473 winds = self.__calculateVelUVW(A,velRadial1)
1467 1474
1468 1475 return winds, heiRang1, SNR1
1469 1476
1470 1477 def __calculateDistance(self, posx, posy, pairsCrossCorr, pairsList, pairs, azimuth = None):
1471 1478
1472 1479 posx = numpy.asarray(posx)
1473 1480 posy = numpy.asarray(posy)
1474 1481
1475 1482 #Rotacion Inversa para alinear con el azimuth
1476 1483 if azimuth!= None:
1477 1484 azimuth = azimuth*math.pi/180
1478 1485 posx1 = posx*math.cos(azimuth) + posy*math.sin(azimuth)
1479 1486 posy1 = -posx*math.sin(azimuth) + posy*math.cos(azimuth)
1480 1487 else:
1481 1488 posx1 = posx
1482 1489 posy1 = posy
1483 1490
1484 1491 #Calculo de Distancias
1485 1492 distx = numpy.zeros(pairsCrossCorr.size)
1486 1493 disty = numpy.zeros(pairsCrossCorr.size)
1487 1494 dist = numpy.zeros(pairsCrossCorr.size)
1488 1495 ang = numpy.zeros(pairsCrossCorr.size)
1489 1496
1490 1497 for i in range(pairsCrossCorr.size):
1491 1498 distx[i] = posx1[pairsList[pairsCrossCorr[i]][1]] - posx1[pairsList[pairsCrossCorr[i]][0]]
1492 1499 disty[i] = posy1[pairsList[pairsCrossCorr[i]][1]] - posy1[pairsList[pairsCrossCorr[i]][0]]
1493 1500 dist[i] = numpy.sqrt(distx[i]**2 + disty[i]**2)
1494 1501 ang[i] = numpy.arctan2(disty[i],distx[i])
1495 1502 #Calculo de Matrices
1496 1503 nPairs = len(pairs)
1497 1504 ang1 = numpy.zeros((nPairs, 2, 1))
1498 1505 dist1 = numpy.zeros((nPairs, 2, 1))
1499 1506
1500 1507 for j in range(nPairs):
1501 1508 dist1[j,0,0] = dist[pairs[j][0]]
1502 1509 dist1[j,1,0] = dist[pairs[j][1]]
1503 1510 ang1[j,0,0] = ang[pairs[j][0]]
1504 1511 ang1[j,1,0] = ang[pairs[j][1]]
1505 1512
1506 1513 return distx,disty, dist1,ang1
1507 1514
1508 1515 def __calculateVelVer(self, phase, lagTRange, _lambda):
1509 1516
1510 1517 Ts = lagTRange[1] - lagTRange[0]
1511 1518 velW = -_lambda*phase/(4*math.pi*Ts)
1512 1519
1513 1520 return velW
1514 1521
1515 1522 def __calculateVelHorDir(self, dist, tau1, tau2, ang):
1516 1523 nPairs = tau1.shape[0]
1517 1524 vel = numpy.zeros((nPairs,3,tau1.shape[2]))
1518 1525
1519 1526 angCos = numpy.cos(ang)
1520 1527 angSin = numpy.sin(ang)
1521 1528
1522 1529 vel0 = dist*tau1/(2*tau2**2)
1523 1530 vel[:,0,:] = (vel0*angCos).sum(axis = 1)
1524 1531 vel[:,1,:] = (vel0*angSin).sum(axis = 1)
1525 1532
1526 1533 ind = numpy.where(numpy.isinf(vel))
1527 1534 vel[ind] = numpy.nan
1528 1535
1529 1536 return vel
1530 1537
1531 1538 def __getPairsAutoCorr(self, pairsList, nChannels):
1532 1539
1533 1540 pairsAutoCorr = numpy.zeros(nChannels, dtype = 'int')*numpy.nan
1534 1541
1535 1542 for l in range(len(pairsList)):
1536 1543 firstChannel = pairsList[l][0]
1537 1544 secondChannel = pairsList[l][1]
1538 1545
1539 1546 #Obteniendo pares de Autocorrelacion
1540 1547 if firstChannel == secondChannel:
1541 1548 pairsAutoCorr[firstChannel] = int(l)
1542 1549
1543 1550 pairsAutoCorr = pairsAutoCorr.astype(int)
1544 1551
1545 1552 pairsCrossCorr = range(len(pairsList))
1546 1553 pairsCrossCorr = numpy.delete(pairsCrossCorr,pairsAutoCorr)
1547 1554
1548 1555 return pairsAutoCorr, pairsCrossCorr
1549 1556
1550 1557 def techniqueSA(self, pairsSelected, pairsList, nChannels, tau, azimuth, _lambda, position_x, position_y, lagTRange, correctFactor):
1551 1558 """
1552 1559 Function that implements Spaced Antenna (SA) technique.
1553 1560
1554 1561 Input: Radial velocities, Direction cosines (x and y) of the Beam, Antenna azimuth,
1555 1562 Direction correction (if necessary), Ranges and SNR
1556 1563
1557 1564 Output: Winds estimation (Zonal, Meridional and Vertical)
1558 1565
1559 1566 Parameters affected: Winds
1560 1567 """
1561 1568 #Cross Correlation pairs obtained
1562 1569 pairsAutoCorr, pairsCrossCorr = self.__getPairsAutoCorr(pairsList, nChannels)
1563 1570 pairsArray = numpy.array(pairsList)[pairsCrossCorr]
1564 1571 pairsSelArray = numpy.array(pairsSelected)
1565 1572 pairs = []
1566 1573
1567 1574 #Wind estimation pairs obtained
1568 1575 for i in range(pairsSelArray.shape[0]/2):
1569 1576 ind1 = numpy.where(numpy.all(pairsArray == pairsSelArray[2*i], axis = 1))[0][0]
1570 1577 ind2 = numpy.where(numpy.all(pairsArray == pairsSelArray[2*i + 1], axis = 1))[0][0]
1571 1578 pairs.append((ind1,ind2))
1572 1579
1573 1580 indtau = tau.shape[0]/2
1574 1581 tau1 = tau[:indtau,:]
1575 1582 tau2 = tau[indtau:-1,:]
1576 1583 tau1 = tau1[pairs,:]
1577 1584 tau2 = tau2[pairs,:]
1578 1585 phase1 = tau[-1,:]
1579 1586
1580 1587 #---------------------------------------------------------------------
1581 1588 #Metodo Directo
1582 1589 distx, disty, dist, ang = self.__calculateDistance(position_x, position_y, pairsCrossCorr, pairsList, pairs,azimuth)
1583 1590 winds = self.__calculateVelHorDir(dist, tau1, tau2, ang)
1584 1591 winds = stats.nanmean(winds, axis=0)
1585 1592 #---------------------------------------------------------------------
1586 1593 #Metodo General
1587 1594 # distx, disty, dist = self.calculateDistance(position_x,position_y,pairsCrossCorr, pairsList, azimuth)
1588 1595 # #Calculo Coeficientes de Funcion de Correlacion
1589 1596 # F,G,A,B,H = self.calculateCoef(tau1,tau2,distx,disty,n)
1590 1597 # #Calculo de Velocidades
1591 1598 # winds = self.calculateVelUV(F,G,A,B,H)
1592 1599
1593 1600 #---------------------------------------------------------------------
1594 1601 winds[2,:] = self.__calculateVelVer(phase1, lagTRange, _lambda)
1595 1602 winds = correctFactor*winds
1596 1603 return winds
1597 1604
1598 1605 def __checkTime(self, currentTime, paramInterval, outputInterval):
1599 1606
1600 1607 dataTime = currentTime + paramInterval
1601 1608 deltaTime = dataTime - self.__initime
1602 1609
1603 1610 if deltaTime >= outputInterval or deltaTime < 0:
1604 1611 self.__dataReady = True
1605 1612 return
1606 1613
1607 1614 def techniqueMeteors(self, arrayMeteor, meteorThresh, heightMin, heightMax):
1608 1615 '''
1609 1616 Function that implements winds estimation technique with detected meteors.
1610 1617
1611 1618 Input: Detected meteors, Minimum meteor quantity to wind estimation
1612 1619
1613 1620 Output: Winds estimation (Zonal and Meridional)
1614 1621
1615 1622 Parameters affected: Winds
1616 1623 '''
1617 1624 # print arrayMeteor.shape
1618 1625 #Settings
1619 1626 nInt = (heightMax - heightMin)/2
1620 1627 # print nInt
1621 1628 nInt = int(nInt)
1622 1629 # print nInt
1623 1630 winds = numpy.zeros((2,nInt))*numpy.nan
1624 1631
1625 1632 #Filter errors
1626 1633 error = numpy.where(arrayMeteor[:,-1] == 0)[0]
1627 1634 finalMeteor = arrayMeteor[error,:]
1628 1635
1629 1636 #Meteor Histogram
1630 1637 finalHeights = finalMeteor[:,2]
1631 1638 hist = numpy.histogram(finalHeights, bins = nInt, range = (heightMin,heightMax))
1632 1639 nMeteorsPerI = hist[0]
1633 1640 heightPerI = hist[1]
1634 1641
1635 1642 #Sort of meteors
1636 1643 indSort = finalHeights.argsort()
1637 1644 finalMeteor2 = finalMeteor[indSort,:]
1638 1645
1639 1646 # Calculating winds
1640 1647 ind1 = 0
1641 1648 ind2 = 0
1642 1649
1643 1650 for i in range(nInt):
1644 1651 nMet = nMeteorsPerI[i]
1645 1652 ind1 = ind2
1646 1653 ind2 = ind1 + nMet
1647 1654
1648 1655 meteorAux = finalMeteor2[ind1:ind2,:]
1649 1656
1650 1657 if meteorAux.shape[0] >= meteorThresh:
1651 1658 vel = meteorAux[:, 6]
1652 1659 zen = meteorAux[:, 4]*numpy.pi/180
1653 1660 azim = meteorAux[:, 3]*numpy.pi/180
1654 1661
1655 1662 n = numpy.cos(zen)
1656 1663 # m = (1 - n**2)/(1 - numpy.tan(azim)**2)
1657 1664 # l = m*numpy.tan(azim)
1658 1665 l = numpy.sin(zen)*numpy.sin(azim)
1659 1666 m = numpy.sin(zen)*numpy.cos(azim)
1660 1667
1661 1668 A = numpy.vstack((l, m)).transpose()
1662 1669 A1 = numpy.dot(numpy.linalg.inv( numpy.dot(A.transpose(),A) ),A.transpose())
1663 1670 windsAux = numpy.dot(A1, vel)
1664 1671
1665 1672 winds[0,i] = windsAux[0]
1666 1673 winds[1,i] = windsAux[1]
1667 1674
1668 1675 return winds, heightPerI[:-1]
1669 1676
1670 1677 def run(self, dataOut, technique, **kwargs):
1671 1678
1672 1679 param = dataOut.data_param
1673 1680 if dataOut.abscissaList != None:
1674 1681 absc = dataOut.abscissaList[:-1]
1675 1682 noise = dataOut.noise
1676 1683 heightList = dataOut.heightList
1677 1684 SNR = dataOut.data_SNR
1678 1685
1679 1686 if technique == 'DBS':
1680 1687
1681 1688 if kwargs.has_key('dirCosx') and kwargs.has_key('dirCosy'):
1682 1689 theta_x = numpy.array(kwargs['dirCosx'])
1683 1690 theta_y = numpy.array(kwargs['dirCosy'])
1684 1691 else:
1685 1692 elev = numpy.array(kwargs['elevation'])
1686 1693 azim = numpy.array(kwargs['azimuth'])
1687 1694 theta_x, theta_y = self.__calculateCosDir(elev, azim)
1688 1695 azimuth = kwargs['correctAzimuth']
1689 1696 if kwargs.has_key('horizontalOnly'):
1690 1697 horizontalOnly = kwargs['horizontalOnly']
1691 1698 else: horizontalOnly = False
1692 1699 if kwargs.has_key('correctFactor'):
1693 1700 correctFactor = kwargs['correctFactor']
1694 1701 else: correctFactor = 1
1695 1702 if kwargs.has_key('channelList'):
1696 1703 channelList = kwargs['channelList']
1697 1704 if len(channelList) == 2:
1698 1705 horizontalOnly = True
1699 1706 arrayChannel = numpy.array(channelList)
1700 1707 param = param[arrayChannel,:,:]
1701 1708 theta_x = theta_x[arrayChannel]
1702 1709 theta_y = theta_y[arrayChannel]
1703 1710
1704 1711 velRadial0 = param[:,1,:] #Radial velocity
1705 1712 dataOut.data_output, dataOut.heightList, dataOut.data_SNR = self.techniqueDBS(velRadial0, theta_x, theta_y, azimuth, correctFactor, horizontalOnly, heightList, SNR) #DBS Function
1706 1713 dataOut.utctimeInit = dataOut.utctime
1707 1714 dataOut.outputInterval = dataOut.timeInterval
1708 1715
1709 1716 elif technique == 'SA':
1710 1717
1711 1718 #Parameters
1712 1719 position_x = kwargs['positionX']
1713 1720 position_y = kwargs['positionY']
1714 1721 azimuth = kwargs['azimuth']
1715 1722
1716 1723 if kwargs.has_key('crosspairsList'):
1717 1724 pairs = kwargs['crosspairsList']
1718 1725 else:
1719 1726 pairs = None
1720 1727
1721 1728 if kwargs.has_key('correctFactor'):
1722 1729 correctFactor = kwargs['correctFactor']
1723 1730 else:
1724 1731 correctFactor = 1
1725 1732
1726 1733 tau = dataOut.data_param
1727 1734 _lambda = dataOut.C/dataOut.frequency
1728 1735 pairsList = dataOut.groupList
1729 1736 nChannels = dataOut.nChannels
1730 1737
1731 1738 dataOut.data_output = self.techniqueSA(pairs, pairsList, nChannels, tau, azimuth, _lambda, position_x, position_y, absc, correctFactor)
1732 1739 dataOut.utctimeInit = dataOut.utctime
1733 1740 dataOut.outputInterval = dataOut.timeInterval
1734 1741
1735 1742 elif technique == 'Meteors':
1736 1743 dataOut.flagNoData = True
1737 1744 self.__dataReady = False
1738 1745
1739 1746 if kwargs.has_key('nHours'):
1740 1747 nHours = kwargs['nHours']
1741 1748 else:
1742 1749 nHours = 1
1743 1750
1744 1751 if kwargs.has_key('meteorsPerBin'):
1745 1752 meteorThresh = kwargs['meteorsPerBin']
1746 1753 else:
1747 1754 meteorThresh = 6
1748 1755
1749 1756 if kwargs.has_key('hmin'):
1750 1757 hmin = kwargs['hmin']
1751 1758 else: hmin = 70
1752 1759 if kwargs.has_key('hmax'):
1753 1760 hmax = kwargs['hmax']
1754 1761 else: hmax = 110
1755 1762
1756 1763 dataOut.outputInterval = nHours*3600
1757 1764
1758 1765 if self.__isConfig == False:
1759 1766 # self.__initime = dataOut.datatime.replace(minute = 0, second = 0, microsecond = 03)
1760 1767 #Get Initial LTC time
1761 1768 self.__initime = datetime.datetime.utcfromtimestamp(dataOut.utctime)
1762 1769 self.__initime = (self.__initime.replace(minute = 0, second = 0, microsecond = 0) - datetime.datetime(1970, 1, 1)).total_seconds()
1763 1770
1764 1771 self.__isConfig = True
1765 1772
1766 1773 if self.__buffer == None:
1767 1774 self.__buffer = dataOut.data_param
1768 1775 self.__firstdata = copy.copy(dataOut)
1769 1776
1770 1777 else:
1771 1778 self.__buffer = numpy.vstack((self.__buffer, dataOut.data_param))
1772 1779
1773 1780 self.__checkTime(dataOut.utctime, dataOut.paramInterval, dataOut.outputInterval) #Check if the buffer is ready
1774 1781
1775 1782 if self.__dataReady:
1776 1783 dataOut.utctimeInit = self.__initime
1777 1784
1778 1785 self.__initime += dataOut.outputInterval #to erase time offset
1779 1786
1780 1787 dataOut.data_output, dataOut.heightList = self.techniqueMeteors(self.__buffer, meteorThresh, hmin, hmax)
1781 1788 dataOut.flagNoData = False
1782 1789 self.__buffer = None
1783 1790
1784 1791 return
1785 1792
1786 1793 class EWDriftsEstimation(Operation):
1787 1794
1788 1795
1789 1796 def __init__(self):
1790 1797 Operation.__init__(self)
1791 1798
1792 1799 def __correctValues(self, heiRang, phi, velRadial, SNR):
1793 1800 listPhi = phi.tolist()
1794 1801 maxid = listPhi.index(max(listPhi))
1795 1802 minid = listPhi.index(min(listPhi))
1796 1803
1797 1804 rango = range(len(phi))
1798 1805 # rango = numpy.delete(rango,maxid)
1799 1806
1800 1807 heiRang1 = heiRang*math.cos(phi[maxid])
1801 1808 heiRangAux = heiRang*math.cos(phi[minid])
1802 1809 indOut = (heiRang1 < heiRangAux[0]).nonzero()
1803 1810 heiRang1 = numpy.delete(heiRang1,indOut)
1804 1811
1805 1812 velRadial1 = numpy.zeros([len(phi),len(heiRang1)])
1806 1813 SNR1 = numpy.zeros([len(phi),len(heiRang1)])
1807 1814
1808 1815 for i in rango:
1809 1816 x = heiRang*math.cos(phi[i])
1810 1817 y1 = velRadial[i,:]
1811 1818 f1 = interpolate.interp1d(x,y1,kind = 'cubic')
1812 1819
1813 1820 x1 = heiRang1
1814 1821 y11 = f1(x1)
1815 1822
1816 1823 y2 = SNR[i,:]
1817 1824 f2 = interpolate.interp1d(x,y2,kind = 'cubic')
1818 1825 y21 = f2(x1)
1819 1826
1820 1827 velRadial1[i,:] = y11
1821 1828 SNR1[i,:] = y21
1822 1829
1823 1830 return heiRang1, velRadial1, SNR1
1824 1831
1825 1832 def run(self, dataOut, zenith, zenithCorrection):
1826 1833 heiRang = dataOut.heightList
1827 1834 velRadial = dataOut.data_param[:,3,:]
1828 1835 SNR = dataOut.data_SNR
1829 1836
1830 1837 zenith = numpy.array(zenith)
1831 1838 zenith -= zenithCorrection
1832 1839 zenith *= numpy.pi/180
1833 1840
1834 1841 heiRang1, velRadial1, SNR1 = self.__correctValues(heiRang, numpy.abs(zenith), velRadial, SNR)
1835 1842
1836 1843 alp = zenith[0]
1837 1844 bet = zenith[1]
1838 1845
1839 1846 w_w = velRadial1[0,:]
1840 1847 w_e = velRadial1[1,:]
1841 1848
1842 1849 w = (w_w*numpy.sin(bet) - w_e*numpy.sin(alp))/(numpy.cos(alp)*numpy.sin(bet) - numpy.cos(bet)*numpy.sin(alp))
1843 1850 u = (w_w*numpy.cos(bet) - w_e*numpy.cos(alp))/(numpy.sin(alp)*numpy.cos(bet) - numpy.sin(bet)*numpy.cos(alp))
1844 1851
1845 1852 winds = numpy.vstack((u,w))
1846 1853
1847 1854 dataOut.heightList = heiRang1
1848 1855 dataOut.data_output = winds
1849 1856 dataOut.data_SNR = SNR1
1850 1857
1851 1858 dataOut.utctimeInit = dataOut.utctime
1852 1859 dataOut.outputInterval = dataOut.timeInterval
1853 1860 return
1854 1861
1855 1862 class PhaseCalibration(Operation):
1856 1863
1857 1864 __buffer = None
1858 1865
1859 1866 __initime = None
1860 1867
1861 1868 __dataReady = False
1862 1869
1863 1870 __isConfig = False
1864 1871
1865 1872 def __checkTime(self, currentTime, initTime, paramInterval, outputInterval):
1866 1873
1867 1874 dataTime = currentTime + paramInterval
1868 1875 deltaTime = dataTime - initTime
1869 1876
1870 1877 if deltaTime >= outputInterval or deltaTime < 0:
1871 1878 return True
1872 1879
1873 1880 return False
1874 1881
1875 1882 def __getGammas(self, pairs, k, d, phases):
1876 1883 gammas = numpy.zeros(2)
1877 1884
1878 1885 for i in range(len(pairs)):
1879 1886
1880 1887 pairi = pairs[i]
1881 1888
1882 1889 #Calculating gamma
1883 1890 jdcos = phases[:,pairi[1]]/(k*d[pairi[1]])
1884 1891 jgamma = numpy.angle(numpy.exp(1j*(k*d[pairi[0]]*jdcos - phases[:,pairi[0]])))
1885 1892
1886 1893 #Revised distribution
1887 1894 jgammaArray = numpy.hstack((jgamma,jgamma+0.5*numpy.pi,jgamma-0.5*numpy.pi))
1888 1895
1889 1896 #Histogram
1890 1897 nBins = 64.0
1891 1898 rmin = -0.5*numpy.pi
1892 1899 rmax = 0.5*numpy.pi
1893 1900 phaseHisto = numpy.histogram(jgammaArray, bins=nBins, range=(rmin,rmax))
1894 1901
1895 1902 meteorsY = phaseHisto[0]
1896 1903 phasesX = phaseHisto[1][:-1]
1897 1904 width = phasesX[1] - phasesX[0]
1898 1905 phasesX += width/2
1899 1906
1900 1907 #Gaussian aproximation
1901 1908 bpeak = meteorsY.argmax()
1902 1909 peak = meteorsY.max()
1903 1910 jmin = bpeak - 5
1904 1911 jmax = bpeak + 5 + 1
1905 1912
1906 1913 if jmin<0:
1907 1914 jmin = 0
1908 1915 jmax = 6
1909 1916 elif jmax > meteorsY.size:
1910 1917 jmin = meteorsY.size - 6
1911 1918 jmax = meteorsY.size
1912 1919
1913 1920 x0 = numpy.array([peak,bpeak,50])
1914 1921 coeff = optimize.leastsq(self.__residualFunction, x0, args=(meteorsY[jmin:jmax], phasesX[jmin:jmax]))
1915 1922
1916 1923 #Gammas
1917 1924 gammas[i] = coeff[0][1]
1918 1925
1919 1926 return gammas
1920 1927
1921 1928 def __residualFunction(self, coeffs, y, t):
1922 1929
1923 1930 return y - self.__gauss_function(t, coeffs)
1924 1931
1925 1932 def __gauss_function(self, t, coeffs):
1926 1933
1927 1934 return coeffs[0]*numpy.exp(-0.5*((t - coeffs[1]) / coeffs[2])**2)
1928 1935
1929 1936 def __getPhases(self, azimuth, h, pairsList, d, gammas, meteorsArray):
1930 1937 meteorOps = MeteorOperations()
1931 1938 nchan = 4
1932 1939 pairx = pairsList[0]
1933 1940 pairy = pairsList[1]
1934 1941 center_xangle = 0
1935 1942 center_yangle = 0
1936 1943 range_angle = numpy.array([10*numpy.pi,numpy.pi,numpy.pi/2,numpy.pi/4])
1937 1944 ntimes = len(range_angle)
1938 1945
1939 1946 nstepsx = 20.0
1940 1947 nstepsy = 20.0
1941 1948
1942 1949 for iz in range(ntimes):
1943 1950 min_xangle = -range_angle[iz]/2 + center_xangle
1944 1951 max_xangle = range_angle[iz]/2 + center_xangle
1945 1952 min_yangle = -range_angle[iz]/2 + center_yangle
1946 1953 max_yangle = range_angle[iz]/2 + center_yangle
1947 1954
1948 1955 inc_x = (max_xangle-min_xangle)/nstepsx
1949 1956 inc_y = (max_yangle-min_yangle)/nstepsy
1950 1957
1951 1958 alpha_y = numpy.arange(nstepsy)*inc_y + min_yangle
1952 1959 alpha_x = numpy.arange(nstepsx)*inc_x + min_xangle
1953 1960 penalty = numpy.zeros((nstepsx,nstepsy))
1954 1961 jph_array = numpy.zeros((nchan,nstepsx,nstepsy))
1955 1962 jph = numpy.zeros(nchan)
1956 1963
1957 1964 # Iterations looking for the offset
1958 1965 for iy in range(int(nstepsy)):
1959 1966 for ix in range(int(nstepsx)):
1960 1967 jph[pairy[1]] = alpha_y[iy]
1961 1968 jph[pairy[0]] = -gammas[1] + alpha_y[iy]*d[pairy[0]]/d[pairy[1]]
1962 1969
1963 1970 jph[pairx[1]] = alpha_x[ix]
1964 1971 jph[pairx[0]] = -gammas[0] + alpha_x[ix]*d[pairx[0]]/d[pairx[1]]
1965 1972
1966 1973 jph_array[:,ix,iy] = jph
1967 1974
1968 1975 meteorsArray1 = meteorOps.getMeteorParams(meteorsArray, azimuth, h, pairsList, jph)
1969 1976 error = meteorsArray1[:,-1]
1970 1977 ind1 = numpy.where(error==0)[0]
1971 1978 penalty[ix,iy] = ind1.size
1972 1979
1973 1980 i,j = numpy.unravel_index(penalty.argmax(), penalty.shape)
1974 1981 phOffset = jph_array[:,i,j]
1975 1982
1976 1983 center_xangle = phOffset[pairx[1]]
1977 1984 center_yangle = phOffset[pairy[1]]
1978 1985
1979 1986 phOffset = numpy.angle(numpy.exp(1j*jph_array[:,i,j]))
1980 1987 phOffset = phOffset*180/numpy.pi
1981 1988 return phOffset
1982 1989
1983 1990
1984 def run(self, dataOut, pairs, distances, hmin, hmax, nHours = None):
1991 def run(self, dataOut, hmin, hmax, direction_25X=-1, direction_20X=1, direction_25Y=-1, direction_20Y=1, nHours = 1):
1985 1992
1986 1993 dataOut.flagNoData = True
1987 self.__dataReady = False
1988
1989 if nHours == None:
1990 nHours = 1
1991
1994 self.__dataReady = False
1992 1995 dataOut.outputInterval = nHours*3600
1993 1996
1994 1997 if self.__isConfig == False:
1995 1998 # self.__initime = dataOut.datatime.replace(minute = 0, second = 0, microsecond = 03)
1996 1999 #Get Initial LTC time
1997 2000 self.__initime = datetime.datetime.utcfromtimestamp(dataOut.utctime)
1998 2001 self.__initime = (self.__initime.replace(minute = 0, second = 0, microsecond = 0) - datetime.datetime(1970, 1, 1)).total_seconds()
1999 2002
2000 2003 self.__isConfig = True
2001 2004
2002 2005 if self.__buffer == None:
2003 2006 self.__buffer = dataOut.data_param.copy()
2004 2007
2005 2008 else:
2006 self.__buffer = numpy.hstack((self.__buffer, dataOut.data_param))
2009 self.__buffer = numpy.vstack((self.__buffer, dataOut.data_param))
2007 2010
2008 2011 self.__dataReady = self.__checkTime(dataOut.utctime, self.__initime, dataOut.paramInterval, dataOut.outputInterval) #Check if the buffer is ready
2009 2012
2010 2013 if self.__dataReady:
2011 2014 dataOut.utctimeInit = self.__initime
2012 2015 self.__initime += dataOut.outputInterval #to erase time offset
2013 2016
2014 2017 freq = dataOut.frequency
2015 2018 c = dataOut.C #m/s
2016 2019 lamb = c/freq
2017 2020 k = 2*numpy.pi/lamb
2018 2021 azimuth = 0
2019 2022 h = (hmin, hmax)
2020 pairsList = ((0,3),(1,2))
2023 pairs = ((0,1),(2,3))
2024 distances = [direction_25X*2.5*lamb, direction_20X*2*lamb, direction_25Y*2.5*lamb, direction_20Y*2*lamb]
2021 2025
2022 2026 meteorsArray = self.__buffer
2023 2027 error = meteorsArray[:,-1]
2024 2028 boolError = (error==0)|(error==3)|(error==4)|(error==13)|(error==14)
2025 2029 ind1 = numpy.where(boolError)[0]
2026 2030 meteorsArray = meteorsArray[ind1,:]
2027 2031 meteorsArray[:,-1] = 0
2028 2032 phases = meteorsArray[:,8:12]
2029 2033
2030 2034 #Calculate Gammas
2031 2035 gammas = self.__getGammas(pairs, k, distances, phases)
2032 2036 # gammas = numpy.array([-21.70409463,45.76935864])*numpy.pi/180
2033 2037 #Calculate Phases
2034 phasesOff = self.__getPhases(azimuth, h, pairsList, distances, gammas, meteorsArray)
2038 phasesOff = self.__getPhases(azimuth, h, pairs, distances, gammas, meteorsArray)
2035 2039 phasesOff = phasesOff.reshape((1,phasesOff.size))
2036 2040 dataOut.data_output = -phasesOff
2037 2041 dataOut.flagNoData = False
2038 2042 self.__buffer = None
2039 2043
2040 2044
2041 2045 return
2042 2046
2043 2047 class MeteorOperations():
2044 2048
2045 2049 def __init__(self):
2046 2050
2047 2051 return
2048 2052
2049 2053 def getMeteorParams(self, arrayParameters0, azimuth, h, pairsList, jph):
2050 2054
2051 2055 arrayParameters = arrayParameters0.copy()
2052 2056 hmin = h[0]
2053 2057 hmax = h[1]
2054 2058
2055 2059 #Calculate AOA (Error N 3, 4)
2056 2060 #JONES ET AL. 1998
2057 2061 AOAthresh = numpy.pi/8
2058 2062 error = arrayParameters[:,-1]
2059 2063 phases = -arrayParameters[:,8:12] + jph
2060 2064 # phases = numpy.unwrap(phases)
2061 2065 arrayParameters[:,3:6], arrayParameters[:,-1] = self.__getAOA(phases, pairsList, error, AOAthresh, azimuth)
2062 2066
2063 2067 #Calculate Heights (Error N 13 and 14)
2064 2068 error = arrayParameters[:,-1]
2065 2069 Ranges = arrayParameters[:,1]
2066 2070 zenith = arrayParameters[:,4]
2067 2071 arrayParameters[:,2], arrayParameters[:,-1] = self.__getHeights(Ranges, zenith, error, hmin, hmax)
2068 2072
2069 2073 #----------------------- Get Final data ------------------------------------
2070 2074 # error = arrayParameters[:,-1]
2071 2075 # ind1 = numpy.where(error==0)[0]
2072 2076 # arrayParameters = arrayParameters[ind1,:]
2073 2077
2074 2078 return arrayParameters
2075 2079
2076 2080 def __getAOA(self, phases, pairsList, error, AOAthresh, azimuth):
2077 2081
2078 2082 arrayAOA = numpy.zeros((phases.shape[0],3))
2079 2083 cosdir0, cosdir = self.__getDirectionCosines(phases, pairsList)
2080 2084
2081 2085 arrayAOA[:,:2] = self.__calculateAOA(cosdir, azimuth)
2082 2086 cosDirError = numpy.sum(numpy.abs(cosdir0 - cosdir), axis = 1)
2083 2087 arrayAOA[:,2] = cosDirError
2084 2088
2085 2089 azimuthAngle = arrayAOA[:,0]
2086 2090 zenithAngle = arrayAOA[:,1]
2087 2091
2088 2092 #Setting Error
2089 2093 indError = numpy.where(numpy.logical_or(error == 3, error == 4))[0]
2090 2094 error[indError] = 0
2091 2095 #Number 3: AOA not fesible
2092 2096 indInvalid = numpy.where(numpy.logical_and((numpy.logical_or(numpy.isnan(zenithAngle), numpy.isnan(azimuthAngle))),error == 0))[0]
2093 2097 error[indInvalid] = 3
2094 2098 #Number 4: Large difference in AOAs obtained from different antenna baselines
2095 2099 indInvalid = numpy.where(numpy.logical_and(cosDirError > AOAthresh,error == 0))[0]
2096 2100 error[indInvalid] = 4
2097 2101 return arrayAOA, error
2098 2102
2099 2103 def __getDirectionCosines(self, arrayPhase, pairsList):
2100 2104
2101 2105 #Initializing some variables
2102 2106 ang_aux = numpy.array([-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8])*2*numpy.pi
2103 2107 ang_aux = ang_aux.reshape(1,ang_aux.size)
2104 2108
2105 2109 cosdir = numpy.zeros((arrayPhase.shape[0],2))
2106 2110 cosdir0 = numpy.zeros((arrayPhase.shape[0],2))
2107 2111
2108 2112
2109 2113 for i in range(2):
2110 2114 #First Estimation
2111 2115 phi0_aux = arrayPhase[:,pairsList[i][0]] + arrayPhase[:,pairsList[i][1]]
2112 2116 #Dealias
2113 2117 indcsi = numpy.where(phi0_aux > numpy.pi)
2114 2118 phi0_aux[indcsi] -= 2*numpy.pi
2115 2119 indcsi = numpy.where(phi0_aux < -numpy.pi)
2116 2120 phi0_aux[indcsi] += 2*numpy.pi
2117 2121 #Direction Cosine 0
2118 2122 cosdir0[:,i] = -(phi0_aux)/(2*numpy.pi*0.5)
2119 2123
2120 2124 #Most-Accurate Second Estimation
2121 2125 phi1_aux = arrayPhase[:,pairsList[i][0]] - arrayPhase[:,pairsList[i][1]]
2122 2126 phi1_aux = phi1_aux.reshape(phi1_aux.size,1)
2123 2127 #Direction Cosine 1
2124 2128 cosdir1 = -(phi1_aux + ang_aux)/(2*numpy.pi*4.5)
2125 2129
2126 2130 #Searching the correct Direction Cosine
2127 2131 cosdir0_aux = cosdir0[:,i]
2128 2132 cosdir0_aux = cosdir0_aux.reshape(cosdir0_aux.size,1)
2129 2133 #Minimum Distance
2130 2134 cosDiff = (cosdir1 - cosdir0_aux)**2
2131 2135 indcos = cosDiff.argmin(axis = 1)
2132 2136 #Saving Value obtained
2133 2137 cosdir[:,i] = cosdir1[numpy.arange(len(indcos)),indcos]
2134 2138
2135 2139 return cosdir0, cosdir
2136 2140
2137 2141 def __calculateAOA(self, cosdir, azimuth):
2138 2142 cosdirX = cosdir[:,0]
2139 2143 cosdirY = cosdir[:,1]
2140 2144
2141 2145 zenithAngle = numpy.arccos(numpy.sqrt(1 - cosdirX**2 - cosdirY**2))*180/numpy.pi
2142 2146 azimuthAngle = numpy.arctan2(cosdirX,cosdirY)*180/numpy.pi + azimuth #0 deg north, 90 deg east
2143 2147 angles = numpy.vstack((azimuthAngle, zenithAngle)).transpose()
2144 2148
2145 2149 return angles
2146 2150
2147 2151 def __getHeights(self, Ranges, zenith, error, minHeight, maxHeight):
2148 2152
2149 2153 Ramb = 375 #Ramb = c/(2*PRF)
2150 2154 Re = 6371 #Earth Radius
2151 2155 heights = numpy.zeros(Ranges.shape)
2152 2156
2153 2157 R_aux = numpy.array([0,1,2])*Ramb
2154 2158 R_aux = R_aux.reshape(1,R_aux.size)
2155 2159
2156 2160 Ranges = Ranges.reshape(Ranges.size,1)
2157 2161
2158 2162 Ri = Ranges + R_aux
2159 2163 hi = numpy.sqrt(Re**2 + Ri**2 + (2*Re*numpy.cos(zenith*numpy.pi/180)*Ri.transpose()).transpose()) - Re
2160 2164
2161 2165 #Check if there is a height between 70 and 110 km
2162 2166 h_bool = numpy.sum(numpy.logical_and(hi > minHeight, hi < maxHeight), axis = 1)
2163 2167 ind_h = numpy.where(h_bool == 1)[0]
2164 2168
2165 2169 hCorr = hi[ind_h, :]
2166 2170 ind_hCorr = numpy.where(numpy.logical_and(hi > minHeight, hi < maxHeight))
2167 2171
2168 2172 hCorr = hi[ind_hCorr]
2169 2173 heights[ind_h] = hCorr
2170 2174
2171 2175 #Setting Error
2172 2176 #Number 13: Height unresolvable echo: not valid height within 70 to 110 km
2173 2177 #Number 14: Height ambiguous echo: more than one possible height within 70 to 110 km
2174 2178 indError = numpy.where(numpy.logical_or(error == 13, error == 14))[0]
2175 2179 error[indError] = 0
2176 2180 indInvalid2 = numpy.where(numpy.logical_and(h_bool > 1, error == 0))[0]
2177 2181 error[indInvalid2] = 14
2178 2182 indInvalid1 = numpy.where(numpy.logical_and(h_bool == 0, error == 0))[0]
2179 2183 error[indInvalid1] = 13
2180 2184
2181 2185 return heights, error No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now