##// END OF EJS Templates
valid en jroproc_parameters
avaldez -
r1394:99588b4ace71
parent child
Show More
@@ -1,509 +1,517
1 1 import os
2 2 import datetime
3 3 import numpy
4 4
5 5 from schainpy.model.graphics.jroplot_base import Plot, plt
6 6 from schainpy.model.graphics.jroplot_spectra import SpectraPlot, RTIPlot, CoherencePlot, SpectraCutPlot
7 7 from schainpy.utils import log
8 8 # libreria wradlib
9 9 import wradlib as wrl
10 10
11 11 EARTH_RADIUS = 6.3710e3
12 12
13 13
14 14 def ll2xy(lat1, lon1, lat2, lon2):
15 15
16 16 p = 0.017453292519943295
17 17 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
18 18 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
19 19 r = 12742 * numpy.arcsin(numpy.sqrt(a))
20 20 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
21 21 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
22 22 theta = -theta + numpy.pi/2
23 23 return r*numpy.cos(theta), r*numpy.sin(theta)
24 24
25 25
26 26 def km2deg(km):
27 27 '''
28 28 Convert distance in km to degrees
29 29 '''
30 30
31 31 return numpy.rad2deg(km/EARTH_RADIUS)
32 32
33 33
34 34
35 35 class SpectralMomentsPlot(SpectraPlot):
36 36 '''
37 37 Plot for Spectral Moments
38 38 '''
39 39 CODE = 'spc_moments'
40 40 # colormap = 'jet'
41 41 # plot_type = 'pcolor'
42 42
43 43 class DobleGaussianPlot(SpectraPlot):
44 44 '''
45 45 Plot for Double Gaussian Plot
46 46 '''
47 47 CODE = 'gaussian_fit'
48 48 # colormap = 'jet'
49 49 # plot_type = 'pcolor'
50 50
51 51 class DoubleGaussianSpectraCutPlot(SpectraCutPlot):
52 52 '''
53 53 Plot SpectraCut with Double Gaussian Fit
54 54 '''
55 55 CODE = 'cut_gaussian_fit'
56 56
57 57 class SnrPlot(RTIPlot):
58 58 '''
59 59 Plot for SNR Data
60 60 '''
61 61
62 62 CODE = 'snr'
63 63 colormap = 'jet'
64 64
65 65 def update(self, dataOut):
66 66
67 67 data = {
68 68 'snr': 10*numpy.log10(dataOut.data_snr)
69 69 }
70 70
71 71 return data, {}
72 72
73 73 class DopplerPlot(RTIPlot):
74 74 '''
75 75 Plot for DOPPLER Data (1st moment)
76 76 '''
77 77
78 78 CODE = 'dop'
79 79 colormap = 'jet'
80 80
81 81 def update(self, dataOut):
82 82
83 83 data = {
84 84 'dop': 10*numpy.log10(dataOut.data_dop)
85 85 }
86 86
87 87 return data, {}
88 88
89 89 class PowerPlot(RTIPlot):
90 90 '''
91 91 Plot for Power Data (0 moment)
92 92 '''
93 93
94 94 CODE = 'pow'
95 95 colormap = 'jet'
96 96
97 97 def update(self, dataOut):
98 98 data = {
99 99 'pow': 10*numpy.log10(dataOut.data_pow/dataOut.normFactor)
100 100 }
101 101 return data, {}
102 102
103 103 class SpectralWidthPlot(RTIPlot):
104 104 '''
105 105 Plot for Spectral Width Data (2nd moment)
106 106 '''
107 107
108 108 CODE = 'width'
109 109 colormap = 'jet'
110 110
111 111 def update(self, dataOut):
112 112
113 113 data = {
114 114 'width': dataOut.data_width
115 115 }
116 116
117 117 return data, {}
118 118
119 119 class SkyMapPlot(Plot):
120 120 '''
121 121 Plot for meteors detection data
122 122 '''
123 123
124 124 CODE = 'param'
125 125
126 126 def setup(self):
127 127
128 128 self.ncols = 1
129 129 self.nrows = 1
130 130 self.width = 7.2
131 131 self.height = 7.2
132 132 self.nplots = 1
133 133 self.xlabel = 'Zonal Zenith Angle (deg)'
134 134 self.ylabel = 'Meridional Zenith Angle (deg)'
135 135 self.polar = True
136 136 self.ymin = -180
137 137 self.ymax = 180
138 138 self.colorbar = False
139 139
140 140 def plot(self):
141 141
142 142 arrayParameters = numpy.concatenate(self.data['param'])
143 143 error = arrayParameters[:, -1]
144 144 indValid = numpy.where(error == 0)[0]
145 145 finalMeteor = arrayParameters[indValid, :]
146 146 finalAzimuth = finalMeteor[:, 3]
147 147 finalZenith = finalMeteor[:, 4]
148 148
149 149 x = finalAzimuth * numpy.pi / 180
150 150 y = finalZenith
151 151
152 152 ax = self.axes[0]
153 153
154 154 if ax.firsttime:
155 155 ax.plot = ax.plot(x, y, 'bo', markersize=5)[0]
156 156 else:
157 157 ax.plot.set_data(x, y)
158 158
159 159 dt1 = self.getDateTime(self.data.min_time).strftime('%y/%m/%d %H:%M:%S')
160 160 dt2 = self.getDateTime(self.data.max_time).strftime('%y/%m/%d %H:%M:%S')
161 161 title = 'Meteor Detection Sky Map\n %s - %s \n Number of events: %5.0f\n' % (dt1,
162 162 dt2,
163 163 len(x))
164 164 self.titles[0] = title
165 165
166 166
167 167 class GenericRTIPlot(Plot):
168 168 '''
169 169 Plot for data_xxxx object
170 170 '''
171 171
172 172 CODE = 'param'
173 173 colormap = 'viridis'
174 174 plot_type = 'pcolorbuffer'
175 175
176 176 def setup(self):
177 177 self.xaxis = 'time'
178 178 self.ncols = 1
179 179 self.nrows = self.data.shape('param')[0]
180 180 self.nplots = self.nrows
181 181 self.plots_adjust.update({'hspace':0.8, 'left': 0.1, 'bottom': 0.08, 'right':0.95, 'top': 0.95})
182 182
183 183 if not self.xlabel:
184 184 self.xlabel = 'Time'
185 185
186 186 self.ylabel = 'Range [km]'
187 187 if not self.titles:
188 188 self.titles = ['Param {}'.format(x) for x in range(self.nrows)]
189 189
190 190 def update(self, dataOut):
191 191
192 192 data = {
193 193 'param' : numpy.concatenate([getattr(dataOut, attr) for attr in self.attr_data], axis=0)
194 194 }
195 195
196 196 meta = {}
197 197
198 198 return data, meta
199 199
200 200 def plot(self):
201 201 # self.data.normalize_heights()
202 202 self.x = self.data.times
203 203 self.y = self.data.yrange
204 204 self.z = self.data['param']
205 205 self.z = 10*numpy.log10(self.z)
206 206 self.z = numpy.ma.masked_invalid(self.z)
207 207
208 208 if self.decimation is None:
209 209 x, y, z = self.fill_gaps(self.x, self.y, self.z)
210 210 else:
211 211 x, y, z = self.fill_gaps(*self.decimate())
212 212
213 213 for n, ax in enumerate(self.axes):
214 214
215 215 self.zmax = self.zmax if self.zmax is not None else numpy.max(
216 216 self.z[n])
217 217 self.zmin = self.zmin if self.zmin is not None else numpy.min(
218 218 self.z[n])
219 219
220 220 if ax.firsttime:
221 221 if self.zlimits is not None:
222 222 self.zmin, self.zmax = self.zlimits[n]
223 223
224 224 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
225 225 vmin=self.zmin,
226 226 vmax=self.zmax,
227 227 cmap=self.cmaps[n]
228 228 )
229 229 else:
230 230 if self.zlimits is not None:
231 231 self.zmin, self.zmax = self.zlimits[n]
232 232 ax.collections.remove(ax.collections[0])
233 233 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
234 234 vmin=self.zmin,
235 235 vmax=self.zmax,
236 236 cmap=self.cmaps[n]
237 237 )
238 238
239 239
240 240 class PolarMapPlot(Plot):
241 241 '''
242 242 Plot for weather radar
243 243 '''
244 244
245 245 CODE = 'param'
246 246 colormap = 'seismic'
247 247
248 248 def setup(self):
249 249 self.ncols = 1
250 250 self.nrows = 1
251 251 self.width = 9
252 252 self.height = 8
253 253 self.mode = self.data.meta['mode']
254 254 if self.channels is not None:
255 255 self.nplots = len(self.channels)
256 256 self.nrows = len(self.channels)
257 257 else:
258 258 self.nplots = self.data.shape(self.CODE)[0]
259 259 self.nrows = self.nplots
260 260 self.channels = list(range(self.nplots))
261 261 if self.mode == 'E':
262 262 self.xlabel = 'Longitude'
263 263 self.ylabel = 'Latitude'
264 264 else:
265 265 self.xlabel = 'Range (km)'
266 266 self.ylabel = 'Height (km)'
267 267 self.bgcolor = 'white'
268 268 self.cb_labels = self.data.meta['units']
269 269 self.lat = self.data.meta['latitude']
270 270 self.lon = self.data.meta['longitude']
271 271 self.xmin, self.xmax = float(
272 272 km2deg(self.xmin) + self.lon), float(km2deg(self.xmax) + self.lon)
273 273 self.ymin, self.ymax = float(
274 274 km2deg(self.ymin) + self.lat), float(km2deg(self.ymax) + self.lat)
275 275 # self.polar = True
276 276
277 277 def plot(self):
278 278
279 279 for n, ax in enumerate(self.axes):
280 280 data = self.data['param'][self.channels[n]]
281 281
282 282 zeniths = numpy.linspace(
283 283 0, self.data.meta['max_range'], data.shape[1])
284 284 if self.mode == 'E':
285 285 azimuths = -numpy.radians(self.data.yrange)+numpy.pi/2
286 286 r, theta = numpy.meshgrid(zeniths, azimuths)
287 287 x, y = r*numpy.cos(theta)*numpy.cos(numpy.radians(self.data.meta['elevation'])), r*numpy.sin(
288 288 theta)*numpy.cos(numpy.radians(self.data.meta['elevation']))
289 289 x = km2deg(x) + self.lon
290 290 y = km2deg(y) + self.lat
291 291 else:
292 292 azimuths = numpy.radians(self.data.yrange)
293 293 r, theta = numpy.meshgrid(zeniths, azimuths)
294 294 x, y = r*numpy.cos(theta), r*numpy.sin(theta)
295 295 self.y = zeniths
296 296
297 297 if ax.firsttime:
298 298 if self.zlimits is not None:
299 299 self.zmin, self.zmax = self.zlimits[n]
300 300 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
301 301 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
302 302 vmin=self.zmin,
303 303 vmax=self.zmax,
304 304 cmap=self.cmaps[n])
305 305 else:
306 306 if self.zlimits is not None:
307 307 self.zmin, self.zmax = self.zlimits[n]
308 308 ax.collections.remove(ax.collections[0])
309 309 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
310 310 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
311 311 vmin=self.zmin,
312 312 vmax=self.zmax,
313 313 cmap=self.cmaps[n])
314 314
315 315 if self.mode == 'A':
316 316 continue
317 317
318 318 # plot district names
319 319 f = open('/data/workspace/schain_scripts/distrito.csv')
320 320 for line in f:
321 321 label, lon, lat = [s.strip() for s in line.split(',') if s]
322 322 lat = float(lat)
323 323 lon = float(lon)
324 324 # ax.plot(lon, lat, '.b', ms=2)
325 325 ax.text(lon, lat, label.decode('utf8'), ha='center',
326 326 va='bottom', size='8', color='black')
327 327
328 328 # plot limites
329 329 limites = []
330 330 tmp = []
331 331 for line in open('/data/workspace/schain_scripts/lima.csv'):
332 332 if '#' in line:
333 333 if tmp:
334 334 limites.append(tmp)
335 335 tmp = []
336 336 continue
337 337 values = line.strip().split(',')
338 338 tmp.append((float(values[0]), float(values[1])))
339 339 for points in limites:
340 340 ax.add_patch(
341 341 Polygon(points, ec='k', fc='none', ls='--', lw=0.5))
342 342
343 343 # plot Cuencas
344 344 for cuenca in ('rimac', 'lurin', 'mala', 'chillon', 'chilca', 'chancay-huaral'):
345 345 f = open('/data/workspace/schain_scripts/{}.csv'.format(cuenca))
346 346 values = [line.strip().split(',') for line in f]
347 347 points = [(float(s[0]), float(s[1])) for s in values]
348 348 ax.add_patch(Polygon(points, ec='b', fc='none'))
349 349
350 350 # plot grid
351 351 for r in (15, 30, 45, 60):
352 352 ax.add_artist(plt.Circle((self.lon, self.lat),
353 353 km2deg(r), color='0.6', fill=False, lw=0.2))
354 354 ax.text(
355 355 self.lon + (km2deg(r))*numpy.cos(60*numpy.pi/180),
356 356 self.lat + (km2deg(r))*numpy.sin(60*numpy.pi/180),
357 357 '{}km'.format(r),
358 358 ha='center', va='bottom', size='8', color='0.6', weight='heavy')
359 359
360 360 if self.mode == 'E':
361 361 title = 'El={}$^\circ$'.format(self.data.meta['elevation'])
362 362 label = 'E{:02d}'.format(int(self.data.meta['elevation']))
363 363 else:
364 364 title = 'Az={}$^\circ$'.format(self.data.meta['azimuth'])
365 365 label = 'A{:02d}'.format(int(self.data.meta['azimuth']))
366 366
367 367 self.save_labels = ['{}-{}'.format(lbl, label) for lbl in self.labels]
368 368 self.titles = ['{} {}'.format(
369 369 self.data.parameters[x], title) for x in self.channels]
370 370
371 371 class WeatherPlot(Plot):
372 372 CODE = 'weather'
373 373 plot_name = 'weather'
374 374 plot_type = 'ppistyle'
375 375 buffering = False
376 376
377 377 def setup(self):
378 378 self.ncols = 1
379 379 self.nrows = 1
380 380 self.nplots= 1
381 381 self.ylabel= 'Range [Km]'
382 382 self.titles= ['Weather']
383 383 self.colorbar=False
384 384 self.width =8
385 385 self.height =8
386 386 self.ini =0
387 387 self.len_azi =0
388 388 self.buffer_ini = None
389 389 self.buffer_azi = None
390 390 self.plots_adjust.update({'wspace': 0.4, 'hspace':0.4, 'left': 0.1, 'right': 0.9, 'bottom': 0.08})
391 391 self.flag =0
392 392 self.indicador= 0
393 393
394 394 def update(self, dataOut):
395 395
396 396 data = {}
397 397 meta = {}
398 data['weather'] = 10*numpy.log10(dataOut.data_360[0]/(250.0))
398 if hasattr(dataOut, 'dataPP_POWER'):
399 factor = 1
400
401 if hasattr(dataOut, 'nFFTPoints'):
402 factor = dataOut.normFactor
403
404 print("factor",factor)
405 data['weather'] = 10*numpy.log10(dataOut.data_360[0]/(factor))
406 print("weather",data['weather'])
399 407 data['azi'] = dataOut.data_azi
400 408 return data, meta
401 409
402 410 def const_ploteo(self,data_weather,data_azi,step,res):
403 411 if self.ini==0:
404 412 #------- AZIMUTH
405 413 n = (360/res)-len(data_azi)
406 414 start = data_azi[-1] + res
407 415 end = data_azi[0] - res
408 416 if start>end:
409 417 end = end + 360
410 418 azi_vacia = numpy.linspace(start,end,int(n))
411 419 azi_vacia = numpy.where(azi_vacia>360,azi_vacia-360,azi_vacia)
412 420 data_azi = numpy.hstack((data_azi,azi_vacia))
413 421 # RADAR
414 422 val_mean = numpy.mean(data_weather[:,0])
415 423 data_weather_cmp = numpy.ones([(360-data_weather.shape[0]),data_weather.shape[1]])*val_mean
416 424 data_weather = numpy.vstack((data_weather,data_weather_cmp))
417 425 else:
418 426 # azimuth
419 427 flag=0
420 428 start_azi = self.res_azi[0]
421 429 start = data_azi[0]
422 430 end = data_azi[-1]
423 431 print("start",start)
424 432 print("end",end)
425 433 if start< start_azi:
426 434 start = start +360
427 435 if end <start_azi:
428 436 end = end +360
429 437
430 438 print("start",start)
431 439 print("end",end)
432 440 #### AQUI SERA LA MAGIA
433 441 pos_ini = int((start-start_azi)/res)
434 442 len_azi = len(data_azi)
435 443 if (360-pos_ini)<len_azi:
436 444 if pos_ini+1==360:
437 445 pos_ini=0
438 446 else:
439 447 flag=1
440 448 dif= 360-pos_ini
441 449 comp= len_azi-dif
442 450
443 451 print(pos_ini)
444 452 print(len_azi)
445 453 print("shape",self.res_azi.shape)
446 454 if flag==0:
447 455 # AZIMUTH
448 456 self.res_azi[pos_ini:pos_ini+len_azi] = data_azi
449 457 # RADAR
450 458 self.res_weather[pos_ini:pos_ini+len_azi,:] = data_weather
451 459 else:
452 460 # AZIMUTH
453 461 self.res_azi[pos_ini:pos_ini+dif] = data_azi[0:dif]
454 462 self.res_azi[0:comp] = data_azi[dif:]
455 463 # RADAR
456 464 self.res_weather[pos_ini:pos_ini+dif,:] = data_weather[0:dif,:]
457 465 self.res_weather[0:comp,:] = data_weather[dif:,:]
458 466 flag=0
459 467 data_azi = self.res_azi
460 468 data_weather = self.res_weather
461 469
462 470 return data_weather,data_azi
463 471
464 472 def plot(self):
465 473 print("--------------------------------------",self.ini,"-----------------------------------")
466 474 #numpy.set_printoptions(suppress=True)
467 475 #print(self.data.times)
468 476 thisDatetime = datetime.datetime.utcfromtimestamp(self.data.times[-1])
469 477 data = self.data[-1]
470 478 # ALTURA altura_tmp_h
471 479 altura_h = (data['weather'].shape[1])/10.0
472 480 stoprange = float(altura_h*1.5)#stoprange = float(33*1.5) por ahora 400
473 481 rangestep = float(0.15)
474 482 r = numpy.arange(0, stoprange, rangestep)
475 483 self.y = 2*r
476 484 # RADAR
477 485 #data_weather = data['weather']
478 486 # PEDESTAL
479 487 #data_azi = data['azi']
480 488 res = 1
481 489 # STEP
482 490 step = (360/(res*data['weather'].shape[0]))
483 491 #print("shape wr_data", wr_data.shape)
484 492 #print("shape wr_azi",wr_azi.shape)
485 493 #print("step",step)
486 494 print("Time---->",self.data.times[-1],thisDatetime)
487 495 #print("alturas", len(self.y))
488 496 self.res_weather, self.res_azi = self.const_ploteo(data_weather=data['weather'],data_azi=data['azi'],step=step,res=res)
489 497 #numpy.set_printoptions(suppress=True)
490 498 #print("resultado",self.res_azi)
491 499 ##########################################################
492 500 ################# PLOTEO ###################
493 501 ##########################################################
494 502
495 503 for i,ax in enumerate(self.axes):
496 504 if ax.firsttime:
497 505 plt.clf()
498 506 cgax, pm = wrl.vis.plot_ppi(self.res_weather,r=r,az=self.res_azi,fig=self.figures[0], proj='cg', vmin=1, vmax=60)
499 507 else:
500 508 plt.clf()
501 cgax, pm = wrl.vis.plot_ppi(self.res_weather,r=r,az=self.res_azi,fig=self.figures[0], proj='cg', vmin=0, vmax=60)
509 cgax, pm = wrl.vis.plot_ppi(self.res_weather,r=r,az=self.res_azi,fig=self.figures[0], proj='cg', vmin=1, vmax=60)
502 510 caax = cgax.parasites[0]
503 511 paax = cgax.parasites[1]
504 512 cbar = plt.gcf().colorbar(pm, pad=0.075)
505 513 caax.set_xlabel('x_range [km]')
506 514 caax.set_ylabel('y_range [km]')
507 515 plt.text(1.0, 1.05, 'azimuth '+str(thisDatetime)+"step"+str(self.ini), transform=caax.transAxes, va='bottom',ha='right')
508 516
509 517 self.ini= self.ini+1
@@ -1,4474 +1,4473
1 1 import numpy,os,h5py
2 2 import math
3 3 from scipy import optimize, interpolate, signal, stats, ndimage
4 4 import scipy
5 5 import re
6 6 import datetime
7 7 import copy
8 8 import sys
9 9 import importlib
10 10 import itertools
11 11 from multiprocessing import Pool, TimeoutError
12 12 from multiprocessing.pool import ThreadPool
13 13 import time
14 14
15 15 from scipy.optimize import fmin_l_bfgs_b #optimize with bounds on state papameters
16 16 from .jroproc_base import ProcessingUnit, Operation, MPDecorator
17 17 from schainpy.model.data.jrodata import Parameters, hildebrand_sekhon
18 18 from scipy import asarray as ar,exp
19 19 from scipy.optimize import curve_fit
20 20 from schainpy.utils import log
21 21 import warnings
22 22 from numpy import NaN
23 23 from scipy.optimize.optimize import OptimizeWarning
24 24 warnings.filterwarnings('ignore')
25 25
26 26 import matplotlib.pyplot as plt
27 27
28 28 SPEED_OF_LIGHT = 299792458
29 29
30 30 '''solving pickling issue'''
31 31
32 32 def _pickle_method(method):
33 33 func_name = method.__func__.__name__
34 34 obj = method.__self__
35 35 cls = method.__self__.__class__
36 36 return _unpickle_method, (func_name, obj, cls)
37 37
38 38 def _unpickle_method(func_name, obj, cls):
39 39 for cls in cls.mro():
40 40 try:
41 41 func = cls.__dict__[func_name]
42 42 except KeyError:
43 43 pass
44 44 else:
45 45 break
46 46 return func.__get__(obj, cls)
47 47
48 48 def isNumber(str):
49 49 try:
50 50 float(str)
51 51 return True
52 52 except:
53 53 return False
54 54
55 55 class ParametersProc(ProcessingUnit):
56 56
57 57 METHODS = {}
58 58 nSeconds = None
59 59
60 60 def __init__(self):
61 61 ProcessingUnit.__init__(self)
62 62
63 63 # self.objectDict = {}
64 64 self.buffer = None
65 65 self.firstdatatime = None
66 66 self.profIndex = 0
67 67 self.dataOut = Parameters()
68 68 self.setupReq = False #Agregar a todas las unidades de proc
69 69
70 70 def __updateObjFromInput(self):
71 71
72 72 self.dataOut.inputUnit = self.dataIn.type
73 73
74 74 self.dataOut.timeZone = self.dataIn.timeZone
75 75 self.dataOut.dstFlag = self.dataIn.dstFlag
76 76 self.dataOut.errorCount = self.dataIn.errorCount
77 77 self.dataOut.useLocalTime = self.dataIn.useLocalTime
78 78
79 79 self.dataOut.radarControllerHeaderObj = self.dataIn.radarControllerHeaderObj.copy()
80 80 self.dataOut.systemHeaderObj = self.dataIn.systemHeaderObj.copy()
81 81 self.dataOut.channelList = self.dataIn.channelList
82 82 self.dataOut.heightList = self.dataIn.heightList
83 83 self.dataOut.dtype = numpy.dtype([('real','<f4'),('imag','<f4')])
84 84 # self.dataOut.nHeights = self.dataIn.nHeights
85 85 # self.dataOut.nChannels = self.dataIn.nChannels
86 86 # self.dataOut.nBaud = self.dataIn.nBaud
87 87 # self.dataOut.nCode = self.dataIn.nCode
88 88 # self.dataOut.code = self.dataIn.code
89 89 # self.dataOut.nProfiles = self.dataOut.nFFTPoints
90 90 self.dataOut.flagDiscontinuousBlock = self.dataIn.flagDiscontinuousBlock
91 91 # self.dataOut.utctime = self.firstdatatime
92 92 self.dataOut.utctime = self.dataIn.utctime
93 93 self.dataOut.flagDecodeData = self.dataIn.flagDecodeData #asumo q la data esta decodificada
94 94 self.dataOut.flagDeflipData = self.dataIn.flagDeflipData #asumo q la data esta sin flip
95 95 self.dataOut.nCohInt = self.dataIn.nCohInt
96 96 # self.dataOut.nIncohInt = 1
97 97 # self.dataOut.ippSeconds = self.dataIn.ippSeconds
98 98 # self.dataOut.windowOfFilter = self.dataIn.windowOfFilter
99 99 self.dataOut.timeInterval1 = self.dataIn.timeInterval
100 100 self.dataOut.heightList = self.dataIn.heightList
101 101 self.dataOut.frequency = self.dataIn.frequency
102 102 # self.dataOut.noise = self.dataIn.noise
103 103
104 104 def run(self):
105 105
106 106
107 107 #print("HOLA MUNDO SOY YO")
108 108 #---------------------- Voltage Data ---------------------------
109 109
110 110 if self.dataIn.type == "Voltage":
111 111
112 112 self.__updateObjFromInput()
113 113 self.dataOut.data_pre = self.dataIn.data.copy()
114 114 self.dataOut.flagNoData = False
115 115 self.dataOut.utctimeInit = self.dataIn.utctime
116 116 self.dataOut.paramInterval = self.dataIn.nProfiles*self.dataIn.nCohInt*self.dataIn.ippSeconds
117 117
118 118 if hasattr(self.dataIn, 'flagDataAsBlock'):
119 119 self.dataOut.flagDataAsBlock = self.dataIn.flagDataAsBlock
120 120
121 121 if hasattr(self.dataIn, 'profileIndex'):
122 122 self.dataOut.profileIndex = self.dataIn.profileIndex
123 123
124 124 if hasattr(self.dataIn, 'dataPP_POW'):
125 125 self.dataOut.dataPP_POW = self.dataIn.dataPP_POW
126 126
127 127 if hasattr(self.dataIn, 'dataPP_POWER'):
128 128 self.dataOut.dataPP_POWER = self.dataIn.dataPP_POWER
129 129
130 130 if hasattr(self.dataIn, 'dataPP_DOP'):
131 131 self.dataOut.dataPP_DOP = self.dataIn.dataPP_DOP
132 132
133 133 if hasattr(self.dataIn, 'dataPP_SNR'):
134 134 self.dataOut.dataPP_SNR = self.dataIn.dataPP_SNR
135 135
136 136 if hasattr(self.dataIn, 'dataPP_WIDTH'):
137 137 self.dataOut.dataPP_WIDTH = self.dataIn.dataPP_WIDTH
138 138 return
139 139
140 140 #---------------------- Spectra Data ---------------------------
141 141
142 142 if self.dataIn.type == "Spectra":
143 143 #print("que paso en spectra")
144 144 self.dataOut.data_pre = [self.dataIn.data_spc, self.dataIn.data_cspc]
145 145 self.dataOut.data_spc = self.dataIn.data_spc
146 146 self.dataOut.data_cspc = self.dataIn.data_cspc
147 147 self.dataOut.nProfiles = self.dataIn.nProfiles
148 148 self.dataOut.nIncohInt = self.dataIn.nIncohInt
149 149 self.dataOut.nFFTPoints = self.dataIn.nFFTPoints
150 150 self.dataOut.ippFactor = self.dataIn.ippFactor
151 151 self.dataOut.abscissaList = self.dataIn.getVelRange(1)
152 152 self.dataOut.spc_noise = self.dataIn.getNoise()
153 153 self.dataOut.spc_range = (self.dataIn.getFreqRange(1) , self.dataIn.getAcfRange(1) , self.dataIn.getVelRange(1))
154 154 # self.dataOut.normFactor = self.dataIn.normFactor
155 155 self.dataOut.pairsList = self.dataIn.pairsList
156 156 self.dataOut.groupList = self.dataIn.pairsList
157 157 self.dataOut.flagNoData = False
158 158
159 159 if hasattr(self.dataIn, 'flagDataAsBlock'):
160 160 self.dataOut.flagDataAsBlock = self.dataIn.flagDataAsBlock
161 161
162 162 if hasattr(self.dataIn, 'ChanDist'): #Distances of receiver channels
163 163 self.dataOut.ChanDist = self.dataIn.ChanDist
164 164 else: self.dataOut.ChanDist = None
165 165
166 166 #if hasattr(self.dataIn, 'VelRange'): #Velocities range
167 167 # self.dataOut.VelRange = self.dataIn.VelRange
168 168 #else: self.dataOut.VelRange = None
169 169
170 170 if hasattr(self.dataIn, 'RadarConst'): #Radar Constant
171 171 self.dataOut.RadarConst = self.dataIn.RadarConst
172 172
173 173 if hasattr(self.dataIn, 'NPW'): #NPW
174 174 self.dataOut.NPW = self.dataIn.NPW
175 175
176 176 if hasattr(self.dataIn, 'COFA'): #COFA
177 177 self.dataOut.COFA = self.dataIn.COFA
178 178
179 179
180 180
181 181 #---------------------- Correlation Data ---------------------------
182 182
183 183 if self.dataIn.type == "Correlation":
184 184 acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf = self.dataIn.splitFunctions()
185 185
186 186 self.dataOut.data_pre = (self.dataIn.data_cf[acf_ind,:], self.dataIn.data_cf[ccf_ind,:,:])
187 187 self.dataOut.normFactor = (self.dataIn.normFactor[acf_ind,:], self.dataIn.normFactor[ccf_ind,:])
188 188 self.dataOut.groupList = (acf_pairs, ccf_pairs)
189 189
190 190 self.dataOut.abscissaList = self.dataIn.lagRange
191 191 self.dataOut.noise = self.dataIn.noise
192 192 self.dataOut.data_snr = self.dataIn.SNR
193 193 self.dataOut.flagNoData = False
194 194 self.dataOut.nAvg = self.dataIn.nAvg
195 195
196 196 #---------------------- Parameters Data ---------------------------
197 197
198 198 if self.dataIn.type == "Parameters":
199 199 self.dataOut.copy(self.dataIn)
200 200 self.dataOut.flagNoData = False
201 201 #print("yo si entre")
202 202
203 203 return True
204 204
205 205 self.__updateObjFromInput()
206 206 #print("yo si entre2")
207 207
208 208 self.dataOut.utctimeInit = self.dataIn.utctime
209 209 self.dataOut.paramInterval = self.dataIn.timeInterval
210 210 #print("soy spectra ",self.dataOut.utctimeInit)
211 211 return
212 212
213 213
214 214 def target(tups):
215 215
216 216 obj, args = tups
217 217
218 218 return obj.FitGau(args)
219 219
220 220 class RemoveWideGC(Operation):
221 221 ''' This class remove the wide clutter and replace it with a simple interpolation points
222 222 This mainly applies to CLAIRE radar
223 223
224 224 ClutterWidth : Width to look for the clutter peak
225 225
226 226 Input:
227 227
228 228 self.dataOut.data_pre : SPC and CSPC
229 229 self.dataOut.spc_range : To select wind and rainfall velocities
230 230
231 231 Affected:
232 232
233 233 self.dataOut.data_pre : It is used for the new SPC and CSPC ranges of wind
234 234
235 235 Written by D. ScipiΓ³n 25.02.2021
236 236 '''
237 237 def __init__(self):
238 238 Operation.__init__(self)
239 239 self.i = 0
240 240 self.ich = 0
241 241 self.ir = 0
242 242
243 243 def run(self, dataOut, ClutterWidth=2.5):
244 244 # print ('Entering RemoveWideGC ... ')
245 245
246 246 self.spc = dataOut.data_pre[0].copy()
247 247 self.spc_out = dataOut.data_pre[0].copy()
248 248 self.Num_Chn = self.spc.shape[0]
249 249 self.Num_Hei = self.spc.shape[2]
250 250 VelRange = dataOut.spc_range[2][:-1]
251 251 dv = VelRange[1]-VelRange[0]
252 252
253 253 # Find the velocities that corresponds to zero
254 254 gc_values = numpy.squeeze(numpy.where(numpy.abs(VelRange) <= ClutterWidth))
255 255
256 256 # Removing novalid data from the spectra
257 257 for ich in range(self.Num_Chn) :
258 258 for ir in range(self.Num_Hei) :
259 259 # Estimate the noise at each range
260 260 HSn = hildebrand_sekhon(self.spc[ich,:,ir],dataOut.nIncohInt)
261 261
262 262 # Removing the noise floor at each range
263 263 novalid = numpy.where(self.spc[ich,:,ir] < HSn)
264 264 self.spc[ich,novalid,ir] = HSn
265 265
266 266 junk = numpy.append(numpy.insert(numpy.squeeze(self.spc[ich,gc_values,ir]),0,HSn),HSn)
267 267 j1index = numpy.squeeze(numpy.where(numpy.diff(junk)>0))
268 268 j2index = numpy.squeeze(numpy.where(numpy.diff(junk)<0))
269 269 if ((numpy.size(j1index)<=1) | (numpy.size(j2index)<=1)) :
270 270 continue
271 271 junk3 = numpy.squeeze(numpy.diff(j1index))
272 272 junk4 = numpy.squeeze(numpy.diff(j2index))
273 273
274 274 valleyindex = j2index[numpy.where(junk4>1)]
275 275 peakindex = j1index[numpy.where(junk3>1)]
276 276
277 277 isvalid = numpy.squeeze(numpy.where(numpy.abs(VelRange[gc_values[peakindex]]) <= 2.5*dv))
278 278 if numpy.size(isvalid) == 0 :
279 279 continue
280 280 if numpy.size(isvalid) >1 :
281 281 vindex = numpy.argmax(self.spc[ich,gc_values[peakindex[isvalid]],ir])
282 282 isvalid = isvalid[vindex]
283 283
284 284 # clutter peak
285 285 gcpeak = peakindex[isvalid]
286 286 vl = numpy.where(valleyindex < gcpeak)
287 287 if numpy.size(vl) == 0:
288 288 continue
289 289 gcvl = valleyindex[vl[0][-1]]
290 290 vr = numpy.where(valleyindex > gcpeak)
291 291 if numpy.size(vr) == 0:
292 292 continue
293 293 gcvr = valleyindex[vr[0][0]]
294 294
295 295 # Removing the clutter
296 296 interpindex = numpy.array([gc_values[gcvl], gc_values[gcvr]])
297 297 gcindex = gc_values[gcvl+1:gcvr-1]
298 298 self.spc_out[ich,gcindex,ir] = numpy.interp(VelRange[gcindex],VelRange[interpindex],self.spc[ich,interpindex,ir])
299 299
300 300 dataOut.data_pre[0] = self.spc_out
301 301 #print ('Leaving RemoveWideGC ... ')
302 302 return dataOut
303 303
304 304 class SpectralFilters(Operation):
305 305 ''' This class allows to replace the novalid values with noise for each channel
306 306 This applies to CLAIRE RADAR
307 307
308 308 PositiveLimit : RightLimit of novalid data
309 309 NegativeLimit : LeftLimit of novalid data
310 310
311 311 Input:
312 312
313 313 self.dataOut.data_pre : SPC and CSPC
314 314 self.dataOut.spc_range : To select wind and rainfall velocities
315 315
316 316 Affected:
317 317
318 318 self.dataOut.data_pre : It is used for the new SPC and CSPC ranges of wind
319 319
320 320 Written by D. ScipiΓ³n 29.01.2021
321 321 '''
322 322 def __init__(self):
323 323 Operation.__init__(self)
324 324 self.i = 0
325 325
326 326 def run(self, dataOut, ):
327 327
328 328 self.spc = dataOut.data_pre[0].copy()
329 329 self.Num_Chn = self.spc.shape[0]
330 330 VelRange = dataOut.spc_range[2]
331 331
332 332 # novalid corresponds to data within the Negative and PositiveLimit
333 333
334 334
335 335 # Removing novalid data from the spectra
336 336 for i in range(self.Num_Chn):
337 337 self.spc[i,novalid,:] = dataOut.noise[i]
338 338 dataOut.data_pre[0] = self.spc
339 339 return dataOut
340 340
341 341 class GaussianFit(Operation):
342 342
343 343 '''
344 344 Function that fit of one and two generalized gaussians (gg) based
345 345 on the PSD shape across an "power band" identified from a cumsum of
346 346 the measured spectrum - noise.
347 347
348 348 Input:
349 349 self.dataOut.data_pre : SelfSpectra
350 350
351 351 Output:
352 352 self.dataOut.SPCparam : SPC_ch1, SPC_ch2
353 353
354 354 '''
355 355 def __init__(self):
356 356 Operation.__init__(self)
357 357 self.i=0
358 358
359 359
360 360 # def run(self, dataOut, num_intg=7, pnoise=1., SNRlimit=-9): #num_intg: Incoherent integrations, pnoise: Noise, vel_arr: range of velocities, similar to the ftt points
361 361 def run(self, dataOut, SNRdBlimit=-9, method='generalized'):
362 362 """This routine will find a couple of generalized Gaussians to a power spectrum
363 363 methods: generalized, squared
364 364 input: spc
365 365 output:
366 366 noise, amplitude0,shift0,width0,p0,Amplitude1,shift1,width1,p1
367 367 """
368 368 print ('Entering ',method,' double Gaussian fit')
369 369 self.spc = dataOut.data_pre[0].copy()
370 370 self.Num_Hei = self.spc.shape[2]
371 371 self.Num_Bin = self.spc.shape[1]
372 372 self.Num_Chn = self.spc.shape[0]
373 373
374 374 start_time = time.time()
375 375
376 376 pool = Pool(processes=self.Num_Chn)
377 377 args = [(dataOut.spc_range[2], ich, dataOut.spc_noise[ich], dataOut.nIncohInt, SNRdBlimit) for ich in range(self.Num_Chn)]
378 378 objs = [self for __ in range(self.Num_Chn)]
379 379 attrs = list(zip(objs, args))
380 380 DGauFitParam = pool.map(target, attrs)
381 381 # Parameters:
382 382 # 0. Noise, 1. Amplitude, 2. Shift, 3. Width 4. Power
383 383 dataOut.DGauFitParams = numpy.asarray(DGauFitParam)
384 384
385 385 # Double Gaussian Curves
386 386 gau0 = numpy.zeros([self.Num_Chn,self.Num_Bin,self.Num_Hei])
387 387 gau0[:] = numpy.NaN
388 388 gau1 = numpy.zeros([self.Num_Chn,self.Num_Bin,self.Num_Hei])
389 389 gau1[:] = numpy.NaN
390 390 x_mtr = numpy.transpose(numpy.tile(dataOut.getVelRange(1)[:-1], (self.Num_Hei,1)))
391 391 for iCh in range(self.Num_Chn):
392 392 N0 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][0,:,0]] * self.Num_Bin))
393 393 N1 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][0,:,1]] * self.Num_Bin))
394 394 A0 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][1,:,0]] * self.Num_Bin))
395 395 A1 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][1,:,1]] * self.Num_Bin))
396 396 v0 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][2,:,0]] * self.Num_Bin))
397 397 v1 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][2,:,1]] * self.Num_Bin))
398 398 s0 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][3,:,0]] * self.Num_Bin))
399 399 s1 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][3,:,1]] * self.Num_Bin))
400 400 if method == 'genealized':
401 401 p0 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][4,:,0]] * self.Num_Bin))
402 402 p1 = numpy.transpose(numpy.transpose([dataOut.DGauFitParams[iCh][4,:,1]] * self.Num_Bin))
403 403 elif method == 'squared':
404 404 p0 = 2.
405 405 p1 = 2.
406 406 gau0[iCh] = A0*numpy.exp(-0.5*numpy.abs((x_mtr-v0)/s0)**p0)+N0
407 407 gau1[iCh] = A1*numpy.exp(-0.5*numpy.abs((x_mtr-v1)/s1)**p1)+N1
408 408 dataOut.GaussFit0 = gau0
409 409 dataOut.GaussFit1 = gau1
410 410
411 411 print('Leaving ',method ,' double Gaussian fit')
412 412 return dataOut
413 413
414 414 def FitGau(self, X):
415 415 # print('Entering FitGau')
416 416 # Assigning the variables
417 417 Vrange, ch, wnoise, num_intg, SNRlimit = X
418 418 # Noise Limits
419 419 noisebl = wnoise * 0.9
420 420 noisebh = wnoise * 1.1
421 421 # Radar Velocity
422 422 Va = max(Vrange)
423 423 deltav = Vrange[1] - Vrange[0]
424 424 x = numpy.arange(self.Num_Bin)
425 425
426 426 # print ('stop 0')
427 427
428 428 # 5 parameters, 2 Gaussians
429 429 DGauFitParam = numpy.zeros([5, self.Num_Hei,2])
430 430 DGauFitParam[:] = numpy.NaN
431 431
432 432 # SPCparam = []
433 433 # SPC_ch1 = numpy.zeros([self.Num_Bin,self.Num_Hei])
434 434 # SPC_ch2 = numpy.zeros([self.Num_Bin,self.Num_Hei])
435 435 # SPC_ch1[:] = 0 #numpy.NaN
436 436 # SPC_ch2[:] = 0 #numpy.NaN
437 437 # print ('stop 1')
438 438 for ht in range(self.Num_Hei):
439 439 # print (ht)
440 440 # print ('stop 2')
441 441 # Spectra at each range
442 442 spc = numpy.asarray(self.spc)[ch,:,ht]
443 443 snr = ( spc.mean() - wnoise ) / wnoise
444 444 snrdB = 10.*numpy.log10(snr)
445 445
446 446 #print ('stop 3')
447 447 if snrdB < SNRlimit :
448 448 # snr = numpy.NaN
449 449 # SPC_ch1[:,ht] = 0#numpy.NaN
450 450 # SPC_ch1[:,ht] = 0#numpy.NaN
451 451 # SPCparam = (SPC_ch1,SPC_ch2)
452 452 # print ('SNR less than SNRth')
453 453 continue
454 454 # wnoise = hildebrand_sekhon(spc,num_intg)
455 455 # print ('stop 2.01')
456 456 #############################################
457 457 # normalizing spc and noise
458 458 # This part differs from gg1
459 459 # spc_norm_max = max(spc) #commented by D. ScipiΓ³n 19.03.2021
460 460 #spc = spc / spc_norm_max
461 461 # pnoise = pnoise #/ spc_norm_max #commented by D. ScipiΓ³n 19.03.2021
462 462 #############################################
463 463
464 464 # print ('stop 2.1')
465 465 fatspectra=1.0
466 466 # noise per channel.... we might want to use the noise at each range
467 467
468 468 # wnoise = noise_ #/ spc_norm_max #commented by D. ScipiΓ³n 19.03.2021
469 469 #wnoise,stdv,i_max,index =enoise(spc,num_intg) #noise estimate using Hildebrand Sekhon, only wnoise is used
470 470 #if wnoise>1.1*pnoise: # to be tested later
471 471 # wnoise=pnoise
472 472 # noisebl = wnoise*0.9
473 473 # noisebh = wnoise*1.1
474 474 spc = spc - wnoise # signal
475 475
476 476 # print ('stop 2.2')
477 477 minx = numpy.argmin(spc)
478 478 #spcs=spc.copy()
479 479 spcs = numpy.roll(spc,-minx)
480 480 cum = numpy.cumsum(spcs)
481 481 # tot_noise = wnoise * self.Num_Bin #64;
482 482
483 483 # print ('stop 2.3')
484 484 # snr = sum(spcs) / tot_noise
485 485 # snrdB = 10.*numpy.log10(snr)
486 486 #print ('stop 3')
487 487 # if snrdB < SNRlimit :
488 488 # snr = numpy.NaN
489 489 # SPC_ch1[:,ht] = 0#numpy.NaN
490 490 # SPC_ch1[:,ht] = 0#numpy.NaN
491 491 # SPCparam = (SPC_ch1,SPC_ch2)
492 492 # print ('SNR less than SNRth')
493 493 # continue
494 494
495 495
496 496 #if snrdB<-18 or numpy.isnan(snrdB) or num_intg<4:
497 497 # return [None,]*4,[None,]*4,None,snrdB,None,None,[None,]*5,[None,]*9,None
498 498 # print ('stop 4')
499 499 cummax = max(cum)
500 500 epsi = 0.08 * fatspectra # cumsum to narrow down the energy region
501 501 cumlo = cummax * epsi
502 502 cumhi = cummax * (1-epsi)
503 503 powerindex = numpy.array(numpy.where(numpy.logical_and(cum>cumlo, cum<cumhi))[0])
504 504
505 505 # print ('stop 5')
506 506 if len(powerindex) < 1:# case for powerindex 0
507 507 # print ('powerindex < 1')
508 508 continue
509 509 powerlo = powerindex[0]
510 510 powerhi = powerindex[-1]
511 511 powerwidth = powerhi-powerlo
512 512 if powerwidth <= 1:
513 513 # print('powerwidth <= 1')
514 514 continue
515 515
516 516 # print ('stop 6')
517 517 firstpeak = powerlo + powerwidth/10.# first gaussian energy location
518 518 secondpeak = powerhi - powerwidth/10. #second gaussian energy location
519 519 midpeak = (firstpeak + secondpeak)/2.
520 520 firstamp = spcs[int(firstpeak)]
521 521 secondamp = spcs[int(secondpeak)]
522 522 midamp = spcs[int(midpeak)]
523 523
524 524 y_data = spc + wnoise
525 525
526 526 ''' single Gaussian '''
527 527 shift0 = numpy.mod(midpeak+minx, self.Num_Bin )
528 528 width0 = powerwidth/4.#Initialization entire power of spectrum divided by 4
529 529 power0 = 2.
530 530 amplitude0 = midamp
531 531 state0 = [shift0,width0,amplitude0,power0,wnoise]
532 532 bnds = ((0,self.Num_Bin-1),(1,powerwidth),(0,None),(0.5,3.),(noisebl,noisebh))
533 533 lsq1 = fmin_l_bfgs_b(self.misfit1, state0, args=(y_data,x,num_intg), bounds=bnds, approx_grad=True)
534 534 # print ('stop 7.1')
535 535 # print (bnds)
536 536
537 537 chiSq1=lsq1[1]
538 538
539 539 # print ('stop 8')
540 540 if fatspectra<1.0 and powerwidth<4:
541 541 choice=0
542 542 Amplitude0=lsq1[0][2]
543 543 shift0=lsq1[0][0]
544 544 width0=lsq1[0][1]
545 545 p0=lsq1[0][3]
546 546 Amplitude1=0.
547 547 shift1=0.
548 548 width1=0.
549 549 p1=0.
550 550 noise=lsq1[0][4]
551 551 #return (numpy.array([shift0,width0,Amplitude0,p0]),
552 552 # numpy.array([shift1,width1,Amplitude1,p1]),noise,snrdB,chiSq1,6.,sigmas1,[None,]*9,choice)
553 553
554 554 # print ('stop 9')
555 555 ''' two Gaussians '''
556 556 #shift0=numpy.mod(firstpeak+minx,64); shift1=numpy.mod(secondpeak+minx,64)
557 557 shift0 = numpy.mod(firstpeak+minx, self.Num_Bin )
558 558 shift1 = numpy.mod(secondpeak+minx, self.Num_Bin )
559 559 width0 = powerwidth/6.
560 560 width1 = width0
561 561 power0 = 2.
562 562 power1 = power0
563 563 amplitude0 = firstamp
564 564 amplitude1 = secondamp
565 565 state0 = [shift0,width0,amplitude0,power0,shift1,width1,amplitude1,power1,wnoise]
566 566 #bnds=((0,63),(1,powerwidth/2.),(0,None),(0.5,3.),(0,63),(1,powerwidth/2.),(0,None),(0.5,3.),(noisebl,noisebh))
567 567 bnds=((0,self.Num_Bin-1),(1,powerwidth/2.),(0,None),(0.5,3.),(0,self.Num_Bin-1),(1,powerwidth/2.),(0,None),(0.5,3.),(noisebl,noisebh))
568 568 #bnds=(( 0,(self.Num_Bin-1) ),(1,powerwidth/2.),(0,None),(0.5,3.),( 0,(self.Num_Bin-1)),(1,powerwidth/2.),(0,None),(0.5,3.),(0.1,0.5))
569 569
570 570 # print ('stop 10')
571 571 lsq2 = fmin_l_bfgs_b( self.misfit2 , state0 , args=(y_data,x,num_intg) , bounds=bnds , approx_grad=True )
572 572
573 573 # print ('stop 11')
574 574 chiSq2 = lsq2[1]
575 575
576 576 # print ('stop 12')
577 577
578 578 oneG = (chiSq1<5 and chiSq1/chiSq2<2.0) and (abs(lsq2[0][0]-lsq2[0][4])<(lsq2[0][1]+lsq2[0][5])/3. or abs(lsq2[0][0]-lsq2[0][4])<10)
579 579
580 580 # print ('stop 13')
581 581 if snrdB>-12: # when SNR is strong pick the peak with least shift (LOS velocity) error
582 582 if oneG:
583 583 choice = 0
584 584 else:
585 585 w1 = lsq2[0][1]; w2 = lsq2[0][5]
586 586 a1 = lsq2[0][2]; a2 = lsq2[0][6]
587 587 p1 = lsq2[0][3]; p2 = lsq2[0][7]
588 588 s1 = (2**(1+1./p1))*scipy.special.gamma(1./p1)/p1
589 589 s2 = (2**(1+1./p2))*scipy.special.gamma(1./p2)/p2
590 590 gp1 = a1*w1*s1; gp2 = a2*w2*s2 # power content of each ggaussian with proper p scaling
591 591
592 592 if gp1>gp2:
593 593 if a1>0.7*a2:
594 594 choice = 1
595 595 else:
596 596 choice = 2
597 597 elif gp2>gp1:
598 598 if a2>0.7*a1:
599 599 choice = 2
600 600 else:
601 601 choice = 1
602 602 else:
603 603 choice = numpy.argmax([a1,a2])+1
604 604 #else:
605 605 #choice=argmin([std2a,std2b])+1
606 606
607 607 else: # with low SNR go to the most energetic peak
608 608 choice = numpy.argmax([lsq1[0][2]*lsq1[0][1],lsq2[0][2]*lsq2[0][1],lsq2[0][6]*lsq2[0][5]])
609 609
610 610 # print ('stop 14')
611 611 shift0 = lsq2[0][0]
612 612 vel0 = Vrange[0] + shift0 * deltav
613 613 shift1 = lsq2[0][4]
614 614 # vel1=Vrange[0] + shift1 * deltav
615 615
616 616 # max_vel = 1.0
617 617 # Va = max(Vrange)
618 618 # deltav = Vrange[1]-Vrange[0]
619 619 # print ('stop 15')
620 620 #first peak will be 0, second peak will be 1
621 621 # if vel0 > -1.0 and vel0 < max_vel : #first peak is in the correct range # Commented by D.ScipiΓ³n 19.03.2021
622 622 if vel0 > -Va and vel0 < Va : #first peak is in the correct range
623 623 shift0 = lsq2[0][0]
624 624 width0 = lsq2[0][1]
625 625 Amplitude0 = lsq2[0][2]
626 626 p0 = lsq2[0][3]
627 627
628 628 shift1 = lsq2[0][4]
629 629 width1 = lsq2[0][5]
630 630 Amplitude1 = lsq2[0][6]
631 631 p1 = lsq2[0][7]
632 632 noise = lsq2[0][8]
633 633 else:
634 634 shift1 = lsq2[0][0]
635 635 width1 = lsq2[0][1]
636 636 Amplitude1 = lsq2[0][2]
637 637 p1 = lsq2[0][3]
638 638
639 639 shift0 = lsq2[0][4]
640 640 width0 = lsq2[0][5]
641 641 Amplitude0 = lsq2[0][6]
642 642 p0 = lsq2[0][7]
643 643 noise = lsq2[0][8]
644 644
645 645 if Amplitude0<0.05: # in case the peak is noise
646 646 shift0,width0,Amplitude0,p0 = 4*[numpy.NaN]
647 647 if Amplitude1<0.05:
648 648 shift1,width1,Amplitude1,p1 = 4*[numpy.NaN]
649 649
650 650 # print ('stop 16 ')
651 651 # SPC_ch1[:,ht] = noise + Amplitude0*numpy.exp(-0.5*(abs(x-shift0)/width0)**p0)
652 652 # SPC_ch2[:,ht] = noise + Amplitude1*numpy.exp(-0.5*(abs(x-shift1)/width1)**p1)
653 653 # SPCparam = (SPC_ch1,SPC_ch2)
654 654
655 655 DGauFitParam[0,ht,0] = noise
656 656 DGauFitParam[0,ht,1] = noise
657 657 DGauFitParam[1,ht,0] = Amplitude0
658 658 DGauFitParam[1,ht,1] = Amplitude1
659 659 DGauFitParam[2,ht,0] = Vrange[0] + shift0 * deltav
660 660 DGauFitParam[2,ht,1] = Vrange[0] + shift1 * deltav
661 661 DGauFitParam[3,ht,0] = width0 * deltav
662 662 DGauFitParam[3,ht,1] = width1 * deltav
663 663 DGauFitParam[4,ht,0] = p0
664 664 DGauFitParam[4,ht,1] = p1
665 665
666 666 # print (DGauFitParam.shape)
667 667 # print ('Leaving FitGau')
668 668 return DGauFitParam
669 669 # return SPCparam
670 670 # return GauSPC
671 671
672 672 def y_model1(self,x,state):
673 673 shift0, width0, amplitude0, power0, noise = state
674 674 model0 = amplitude0*numpy.exp(-0.5*abs((x - shift0)/width0)**power0)
675 675 model0u = amplitude0*numpy.exp(-0.5*abs((x - shift0 - self.Num_Bin)/width0)**power0)
676 676 model0d = amplitude0*numpy.exp(-0.5*abs((x - shift0 + self.Num_Bin)/width0)**power0)
677 677 return model0 + model0u + model0d + noise
678 678
679 679 def y_model2(self,x,state): #Equation for two generalized Gaussians with Nyquist
680 680 shift0, width0, amplitude0, power0, shift1, width1, amplitude1, power1, noise = state
681 681 model0 = amplitude0*numpy.exp(-0.5*abs((x-shift0)/width0)**power0)
682 682 model0u = amplitude0*numpy.exp(-0.5*abs((x - shift0 - self.Num_Bin)/width0)**power0)
683 683 model0d = amplitude0*numpy.exp(-0.5*abs((x - shift0 + self.Num_Bin)/width0)**power0)
684 684
685 685 model1 = amplitude1*numpy.exp(-0.5*abs((x - shift1)/width1)**power1)
686 686 model1u = amplitude1*numpy.exp(-0.5*abs((x - shift1 - self.Num_Bin)/width1)**power1)
687 687 model1d = amplitude1*numpy.exp(-0.5*abs((x - shift1 + self.Num_Bin)/width1)**power1)
688 688 return model0 + model0u + model0d + model1 + model1u + model1d + noise
689 689
690 690 def misfit1(self,state,y_data,x,num_intg): # This function compares how close real data is with the model data, the close it is, the better it is.
691 691
692 692 return num_intg*sum((numpy.log(y_data)-numpy.log(self.y_model1(x,state)))**2)#/(64-5.) # /(64-5.) can be commented
693 693
694 694 def misfit2(self,state,y_data,x,num_intg):
695 695 return num_intg*sum((numpy.log(y_data)-numpy.log(self.y_model2(x,state)))**2)#/(64-9.)
696 696
697 697
698 698
699 699 class PrecipitationProc(Operation):
700 700
701 701 '''
702 702 Operator that estimates Reflectivity factor (Z), and estimates rainfall Rate (R)
703 703
704 704 Input:
705 705 self.dataOut.data_pre : SelfSpectra
706 706
707 707 Output:
708 708
709 709 self.dataOut.data_output : Reflectivity factor, rainfall Rate
710 710
711 711
712 712 Parameters affected:
713 713 '''
714 714
715 715 def __init__(self):
716 716 Operation.__init__(self)
717 717 self.i=0
718 718
719 719 def run(self, dataOut, radar=None, Pt=5000, Gt=295.1209, Gr=70.7945, Lambda=0.6741, aL=2.5118,
720 720 tauW=4e-06, ThetaT=0.1656317, ThetaR=0.36774087, Km2 = 0.93, Altitude=3350,SNRdBlimit=-30):
721 721
722 722 # print ('Entering PrecepitationProc ... ')
723 723
724 724 if radar == "MIRA35C" :
725 725
726 726 self.spc = dataOut.data_pre[0].copy()
727 727 self.Num_Hei = self.spc.shape[2]
728 728 self.Num_Bin = self.spc.shape[1]
729 729 self.Num_Chn = self.spc.shape[0]
730 730 Ze = self.dBZeMODE2(dataOut)
731 731
732 732 else:
733 733
734 734 self.spc = dataOut.data_pre[0].copy()
735 735
736 736 #NOTA SE DEBE REMOVER EL RANGO DEL PULSO TX
737 737 self.spc[:,:,0:7]= numpy.NaN
738 738
739 739 self.Num_Hei = self.spc.shape[2]
740 740 self.Num_Bin = self.spc.shape[1]
741 741 self.Num_Chn = self.spc.shape[0]
742 742
743 743 VelRange = dataOut.spc_range[2]
744 744
745 745 ''' Se obtiene la constante del RADAR '''
746 746
747 747 self.Pt = Pt
748 748 self.Gt = Gt
749 749 self.Gr = Gr
750 750 self.Lambda = Lambda
751 751 self.aL = aL
752 752 self.tauW = tauW
753 753 self.ThetaT = ThetaT
754 754 self.ThetaR = ThetaR
755 755 self.GSys = 10**(36.63/10) # Ganancia de los LNA 36.63 dB
756 756 self.lt = 10**(1.67/10) # Perdida en cables Tx 1.67 dB
757 757 self.lr = 10**(5.73/10) # Perdida en cables Rx 5.73 dB
758 758
759 759 Numerator = ( (4*numpy.pi)**3 * aL**2 * 16 * numpy.log(2) )
760 760 Denominator = ( Pt * Gt * Gr * Lambda**2 * SPEED_OF_LIGHT * tauW * numpy.pi * ThetaT * ThetaR)
761 761 RadarConstant = 10e-26 * Numerator / Denominator #
762 762 ExpConstant = 10**(40/10) #Constante Experimental
763 763
764 764 SignalPower = numpy.zeros([self.Num_Chn,self.Num_Bin,self.Num_Hei])
765 765 for i in range(self.Num_Chn):
766 766 SignalPower[i,:,:] = self.spc[i,:,:] - dataOut.noise[i]
767 767 SignalPower[numpy.where(SignalPower < 0)] = 1e-20
768 768
769 769 SPCmean = numpy.mean(SignalPower, 0)
770 770 Pr = SPCmean[:,:]/dataOut.normFactor
771 771
772 772 # Declaring auxiliary variables
773 773 Range = dataOut.heightList*1000. #Range in m
774 774 # replicate the heightlist to obtain a matrix [Num_Bin,Num_Hei]
775 775 rMtrx = numpy.transpose(numpy.transpose([dataOut.heightList*1000.] * self.Num_Bin))
776 776 zMtrx = rMtrx+Altitude
777 777 # replicate the VelRange to obtain a matrix [Num_Bin,Num_Hei]
778 778 VelMtrx = numpy.transpose(numpy.tile(VelRange[:-1], (self.Num_Hei,1)))
779 779
780 780 # height dependence to air density Foote and Du Toit (1969)
781 781 delv_z = 1 + 3.68e-5 * zMtrx + 1.71e-9 * zMtrx**2
782 782 VMtrx = VelMtrx / delv_z #Normalized velocity
783 783 VMtrx[numpy.where(VMtrx> 9.6)] = numpy.NaN
784 784 # Diameter is related to the fall speed of falling drops
785 785 D_Vz = -1.667 * numpy.log( 0.9369 - 0.097087 * VMtrx ) # D in [mm]
786 786 # Only valid for D>= 0.16 mm
787 787 D_Vz[numpy.where(D_Vz < 0.16)] = numpy.NaN
788 788
789 789 #Calculate Radar Reflectivity ETAn
790 790 ETAn = (RadarConstant *ExpConstant) * Pr * rMtrx**2 #Reflectivity (ETA)
791 791 ETAd = ETAn * 6.18 * exp( -0.6 * D_Vz ) * delv_z
792 792 # Radar Cross Section
793 793 sigmaD = Km2 * (D_Vz * 1e-3 )**6 * numpy.pi**5 / Lambda**4
794 794 # Drop Size Distribution
795 795 DSD = ETAn / sigmaD
796 796 # Equivalente Reflectivy
797 797 Ze_eqn = numpy.nansum( DSD * D_Vz**6 ,axis=0)
798 798 Ze_org = numpy.nansum(ETAn * Lambda**4, axis=0) / (1e-18*numpy.pi**5 * Km2) # [mm^6 /m^3]
799 799 # RainFall Rate
800 800 RR = 0.0006*numpy.pi * numpy.nansum( D_Vz**3 * DSD * VelMtrx ,0) #mm/hr
801 801
802 802 # Censoring the data
803 803 # Removing data with SNRth < 0dB se debe considerar el SNR por canal
804 804 SNRth = 10**(SNRdBlimit/10) #-30dB
805 805 novalid = numpy.where((dataOut.data_snr[0,:] <SNRth) | (dataOut.data_snr[1,:] <SNRth) | (dataOut.data_snr[2,:] <SNRth)) # AND condition. Maybe OR condition better
806 806 W = numpy.nanmean(dataOut.data_dop,0)
807 807 W[novalid] = numpy.NaN
808 808 Ze_org[novalid] = numpy.NaN
809 809 RR[novalid] = numpy.NaN
810 810
811 811 dataOut.data_output = RR[8]
812 812 dataOut.data_param = numpy.ones([3,self.Num_Hei])
813 813 dataOut.channelList = [0,1,2]
814 814
815 815 dataOut.data_param[0]=10*numpy.log10(Ze_org)
816 816 dataOut.data_param[1]=-W
817 817 dataOut.data_param[2]=RR
818 818
819 819 # print ('Leaving PrecepitationProc ... ')
820 820 return dataOut
821 821
822 822 def dBZeMODE2(self, dataOut): # Processing for MIRA35C
823 823
824 824 NPW = dataOut.NPW
825 825 COFA = dataOut.COFA
826 826
827 827 SNR = numpy.array([self.spc[0,:,:] / NPW[0]]) #, self.spc[1,:,:] / NPW[1]])
828 828 RadarConst = dataOut.RadarConst
829 829 #frequency = 34.85*10**9
830 830
831 831 ETA = numpy.zeros(([self.Num_Chn ,self.Num_Hei]))
832 832 data_output = numpy.ones([self.Num_Chn , self.Num_Hei])*numpy.NaN
833 833
834 834 ETA = numpy.sum(SNR,1)
835 835
836 836 ETA = numpy.where(ETA != 0. , ETA, numpy.NaN)
837 837
838 838 Ze = numpy.ones([self.Num_Chn, self.Num_Hei] )
839 839
840 840 for r in range(self.Num_Hei):
841 841
842 842 Ze[0,r] = ( ETA[0,r] ) * COFA[0,r][0] * RadarConst * ((r/5000.)**2)
843 843 #Ze[1,r] = ( ETA[1,r] ) * COFA[1,r][0] * RadarConst * ((r/5000.)**2)
844 844
845 845 return Ze
846 846
847 847 # def GetRadarConstant(self):
848 848 #
849 849 # """
850 850 # Constants:
851 851 #
852 852 # Pt: Transmission Power dB 5kW 5000
853 853 # Gt: Transmission Gain dB 24.7 dB 295.1209
854 854 # Gr: Reception Gain dB 18.5 dB 70.7945
855 855 # Lambda: Wavelenght m 0.6741 m 0.6741
856 856 # aL: Attenuation loses dB 4dB 2.5118
857 857 # tauW: Width of transmission pulse s 4us 4e-6
858 858 # ThetaT: Transmission antenna bean angle rad 0.1656317 rad 0.1656317
859 859 # ThetaR: Reception antenna beam angle rad 0.36774087 rad 0.36774087
860 860 #
861 861 # """
862 862 #
863 863 # Numerator = ( (4*numpy.pi)**3 * aL**2 * 16 * numpy.log(2) )
864 864 # Denominator = ( Pt * Gt * Gr * Lambda**2 * SPEED_OF_LIGHT * TauW * numpy.pi * ThetaT * TheraR)
865 865 # RadarConstant = Numerator / Denominator
866 866 #
867 867 # return RadarConstant
868 868
869 869
870 870
871 871 class FullSpectralAnalysis(Operation):
872 872
873 873 """
874 874 Function that implements Full Spectral Analysis technique.
875 875
876 876 Input:
877 877 self.dataOut.data_pre : SelfSpectra and CrossSpectra data
878 878 self.dataOut.groupList : Pairlist of channels
879 879 self.dataOut.ChanDist : Physical distance between receivers
880 880
881 881
882 882 Output:
883 883
884 884 self.dataOut.data_output : Zonal wind, Meridional wind, and Vertical wind
885 885
886 886
887 887 Parameters affected: Winds, height range, SNR
888 888
889 889 """
890 890 def run(self, dataOut, Xi01=None, Xi02=None, Xi12=None, Eta01=None, Eta02=None, Eta12=None, SNRdBlimit=-30,
891 891 minheight=None, maxheight=None, NegativeLimit=None, PositiveLimit=None):
892 892
893 893 spc = dataOut.data_pre[0].copy()
894 894 cspc = dataOut.data_pre[1]
895 895 nHeights = spc.shape[2]
896 896
897 897 # first_height = 0.75 #km (ref: data header 20170822)
898 898 # resolution_height = 0.075 #km
899 899 '''
900 900 finding height range. check this when radar parameters are changed!
901 901 '''
902 902 if maxheight is not None:
903 903 # range_max = math.ceil((maxheight - first_height) / resolution_height) # theoretical
904 904 range_max = math.ceil(13.26 * maxheight - 3) # empirical, works better
905 905 else:
906 906 range_max = nHeights
907 907 if minheight is not None:
908 908 # range_min = int((minheight - first_height) / resolution_height) # theoretical
909 909 range_min = int(13.26 * minheight - 5) # empirical, works better
910 910 if range_min < 0:
911 911 range_min = 0
912 912 else:
913 913 range_min = 0
914 914
915 915 pairsList = dataOut.groupList
916 916 if dataOut.ChanDist is not None :
917 917 ChanDist = dataOut.ChanDist
918 918 else:
919 919 ChanDist = numpy.array([[Xi01, Eta01],[Xi02,Eta02],[Xi12,Eta12]])
920 920
921 921 # 4 variables: zonal, meridional, vertical, and average SNR
922 922 data_param = numpy.zeros([4,nHeights]) * numpy.NaN
923 923 velocityX = numpy.zeros([nHeights]) * numpy.NaN
924 924 velocityY = numpy.zeros([nHeights]) * numpy.NaN
925 925 velocityZ = numpy.zeros([nHeights]) * numpy.NaN
926 926
927 927 dbSNR = 10*numpy.log10(numpy.average(dataOut.data_snr,0))
928 928
929 929 '''***********************************************WIND ESTIMATION**************************************'''
930 930 for Height in range(nHeights):
931 931
932 932 if Height >= range_min and Height < range_max:
933 933 # error_code will be useful in future analysis
934 934 [Vzon,Vmer,Vver, error_code] = self.WindEstimation(spc[:,:,Height], cspc[:,:,Height], pairsList,
935 935 ChanDist, Height, dataOut.noise, dataOut.spc_range, dbSNR[Height], SNRdBlimit, NegativeLimit, PositiveLimit,dataOut.frequency)
936 936
937 937 if abs(Vzon) < 100. and abs(Vmer) < 100.:
938 938 velocityX[Height] = Vzon
939 939 velocityY[Height] = -Vmer
940 940 velocityZ[Height] = Vver
941 941
942 942 # Censoring data with SNR threshold
943 943 dbSNR [dbSNR < SNRdBlimit] = numpy.NaN
944 944
945 945 data_param[0] = velocityX
946 946 data_param[1] = velocityY
947 947 data_param[2] = velocityZ
948 948 data_param[3] = dbSNR
949 949 dataOut.data_param = data_param
950 950 return dataOut
951 951
952 952 def moving_average(self,x, N=2):
953 953 """ convolution for smoothenig data. note that last N-1 values are convolution with zeroes """
954 954 return numpy.convolve(x, numpy.ones((N,))/N)[(N-1):]
955 955
956 956 def gaus(self,xSamples,Amp,Mu,Sigma):
957 957 return Amp * numpy.exp(-0.5*((xSamples - Mu)/Sigma)**2)
958 958
959 959 def Moments(self, ySamples, xSamples):
960 960 Power = numpy.nanmean(ySamples) # Power, 0th Moment
961 961 yNorm = ySamples / numpy.nansum(ySamples)
962 962 RadVel = numpy.nansum(xSamples * yNorm) # Radial Velocity, 1st Moment
963 963 Sigma2 = numpy.nansum(yNorm * (xSamples - RadVel)**2) # Spectral Width, 2nd Moment
964 964 StdDev = numpy.sqrt(numpy.abs(Sigma2)) # Desv. Estandar, Ancho espectral
965 965 return numpy.array([Power,RadVel,StdDev])
966 966
967 967 def StopWindEstimation(self, error_code):
968 968 Vzon = numpy.NaN
969 969 Vmer = numpy.NaN
970 970 Vver = numpy.NaN
971 971 return Vzon, Vmer, Vver, error_code
972 972
973 973 def AntiAliasing(self, interval, maxstep):
974 974 """
975 975 function to prevent errors from aliased values when computing phaseslope
976 976 """
977 977 antialiased = numpy.zeros(len(interval))
978 978 copyinterval = interval.copy()
979 979
980 980 antialiased[0] = copyinterval[0]
981 981
982 982 for i in range(1,len(antialiased)):
983 983 step = interval[i] - interval[i-1]
984 984 if step > maxstep:
985 985 copyinterval -= 2*numpy.pi
986 986 antialiased[i] = copyinterval[i]
987 987 elif step < maxstep*(-1):
988 988 copyinterval += 2*numpy.pi
989 989 antialiased[i] = copyinterval[i]
990 990 else:
991 991 antialiased[i] = copyinterval[i].copy()
992 992
993 993 return antialiased
994 994
995 995 def WindEstimation(self, spc, cspc, pairsList, ChanDist, Height, noise, AbbsisaRange, dbSNR, SNRlimit, NegativeLimit, PositiveLimit, radfreq):
996 996 """
997 997 Function that Calculates Zonal, Meridional and Vertical wind velocities.
998 998 Initial Version by E. Bocanegra updated by J. Zibell until Nov. 2019.
999 999
1000 1000 Input:
1001 1001 spc, cspc : self spectra and cross spectra data. In Briggs notation something like S_i*(S_i)_conj, (S_j)_conj respectively.
1002 1002 pairsList : Pairlist of channels
1003 1003 ChanDist : array of xi_ij and eta_ij
1004 1004 Height : height at which data is processed
1005 1005 noise : noise in [channels] format for specific height
1006 1006 Abbsisarange : range of the frequencies or velocities
1007 1007 dbSNR, SNRlimit : signal to noise ratio in db, lower limit
1008 1008
1009 1009 Output:
1010 1010 Vzon, Vmer, Vver : wind velocities
1011 1011 error_code : int that states where code is terminated
1012 1012
1013 1013 0 : no error detected
1014 1014 1 : Gaussian of mean spc exceeds widthlimit
1015 1015 2 : no Gaussian of mean spc found
1016 1016 3 : SNR to low or velocity to high -> prec. e.g.
1017 1017 4 : at least one Gaussian of cspc exceeds widthlimit
1018 1018 5 : zero out of three cspc Gaussian fits converged
1019 1019 6 : phase slope fit could not be found
1020 1020 7 : arrays used to fit phase have different length
1021 1021 8 : frequency range is either too short (len <= 5) or very long (> 30% of cspc)
1022 1022
1023 1023 """
1024 1024
1025 1025 error_code = 0
1026 1026
1027 1027 nChan = spc.shape[0]
1028 1028 nProf = spc.shape[1]
1029 1029 nPair = cspc.shape[0]
1030 1030
1031 1031 SPC_Samples = numpy.zeros([nChan, nProf]) # for normalized spc values for one height
1032 1032 CSPC_Samples = numpy.zeros([nPair, nProf], dtype=numpy.complex_) # for normalized cspc values
1033 1033 phase = numpy.zeros([nPair, nProf]) # phase between channels
1034 1034 PhaseSlope = numpy.zeros(nPair) # slope of the phases, channelwise
1035 1035 PhaseInter = numpy.zeros(nPair) # intercept to the slope of the phases, channelwise
1036 1036 xFrec = AbbsisaRange[0][:-1] # frequency range
1037 1037 xVel = AbbsisaRange[2][:-1] # velocity range
1038 1038 xSamples = xFrec # the frequency range is taken
1039 1039 delta_x = xSamples[1] - xSamples[0] # delta_f or delta_x
1040 1040
1041 1041 # only consider velocities with in NegativeLimit and PositiveLimit
1042 1042 if (NegativeLimit is None):
1043 1043 NegativeLimit = numpy.min(xVel)
1044 1044 if (PositiveLimit is None):
1045 1045 PositiveLimit = numpy.max(xVel)
1046 1046 xvalid = numpy.where((xVel > NegativeLimit) & (xVel < PositiveLimit))
1047 1047 xSamples_zoom = xSamples[xvalid]
1048 1048
1049 1049 '''Getting Eij and Nij'''
1050 1050 Xi01, Xi02, Xi12 = ChanDist[:,0]
1051 1051 Eta01, Eta02, Eta12 = ChanDist[:,1]
1052 1052
1053 1053 # spwd limit - updated by D. ScipiΓ³n 30.03.2021
1054 1054 widthlimit = 10
1055 1055 '''************************* SPC is normalized ********************************'''
1056 1056 spc_norm = spc.copy()
1057 1057 # For each channel
1058 1058 for i in range(nChan):
1059 1059 spc_sub = spc_norm[i,:] - noise[i] # only the signal power
1060 1060 SPC_Samples[i] = spc_sub / (numpy.nansum(spc_sub) * delta_x)
1061 1061
1062 1062 '''********************** FITTING MEAN SPC GAUSSIAN **********************'''
1063 1063
1064 1064 """ the gaussian of the mean: first subtract noise, then normalize. this is legal because
1065 1065 you only fit the curve and don't need the absolute value of height for calculation,
1066 1066 only for estimation of width. for normalization of cross spectra, you need initial,
1067 1067 unnormalized self-spectra With noise.
1068 1068
1069 1069 Technically, you don't even need to normalize the self-spectra, as you only need the
1070 1070 width of the peak. However, it was left this way. Note that the normalization has a flaw:
1071 1071 due to subtraction of the noise, some values are below zero. Raw "spc" values should be
1072 1072 >= 0, as it is the modulus squared of the signals (complex * it's conjugate)
1073 1073 """
1074 1074 # initial conditions
1075 1075 popt = [1e-10,0,1e-10]
1076 1076 # Spectra average
1077 1077 SPCMean = numpy.average(SPC_Samples,0)
1078 1078 # Moments in frequency
1079 1079 SPCMoments = self.Moments(SPCMean[xvalid], xSamples_zoom)
1080 1080
1081 1081 # Gauss Fit SPC in frequency domain
1082 1082 if dbSNR > SNRlimit: # only if SNR > SNRth
1083 1083 try:
1084 1084 popt,pcov = curve_fit(self.gaus,xSamples_zoom,SPCMean[xvalid],p0=SPCMoments)
1085 1085 if popt[2] <= 0 or popt[2] > widthlimit: # CONDITION
1086 1086 return self.StopWindEstimation(error_code = 1)
1087 1087 FitGauss = self.gaus(xSamples_zoom,*popt)
1088 1088 except :#RuntimeError:
1089 1089 return self.StopWindEstimation(error_code = 2)
1090 1090 else:
1091 1091 return self.StopWindEstimation(error_code = 3)
1092 1092
1093 1093 '''***************************** CSPC Normalization *************************
1094 1094 The Spc spectra are used to normalize the crossspectra. Peaks from precipitation
1095 1095 influence the norm which is not desired. First, a range is identified where the
1096 1096 wind peak is estimated -> sum_wind is sum of those frequencies. Next, the area
1097 1097 around it gets cut off and values replaced by mean determined by the boundary
1098 1098 data -> sum_noise (spc is not normalized here, thats why the noise is important)
1099 1099
1100 1100 The sums are then added and multiplied by range/datapoints, because you need
1101 1101 an integral and not a sum for normalization.
1102 1102
1103 1103 A norm is found according to Briggs 92.
1104 1104 '''
1105 1105 # for each pair
1106 1106 for i in range(nPair):
1107 1107 cspc_norm = cspc[i,:].copy()
1108 1108 chan_index0 = pairsList[i][0]
1109 1109 chan_index1 = pairsList[i][1]
1110 1110 CSPC_Samples[i] = cspc_norm / (numpy.sqrt(numpy.nansum(spc_norm[chan_index0])*numpy.nansum(spc_norm[chan_index1])) * delta_x)
1111 1111 phase[i] = numpy.arctan2(CSPC_Samples[i].imag, CSPC_Samples[i].real)
1112 1112
1113 1113 CSPCmoments = numpy.vstack([self.Moments(numpy.abs(CSPC_Samples[0,xvalid]), xSamples_zoom),
1114 1114 self.Moments(numpy.abs(CSPC_Samples[1,xvalid]), xSamples_zoom),
1115 1115 self.Moments(numpy.abs(CSPC_Samples[2,xvalid]), xSamples_zoom)])
1116 1116
1117 1117 popt01, popt02, popt12 = [1e-10,0,1e-10], [1e-10,0,1e-10] ,[1e-10,0,1e-10]
1118 1118 FitGauss01, FitGauss02, FitGauss12 = numpy.zeros(len(xSamples)), numpy.zeros(len(xSamples)), numpy.zeros(len(xSamples))
1119 1119
1120 1120 '''*******************************FIT GAUSS CSPC************************************'''
1121 1121 try:
1122 1122 popt01,pcov = curve_fit(self.gaus,xSamples_zoom,numpy.abs(CSPC_Samples[0][xvalid]),p0=CSPCmoments[0])
1123 1123 if popt01[2] > widthlimit: # CONDITION
1124 1124 return self.StopWindEstimation(error_code = 4)
1125 1125 popt02,pcov = curve_fit(self.gaus,xSamples_zoom,numpy.abs(CSPC_Samples[1][xvalid]),p0=CSPCmoments[1])
1126 1126 if popt02[2] > widthlimit: # CONDITION
1127 1127 return self.StopWindEstimation(error_code = 4)
1128 1128 popt12,pcov = curve_fit(self.gaus,xSamples_zoom,numpy.abs(CSPC_Samples[2][xvalid]),p0=CSPCmoments[2])
1129 1129 if popt12[2] > widthlimit: # CONDITION
1130 1130 return self.StopWindEstimation(error_code = 4)
1131 1131
1132 1132 FitGauss01 = self.gaus(xSamples_zoom, *popt01)
1133 1133 FitGauss02 = self.gaus(xSamples_zoom, *popt02)
1134 1134 FitGauss12 = self.gaus(xSamples_zoom, *popt12)
1135 1135 except:
1136 1136 return self.StopWindEstimation(error_code = 5)
1137 1137
1138 1138
1139 1139 '''************* Getting Fij ***************'''
1140 1140 # x-axis point of the gaussian where the center is located from GaussFit of spectra
1141 1141 GaussCenter = popt[1]
1142 1142 ClosestCenter = xSamples_zoom[numpy.abs(xSamples_zoom-GaussCenter).argmin()]
1143 1143 PointGauCenter = numpy.where(xSamples_zoom==ClosestCenter)[0][0]
1144 1144
1145 1145 # Point where e^-1 is located in the gaussian
1146 1146 PeMinus1 = numpy.max(FitGauss) * numpy.exp(-1)
1147 1147 FijClosest = FitGauss[numpy.abs(FitGauss-PeMinus1).argmin()] # The closest point to"Peminus1" in "FitGauss"
1148 1148 PointFij = numpy.where(FitGauss==FijClosest)[0][0]
1149 1149 Fij = numpy.abs(xSamples_zoom[PointFij] - xSamples_zoom[PointGauCenter])
1150 1150
1151 1151 '''********** Taking frequency ranges from mean SPCs **********'''
1152 1152 GauWidth = popt[2] * 3/2 # Bandwidth of Gau01
1153 1153 Range = numpy.empty(2)
1154 1154 Range[0] = GaussCenter - GauWidth
1155 1155 Range[1] = GaussCenter + GauWidth
1156 1156 # Point in x-axis where the bandwidth is located (min:max)
1157 1157 ClosRangeMin = xSamples_zoom[numpy.abs(xSamples_zoom-Range[0]).argmin()]
1158 1158 ClosRangeMax = xSamples_zoom[numpy.abs(xSamples_zoom-Range[1]).argmin()]
1159 1159 PointRangeMin = numpy.where(xSamples_zoom==ClosRangeMin)[0][0]
1160 1160 PointRangeMax = numpy.where(xSamples_zoom==ClosRangeMax)[0][0]
1161 1161 Range = numpy.array([ PointRangeMin, PointRangeMax ])
1162 1162 FrecRange = xSamples_zoom[ Range[0] : Range[1] ]
1163 1163
1164 1164 '''************************** Getting Phase Slope ***************************'''
1165 1165 for i in range(nPair):
1166 1166 if len(FrecRange) > 5:
1167 1167 PhaseRange = phase[i, xvalid[0][Range[0]:Range[1]]].copy()
1168 1168 mask = ~numpy.isnan(FrecRange) & ~numpy.isnan(PhaseRange)
1169 1169 if len(FrecRange) == len(PhaseRange):
1170 1170 try:
1171 1171 slope, intercept, _, _, _ = stats.linregress(FrecRange[mask], self.AntiAliasing(PhaseRange[mask], 4.5))
1172 1172 PhaseSlope[i] = slope
1173 1173 PhaseInter[i] = intercept
1174 1174 except:
1175 1175 return self.StopWindEstimation(error_code = 6)
1176 1176 else:
1177 1177 return self.StopWindEstimation(error_code = 7)
1178 1178 else:
1179 1179 return self.StopWindEstimation(error_code = 8)
1180 1180
1181 1181 '''*** Constants A-H correspond to the convention as in Briggs and Vincent 1992 ***'''
1182 1182
1183 1183 '''Getting constant C'''
1184 1184 cC=(Fij*numpy.pi)**2
1185 1185
1186 1186 '''****** Getting constants F and G ******'''
1187 1187 MijEijNij = numpy.array([[Xi02,Eta02], [Xi12,Eta12]])
1188 1188 # MijEijNij = numpy.array([[Xi01,Eta01], [Xi02,Eta02], [Xi12,Eta12]])
1189 1189 # MijResult0 = (-PhaseSlope[0] * cC) / (2*numpy.pi)
1190 1190 MijResult1 = (-PhaseSlope[1] * cC) / (2*numpy.pi)
1191 1191 MijResult2 = (-PhaseSlope[2] * cC) / (2*numpy.pi)
1192 1192 # MijResults = numpy.array([MijResult0, MijResult1, MijResult2])
1193 1193 MijResults = numpy.array([MijResult1, MijResult2])
1194 1194 (cF,cG) = numpy.linalg.solve(MijEijNij, MijResults)
1195 1195
1196 1196 '''****** Getting constants A, B and H ******'''
1197 1197 W01 = numpy.nanmax( FitGauss01 )
1198 1198 W02 = numpy.nanmax( FitGauss02 )
1199 1199 W12 = numpy.nanmax( FitGauss12 )
1200 1200
1201 1201 WijResult01 = ((cF * Xi01 + cG * Eta01)**2)/cC - numpy.log(W01 / numpy.sqrt(numpy.pi / cC))
1202 1202 WijResult02 = ((cF * Xi02 + cG * Eta02)**2)/cC - numpy.log(W02 / numpy.sqrt(numpy.pi / cC))
1203 1203 WijResult12 = ((cF * Xi12 + cG * Eta12)**2)/cC - numpy.log(W12 / numpy.sqrt(numpy.pi / cC))
1204 1204 WijResults = numpy.array([WijResult01, WijResult02, WijResult12])
1205 1205
1206 1206 WijEijNij = numpy.array([ [Xi01**2, Eta01**2, 2*Xi01*Eta01] , [Xi02**2, Eta02**2, 2*Xi02*Eta02] , [Xi12**2, Eta12**2, 2*Xi12*Eta12] ])
1207 1207 (cA,cB,cH) = numpy.linalg.solve(WijEijNij, WijResults)
1208 1208
1209 1209 VxVy = numpy.array([[cA,cH],[cH,cB]])
1210 1210 VxVyResults = numpy.array([-cF,-cG])
1211 1211 (Vmer,Vzon) = numpy.linalg.solve(VxVy, VxVyResults)
1212 1212 Vver = -SPCMoments[1]*SPEED_OF_LIGHT/(2*radfreq)
1213 1213 error_code = 0
1214 1214
1215 1215 return Vzon, Vmer, Vver, error_code
1216 1216
1217 1217 class SpectralMoments(Operation):
1218 1218
1219 1219 '''
1220 1220 Function SpectralMoments()
1221 1221
1222 1222 Calculates moments (power, mean, standard deviation) and SNR of the signal
1223 1223
1224 1224 Type of dataIn: Spectra
1225 1225
1226 1226 Configuration Parameters:
1227 1227
1228 1228 dirCosx : Cosine director in X axis
1229 1229 dirCosy : Cosine director in Y axis
1230 1230
1231 1231 elevation :
1232 1232 azimuth :
1233 1233
1234 1234 Input:
1235 1235 channelList : simple channel list to select e.g. [2,3,7]
1236 1236 self.dataOut.data_pre : Spectral data
1237 1237 self.dataOut.abscissaList : List of frequencies
1238 1238 self.dataOut.noise : Noise level per channel
1239 1239
1240 1240 Affected:
1241 1241 self.dataOut.moments : Parameters per channel
1242 1242 self.dataOut.data_snr : SNR per channel
1243 1243
1244 1244 '''
1245 1245
1246 1246 def run(self, dataOut):
1247 1247
1248 1248 data = dataOut.data_pre[0]
1249 1249 absc = dataOut.abscissaList[:-1]
1250 1250 noise = dataOut.noise
1251 1251 nChannel = data.shape[0]
1252 1252 data_param = numpy.zeros((nChannel, 4, data.shape[2]))
1253 1253
1254 1254 for ind in range(nChannel):
1255 1255 data_param[ind,:,:] = self.__calculateMoments( data[ind,:,:] , absc , noise[ind] )
1256 1256
1257 1257 dataOut.moments = data_param[:,1:,:]
1258 1258 dataOut.data_snr = data_param[:,0]
1259 1259 dataOut.data_pow = data_param[:,1]
1260 1260 dataOut.data_dop = data_param[:,2]
1261 1261 dataOut.data_width = data_param[:,3]
1262 1262 return dataOut
1263 1263
1264 1264 def __calculateMoments(self, oldspec, oldfreq, n0,
1265 1265 nicoh = None, graph = None, smooth = None, type1 = None, fwindow = None, snrth = None, dc = None, aliasing = None, oldfd = None, wwauto = None):
1266 1266
1267 1267 if (nicoh is None): nicoh = 1
1268 1268 if (graph is None): graph = 0
1269 1269 if (smooth is None): smooth = 0
1270 1270 elif (self.smooth < 3): smooth = 0
1271 1271
1272 1272 if (type1 is None): type1 = 0
1273 1273 if (fwindow is None): fwindow = numpy.zeros(oldfreq.size) + 1
1274 1274 if (snrth is None): snrth = -3
1275 1275 if (dc is None): dc = 0
1276 1276 if (aliasing is None): aliasing = 0
1277 1277 if (oldfd is None): oldfd = 0
1278 1278 if (wwauto is None): wwauto = 0
1279 1279
1280 1280 if (n0 < 1.e-20): n0 = 1.e-20
1281 1281
1282 1282 freq = oldfreq
1283 1283 vec_power = numpy.zeros(oldspec.shape[1])
1284 1284 vec_fd = numpy.zeros(oldspec.shape[1])
1285 1285 vec_w = numpy.zeros(oldspec.shape[1])
1286 1286 vec_snr = numpy.zeros(oldspec.shape[1])
1287 1287
1288 1288 # oldspec = numpy.ma.masked_invalid(oldspec)
1289
1290 1289 for ind in range(oldspec.shape[1]):
1291 1290
1292 1291 spec = oldspec[:,ind]
1293 1292 aux = spec*fwindow
1294 1293 max_spec = aux.max()
1295 1294 m = aux.tolist().index(max_spec)
1296 1295
1297 1296 # Smooth
1298 1297 if (smooth == 0):
1299 1298 spec2 = spec
1300 1299 else:
1301 1300 spec2 = scipy.ndimage.filters.uniform_filter1d(spec,size=smooth)
1302 1301
1303 1302 # Moments Estimation
1304 1303 bb = spec2[numpy.arange(m,spec2.size)]
1305 1304 bb = (bb<n0).nonzero()
1306 1305 bb = bb[0]
1307 1306
1308 1307 ss = spec2[numpy.arange(0,m + 1)]
1309 1308 ss = (ss<n0).nonzero()
1310 1309 ss = ss[0]
1311 1310
1312 1311 if (bb.size == 0):
1313 1312 bb0 = spec.size - 1 - m
1314 1313 else:
1315 1314 bb0 = bb[0] - 1
1316 1315 if (bb0 < 0):
1317 1316 bb0 = 0
1318 1317
1319 1318 if (ss.size == 0):
1320 1319 ss1 = 1
1321 1320 else:
1322 1321 ss1 = max(ss) + 1
1323 1322
1324 1323 if (ss1 > m):
1325 1324 ss1 = m
1326 1325
1327 1326 valid = numpy.arange(int(m + bb0 - ss1 + 1)) + ss1
1328
1327 #valid = numpy.arange(1,oldspec.shape[0])# valid perfil completo igual pulsepair
1329 1328 signal_power = ((spec2[valid] - n0) * fwindow[valid]).mean() # D. ScipiΓ³n added with correct definition
1330 1329 total_power = (spec2[valid] * fwindow[valid]).mean() # D. ScipiΓ³n added with correct definition
1331 1330 power = ((spec2[valid] - n0) * fwindow[valid]).sum()
1332 1331 fd = ((spec2[valid]- n0)*freq[valid] * fwindow[valid]).sum() / power
1333 1332 w = numpy.sqrt(((spec2[valid] - n0)*fwindow[valid]*(freq[valid]- fd)**2).sum() / power)
1334 1333 snr = (spec2.mean()-n0)/n0
1335 1334 if (snr < 1.e-20) :
1336 1335 snr = 1.e-20
1337 1336
1338 1337 # vec_power[ind] = power #D. ScipiΓ³n replaced with the line below
1339 1338 vec_power[ind] = total_power
1340 1339 vec_fd[ind] = fd
1341 1340 vec_w[ind] = w
1342 1341 vec_snr[ind] = snr
1343 1342
1344 1343 return numpy.vstack((vec_snr, vec_power, vec_fd, vec_w))
1345 1344
1346 1345 #------------------ Get SA Parameters --------------------------
1347 1346
1348 1347 def GetSAParameters(self):
1349 1348 #SA en frecuencia
1350 1349 pairslist = self.dataOut.groupList
1351 1350 num_pairs = len(pairslist)
1352 1351
1353 1352 vel = self.dataOut.abscissaList
1354 1353 spectra = self.dataOut.data_pre
1355 1354 cspectra = self.dataIn.data_cspc
1356 1355 delta_v = vel[1] - vel[0]
1357 1356
1358 1357 #Calculating the power spectrum
1359 1358 spc_pow = numpy.sum(spectra, 3)*delta_v
1360 1359 #Normalizing Spectra
1361 1360 norm_spectra = spectra/spc_pow
1362 1361 #Calculating the norm_spectra at peak
1363 1362 max_spectra = numpy.max(norm_spectra, 3)
1364 1363
1365 1364 #Normalizing Cross Spectra
1366 1365 norm_cspectra = numpy.zeros(cspectra.shape)
1367 1366
1368 1367 for i in range(num_chan):
1369 1368 norm_cspectra[i,:,:] = cspectra[i,:,:]/numpy.sqrt(spc_pow[pairslist[i][0],:]*spc_pow[pairslist[i][1],:])
1370 1369
1371 1370 max_cspectra = numpy.max(norm_cspectra,2)
1372 1371 max_cspectra_index = numpy.argmax(norm_cspectra, 2)
1373 1372
1374 1373 for i in range(num_pairs):
1375 1374 cspc_par[i,:,:] = __calculateMoments(norm_cspectra)
1376 1375 #------------------- Get Lags ----------------------------------
1377 1376
1378 1377 class SALags(Operation):
1379 1378 '''
1380 1379 Function GetMoments()
1381 1380
1382 1381 Input:
1383 1382 self.dataOut.data_pre
1384 1383 self.dataOut.abscissaList
1385 1384 self.dataOut.noise
1386 1385 self.dataOut.normFactor
1387 1386 self.dataOut.data_snr
1388 1387 self.dataOut.groupList
1389 1388 self.dataOut.nChannels
1390 1389
1391 1390 Affected:
1392 1391 self.dataOut.data_param
1393 1392
1394 1393 '''
1395 1394 def run(self, dataOut):
1396 1395 data_acf = dataOut.data_pre[0]
1397 1396 data_ccf = dataOut.data_pre[1]
1398 1397 normFactor_acf = dataOut.normFactor[0]
1399 1398 normFactor_ccf = dataOut.normFactor[1]
1400 1399 pairs_acf = dataOut.groupList[0]
1401 1400 pairs_ccf = dataOut.groupList[1]
1402 1401
1403 1402 nHeights = dataOut.nHeights
1404 1403 absc = dataOut.abscissaList
1405 1404 noise = dataOut.noise
1406 1405 SNR = dataOut.data_snr
1407 1406 nChannels = dataOut.nChannels
1408 1407 # pairsList = dataOut.groupList
1409 1408 # pairsAutoCorr, pairsCrossCorr = self.__getPairsAutoCorr(pairsList, nChannels)
1410 1409
1411 1410 for l in range(len(pairs_acf)):
1412 1411 data_acf[l,:,:] = data_acf[l,:,:]/normFactor_acf[l,:]
1413 1412
1414 1413 for l in range(len(pairs_ccf)):
1415 1414 data_ccf[l,:,:] = data_ccf[l,:,:]/normFactor_ccf[l,:]
1416 1415
1417 1416 dataOut.data_param = numpy.zeros((len(pairs_ccf)*2 + 1, nHeights))
1418 1417 dataOut.data_param[:-1,:] = self.__calculateTaus(data_acf, data_ccf, absc)
1419 1418 dataOut.data_param[-1,:] = self.__calculateLag1Phase(data_acf, absc)
1420 1419 return
1421 1420
1422 1421 # def __getPairsAutoCorr(self, pairsList, nChannels):
1423 1422 #
1424 1423 # pairsAutoCorr = numpy.zeros(nChannels, dtype = 'int')*numpy.nan
1425 1424 #
1426 1425 # for l in range(len(pairsList)):
1427 1426 # firstChannel = pairsList[l][0]
1428 1427 # secondChannel = pairsList[l][1]
1429 1428 #
1430 1429 # #Obteniendo pares de Autocorrelacion
1431 1430 # if firstChannel == secondChannel:
1432 1431 # pairsAutoCorr[firstChannel] = int(l)
1433 1432 #
1434 1433 # pairsAutoCorr = pairsAutoCorr.astype(int)
1435 1434 #
1436 1435 # pairsCrossCorr = range(len(pairsList))
1437 1436 # pairsCrossCorr = numpy.delete(pairsCrossCorr,pairsAutoCorr)
1438 1437 #
1439 1438 # return pairsAutoCorr, pairsCrossCorr
1440 1439
1441 1440 def __calculateTaus(self, data_acf, data_ccf, lagRange):
1442 1441
1443 1442 lag0 = data_acf.shape[1]/2
1444 1443 #Funcion de Autocorrelacion
1445 1444 mean_acf = stats.nanmean(data_acf, axis = 0)
1446 1445
1447 1446 #Obtencion Indice de TauCross
1448 1447 ind_ccf = data_ccf.argmax(axis = 1)
1449 1448 #Obtencion Indice de TauAuto
1450 1449 ind_acf = numpy.zeros(ind_ccf.shape,dtype = 'int')
1451 1450 ccf_lag0 = data_ccf[:,lag0,:]
1452 1451
1453 1452 for i in range(ccf_lag0.shape[0]):
1454 1453 ind_acf[i,:] = numpy.abs(mean_acf - ccf_lag0[i,:]).argmin(axis = 0)
1455 1454
1456 1455 #Obtencion de TauCross y TauAuto
1457 1456 tau_ccf = lagRange[ind_ccf]
1458 1457 tau_acf = lagRange[ind_acf]
1459 1458
1460 1459 Nan1, Nan2 = numpy.where(tau_ccf == lagRange[0])
1461 1460
1462 1461 tau_ccf[Nan1,Nan2] = numpy.nan
1463 1462 tau_acf[Nan1,Nan2] = numpy.nan
1464 1463 tau = numpy.vstack((tau_ccf,tau_acf))
1465 1464
1466 1465 return tau
1467 1466
1468 1467 def __calculateLag1Phase(self, data, lagTRange):
1469 1468 data1 = stats.nanmean(data, axis = 0)
1470 1469 lag1 = numpy.where(lagTRange == 0)[0][0] + 1
1471 1470
1472 1471 phase = numpy.angle(data1[lag1,:])
1473 1472
1474 1473 return phase
1475 1474
1476 1475 class SpectralFitting(Operation):
1477 1476 '''
1478 1477 Function GetMoments()
1479 1478
1480 1479 Input:
1481 1480 Output:
1482 1481 Variables modified:
1483 1482 '''
1484 1483
1485 1484 def run(self, dataOut, getSNR = True, path=None, file=None, groupList=None):
1486 1485
1487 1486
1488 1487 if path != None:
1489 1488 sys.path.append(path)
1490 1489 self.dataOut.library = importlib.import_module(file)
1491 1490
1492 1491 #To be inserted as a parameter
1493 1492 groupArray = numpy.array(groupList)
1494 1493 # groupArray = numpy.array([[0,1],[2,3]])
1495 1494 self.dataOut.groupList = groupArray
1496 1495
1497 1496 nGroups = groupArray.shape[0]
1498 1497 nChannels = self.dataIn.nChannels
1499 1498 nHeights=self.dataIn.heightList.size
1500 1499
1501 1500 #Parameters Array
1502 1501 self.dataOut.data_param = None
1503 1502
1504 1503 #Set constants
1505 1504 constants = self.dataOut.library.setConstants(self.dataIn)
1506 1505 self.dataOut.constants = constants
1507 1506 M = self.dataIn.normFactor
1508 1507 N = self.dataIn.nFFTPoints
1509 1508 ippSeconds = self.dataIn.ippSeconds
1510 1509 K = self.dataIn.nIncohInt
1511 1510 pairsArray = numpy.array(self.dataIn.pairsList)
1512 1511
1513 1512 #List of possible combinations
1514 1513 listComb = itertools.combinations(numpy.arange(groupArray.shape[1]),2)
1515 1514 indCross = numpy.zeros(len(list(listComb)), dtype = 'int')
1516 1515
1517 1516 if getSNR:
1518 1517 listChannels = groupArray.reshape((groupArray.size))
1519 1518 listChannels.sort()
1520 1519 noise = self.dataIn.getNoise()
1521 1520 self.dataOut.data_snr = self.__getSNR(self.dataIn.data_spc[listChannels,:,:], noise[listChannels])
1522 1521
1523 1522 for i in range(nGroups):
1524 1523 coord = groupArray[i,:]
1525 1524
1526 1525 #Input data array
1527 1526 data = self.dataIn.data_spc[coord,:,:]/(M*N)
1528 1527 data = data.reshape((data.shape[0]*data.shape[1],data.shape[2]))
1529 1528
1530 1529 #Cross Spectra data array for Covariance Matrixes
1531 1530 ind = 0
1532 1531 for pairs in listComb:
1533 1532 pairsSel = numpy.array([coord[x],coord[y]])
1534 1533 indCross[ind] = int(numpy.where(numpy.all(pairsArray == pairsSel, axis = 1))[0][0])
1535 1534 ind += 1
1536 1535 dataCross = self.dataIn.data_cspc[indCross,:,:]/(M*N)
1537 1536 dataCross = dataCross**2/K
1538 1537
1539 1538 for h in range(nHeights):
1540 1539
1541 1540 #Input
1542 1541 d = data[:,h]
1543 1542
1544 1543 #Covariance Matrix
1545 1544 D = numpy.diag(d**2/K)
1546 1545 ind = 0
1547 1546 for pairs in listComb:
1548 1547 #Coordinates in Covariance Matrix
1549 1548 x = pairs[0]
1550 1549 y = pairs[1]
1551 1550 #Channel Index
1552 1551 S12 = dataCross[ind,:,h]
1553 1552 D12 = numpy.diag(S12)
1554 1553 #Completing Covariance Matrix with Cross Spectras
1555 1554 D[x*N:(x+1)*N,y*N:(y+1)*N] = D12
1556 1555 D[y*N:(y+1)*N,x*N:(x+1)*N] = D12
1557 1556 ind += 1
1558 1557 Dinv=numpy.linalg.inv(D)
1559 1558 L=numpy.linalg.cholesky(Dinv)
1560 1559 LT=L.T
1561 1560
1562 1561 dp = numpy.dot(LT,d)
1563 1562
1564 1563 #Initial values
1565 1564 data_spc = self.dataIn.data_spc[coord,:,h]
1566 1565
1567 1566 if (h>0)and(error1[3]<5):
1568 1567 p0 = self.dataOut.data_param[i,:,h-1]
1569 1568 else:
1570 1569 p0 = numpy.array(self.dataOut.library.initialValuesFunction(data_spc, constants, i))
1571 1570
1572 1571 try:
1573 1572 #Least Squares
1574 1573 minp,covp,infodict,mesg,ier = optimize.leastsq(self.__residFunction,p0,args=(dp,LT,constants),full_output=True)
1575 1574 # minp,covp = optimize.leastsq(self.__residFunction,p0,args=(dp,LT,constants))
1576 1575 #Chi square error
1577 1576 error0 = numpy.sum(infodict['fvec']**2)/(2*N)
1578 1577 #Error with Jacobian
1579 1578 error1 = self.dataOut.library.errorFunction(minp,constants,LT)
1580 1579 except:
1581 1580 minp = p0*numpy.nan
1582 1581 error0 = numpy.nan
1583 1582 error1 = p0*numpy.nan
1584 1583
1585 1584 #Save
1586 1585 if self.dataOut.data_param is None:
1587 1586 self.dataOut.data_param = numpy.zeros((nGroups, p0.size, nHeights))*numpy.nan
1588 1587 self.dataOut.data_error = numpy.zeros((nGroups, p0.size + 1, nHeights))*numpy.nan
1589 1588
1590 1589 self.dataOut.data_error[i,:,h] = numpy.hstack((error0,error1))
1591 1590 self.dataOut.data_param[i,:,h] = minp
1592 1591 return
1593 1592
1594 1593 def __residFunction(self, p, dp, LT, constants):
1595 1594
1596 1595 fm = self.dataOut.library.modelFunction(p, constants)
1597 1596 fmp=numpy.dot(LT,fm)
1598 1597
1599 1598 return dp-fmp
1600 1599
1601 1600 def __getSNR(self, z, noise):
1602 1601
1603 1602 avg = numpy.average(z, axis=1)
1604 1603 SNR = (avg.T-noise)/noise
1605 1604 SNR = SNR.T
1606 1605 return SNR
1607 1606
1608 1607 def __chisq(p,chindex,hindex):
1609 1608 #similar to Resid but calculates CHI**2
1610 1609 [LT,d,fm]=setupLTdfm(p,chindex,hindex)
1611 1610 dp=numpy.dot(LT,d)
1612 1611 fmp=numpy.dot(LT,fm)
1613 1612 chisq=numpy.dot((dp-fmp).T,(dp-fmp))
1614 1613 return chisq
1615 1614
1616 1615 class WindProfiler(Operation):
1617 1616
1618 1617 __isConfig = False
1619 1618
1620 1619 __initime = None
1621 1620 __lastdatatime = None
1622 1621 __integrationtime = None
1623 1622
1624 1623 __buffer = None
1625 1624
1626 1625 __dataReady = False
1627 1626
1628 1627 __firstdata = None
1629 1628
1630 1629 n = None
1631 1630
1632 1631 def __init__(self):
1633 1632 Operation.__init__(self)
1634 1633
1635 1634 def __calculateCosDir(self, elev, azim):
1636 1635 zen = (90 - elev)*numpy.pi/180
1637 1636 azim = azim*numpy.pi/180
1638 1637 cosDirX = numpy.sqrt((1-numpy.cos(zen)**2)/((1+numpy.tan(azim)**2)))
1639 1638 cosDirY = numpy.sqrt(1-numpy.cos(zen)**2-cosDirX**2)
1640 1639
1641 1640 signX = numpy.sign(numpy.cos(azim))
1642 1641 signY = numpy.sign(numpy.sin(azim))
1643 1642
1644 1643 cosDirX = numpy.copysign(cosDirX, signX)
1645 1644 cosDirY = numpy.copysign(cosDirY, signY)
1646 1645 return cosDirX, cosDirY
1647 1646
1648 1647 def __calculateAngles(self, theta_x, theta_y, azimuth):
1649 1648
1650 1649 dir_cosw = numpy.sqrt(1-theta_x**2-theta_y**2)
1651 1650 zenith_arr = numpy.arccos(dir_cosw)
1652 1651 azimuth_arr = numpy.arctan2(theta_x,theta_y) + azimuth*math.pi/180
1653 1652
1654 1653 dir_cosu = numpy.sin(azimuth_arr)*numpy.sin(zenith_arr)
1655 1654 dir_cosv = numpy.cos(azimuth_arr)*numpy.sin(zenith_arr)
1656 1655
1657 1656 return azimuth_arr, zenith_arr, dir_cosu, dir_cosv, dir_cosw
1658 1657
1659 1658 def __calculateMatA(self, dir_cosu, dir_cosv, dir_cosw, horOnly):
1660 1659
1661 1660 #
1662 1661 if horOnly:
1663 1662 A = numpy.c_[dir_cosu,dir_cosv]
1664 1663 else:
1665 1664 A = numpy.c_[dir_cosu,dir_cosv,dir_cosw]
1666 1665 A = numpy.asmatrix(A)
1667 1666 A1 = numpy.linalg.inv(A.transpose()*A)*A.transpose()
1668 1667
1669 1668 return A1
1670 1669
1671 1670 def __correctValues(self, heiRang, phi, velRadial, SNR):
1672 1671 listPhi = phi.tolist()
1673 1672 maxid = listPhi.index(max(listPhi))
1674 1673 minid = listPhi.index(min(listPhi))
1675 1674
1676 1675 rango = list(range(len(phi)))
1677 1676 # rango = numpy.delete(rango,maxid)
1678 1677
1679 1678 heiRang1 = heiRang*math.cos(phi[maxid])
1680 1679 heiRangAux = heiRang*math.cos(phi[minid])
1681 1680 indOut = (heiRang1 < heiRangAux[0]).nonzero()
1682 1681 heiRang1 = numpy.delete(heiRang1,indOut)
1683 1682
1684 1683 velRadial1 = numpy.zeros([len(phi),len(heiRang1)])
1685 1684 SNR1 = numpy.zeros([len(phi),len(heiRang1)])
1686 1685
1687 1686 for i in rango:
1688 1687 x = heiRang*math.cos(phi[i])
1689 1688 y1 = velRadial[i,:]
1690 1689 f1 = interpolate.interp1d(x,y1,kind = 'cubic')
1691 1690
1692 1691 x1 = heiRang1
1693 1692 y11 = f1(x1)
1694 1693
1695 1694 y2 = SNR[i,:]
1696 1695 f2 = interpolate.interp1d(x,y2,kind = 'cubic')
1697 1696 y21 = f2(x1)
1698 1697
1699 1698 velRadial1[i,:] = y11
1700 1699 SNR1[i,:] = y21
1701 1700
1702 1701 return heiRang1, velRadial1, SNR1
1703 1702
1704 1703 def __calculateVelUVW(self, A, velRadial):
1705 1704
1706 1705 #Operacion Matricial
1707 1706 # velUVW = numpy.zeros((velRadial.shape[1],3))
1708 1707 # for ind in range(velRadial.shape[1]):
1709 1708 # velUVW[ind,:] = numpy.dot(A,velRadial[:,ind])
1710 1709 # velUVW = velUVW.transpose()
1711 1710 velUVW = numpy.zeros((A.shape[0],velRadial.shape[1]))
1712 1711 velUVW[:,:] = numpy.dot(A,velRadial)
1713 1712
1714 1713
1715 1714 return velUVW
1716 1715
1717 1716 # def techniqueDBS(self, velRadial0, dirCosx, disrCosy, azimuth, correct, horizontalOnly, heiRang, SNR0):
1718 1717
1719 1718 def techniqueDBS(self, kwargs):
1720 1719 """
1721 1720 Function that implements Doppler Beam Swinging (DBS) technique.
1722 1721
1723 1722 Input: Radial velocities, Direction cosines (x and y) of the Beam, Antenna azimuth,
1724 1723 Direction correction (if necessary), Ranges and SNR
1725 1724
1726 1725 Output: Winds estimation (Zonal, Meridional and Vertical)
1727 1726
1728 1727 Parameters affected: Winds, height range, SNR
1729 1728 """
1730 1729 velRadial0 = kwargs['velRadial']
1731 1730 heiRang = kwargs['heightList']
1732 1731 SNR0 = kwargs['SNR']
1733 1732
1734 1733 if 'dirCosx' in kwargs and 'dirCosy' in kwargs:
1735 1734 theta_x = numpy.array(kwargs['dirCosx'])
1736 1735 theta_y = numpy.array(kwargs['dirCosy'])
1737 1736 else:
1738 1737 elev = numpy.array(kwargs['elevation'])
1739 1738 azim = numpy.array(kwargs['azimuth'])
1740 1739 theta_x, theta_y = self.__calculateCosDir(elev, azim)
1741 1740 azimuth = kwargs['correctAzimuth']
1742 1741 if 'horizontalOnly' in kwargs:
1743 1742 horizontalOnly = kwargs['horizontalOnly']
1744 1743 else: horizontalOnly = False
1745 1744 if 'correctFactor' in kwargs:
1746 1745 correctFactor = kwargs['correctFactor']
1747 1746 else: correctFactor = 1
1748 1747 if 'channelList' in kwargs:
1749 1748 channelList = kwargs['channelList']
1750 1749 if len(channelList) == 2:
1751 1750 horizontalOnly = True
1752 1751 arrayChannel = numpy.array(channelList)
1753 1752 param = param[arrayChannel,:,:]
1754 1753 theta_x = theta_x[arrayChannel]
1755 1754 theta_y = theta_y[arrayChannel]
1756 1755
1757 1756 azimuth_arr, zenith_arr, dir_cosu, dir_cosv, dir_cosw = self.__calculateAngles(theta_x, theta_y, azimuth)
1758 1757 heiRang1, velRadial1, SNR1 = self.__correctValues(heiRang, zenith_arr, correctFactor*velRadial0, SNR0)
1759 1758 A = self.__calculateMatA(dir_cosu, dir_cosv, dir_cosw, horizontalOnly)
1760 1759
1761 1760 #Calculo de Componentes de la velocidad con DBS
1762 1761 winds = self.__calculateVelUVW(A,velRadial1)
1763 1762
1764 1763 return winds, heiRang1, SNR1
1765 1764
1766 1765 def __calculateDistance(self, posx, posy, pairs_ccf, azimuth = None):
1767 1766
1768 1767 nPairs = len(pairs_ccf)
1769 1768 posx = numpy.asarray(posx)
1770 1769 posy = numpy.asarray(posy)
1771 1770
1772 1771 #Rotacion Inversa para alinear con el azimuth
1773 1772 if azimuth!= None:
1774 1773 azimuth = azimuth*math.pi/180
1775 1774 posx1 = posx*math.cos(azimuth) + posy*math.sin(azimuth)
1776 1775 posy1 = -posx*math.sin(azimuth) + posy*math.cos(azimuth)
1777 1776 else:
1778 1777 posx1 = posx
1779 1778 posy1 = posy
1780 1779
1781 1780 #Calculo de Distancias
1782 1781 distx = numpy.zeros(nPairs)
1783 1782 disty = numpy.zeros(nPairs)
1784 1783 dist = numpy.zeros(nPairs)
1785 1784 ang = numpy.zeros(nPairs)
1786 1785
1787 1786 for i in range(nPairs):
1788 1787 distx[i] = posx1[pairs_ccf[i][1]] - posx1[pairs_ccf[i][0]]
1789 1788 disty[i] = posy1[pairs_ccf[i][1]] - posy1[pairs_ccf[i][0]]
1790 1789 dist[i] = numpy.sqrt(distx[i]**2 + disty[i]**2)
1791 1790 ang[i] = numpy.arctan2(disty[i],distx[i])
1792 1791
1793 1792 return distx, disty, dist, ang
1794 1793 #Calculo de Matrices
1795 1794 # nPairs = len(pairs)
1796 1795 # ang1 = numpy.zeros((nPairs, 2, 1))
1797 1796 # dist1 = numpy.zeros((nPairs, 2, 1))
1798 1797 #
1799 1798 # for j in range(nPairs):
1800 1799 # dist1[j,0,0] = dist[pairs[j][0]]
1801 1800 # dist1[j,1,0] = dist[pairs[j][1]]
1802 1801 # ang1[j,0,0] = ang[pairs[j][0]]
1803 1802 # ang1[j,1,0] = ang[pairs[j][1]]
1804 1803 #
1805 1804 # return distx,disty, dist1,ang1
1806 1805
1807 1806
1808 1807 def __calculateVelVer(self, phase, lagTRange, _lambda):
1809 1808
1810 1809 Ts = lagTRange[1] - lagTRange[0]
1811 1810 velW = -_lambda*phase/(4*math.pi*Ts)
1812 1811
1813 1812 return velW
1814 1813
1815 1814 def __calculateVelHorDir(self, dist, tau1, tau2, ang):
1816 1815 nPairs = tau1.shape[0]
1817 1816 nHeights = tau1.shape[1]
1818 1817 vel = numpy.zeros((nPairs,3,nHeights))
1819 1818 dist1 = numpy.reshape(dist, (dist.size,1))
1820 1819
1821 1820 angCos = numpy.cos(ang)
1822 1821 angSin = numpy.sin(ang)
1823 1822
1824 1823 vel0 = dist1*tau1/(2*tau2**2)
1825 1824 vel[:,0,:] = (vel0*angCos).sum(axis = 1)
1826 1825 vel[:,1,:] = (vel0*angSin).sum(axis = 1)
1827 1826
1828 1827 ind = numpy.where(numpy.isinf(vel))
1829 1828 vel[ind] = numpy.nan
1830 1829
1831 1830 return vel
1832 1831
1833 1832 # def __getPairsAutoCorr(self, pairsList, nChannels):
1834 1833 #
1835 1834 # pairsAutoCorr = numpy.zeros(nChannels, dtype = 'int')*numpy.nan
1836 1835 #
1837 1836 # for l in range(len(pairsList)):
1838 1837 # firstChannel = pairsList[l][0]
1839 1838 # secondChannel = pairsList[l][1]
1840 1839 #
1841 1840 # #Obteniendo pares de Autocorrelacion
1842 1841 # if firstChannel == secondChannel:
1843 1842 # pairsAutoCorr[firstChannel] = int(l)
1844 1843 #
1845 1844 # pairsAutoCorr = pairsAutoCorr.astype(int)
1846 1845 #
1847 1846 # pairsCrossCorr = range(len(pairsList))
1848 1847 # pairsCrossCorr = numpy.delete(pairsCrossCorr,pairsAutoCorr)
1849 1848 #
1850 1849 # return pairsAutoCorr, pairsCrossCorr
1851 1850
1852 1851 # def techniqueSA(self, pairsSelected, pairsList, nChannels, tau, azimuth, _lambda, position_x, position_y, lagTRange, correctFactor):
1853 1852 def techniqueSA(self, kwargs):
1854 1853
1855 1854 """
1856 1855 Function that implements Spaced Antenna (SA) technique.
1857 1856
1858 1857 Input: Radial velocities, Direction cosines (x and y) of the Beam, Antenna azimuth,
1859 1858 Direction correction (if necessary), Ranges and SNR
1860 1859
1861 1860 Output: Winds estimation (Zonal, Meridional and Vertical)
1862 1861
1863 1862 Parameters affected: Winds
1864 1863 """
1865 1864 position_x = kwargs['positionX']
1866 1865 position_y = kwargs['positionY']
1867 1866 azimuth = kwargs['azimuth']
1868 1867
1869 1868 if 'correctFactor' in kwargs:
1870 1869 correctFactor = kwargs['correctFactor']
1871 1870 else:
1872 1871 correctFactor = 1
1873 1872
1874 1873 groupList = kwargs['groupList']
1875 1874 pairs_ccf = groupList[1]
1876 1875 tau = kwargs['tau']
1877 1876 _lambda = kwargs['_lambda']
1878 1877
1879 1878 #Cross Correlation pairs obtained
1880 1879 # pairsAutoCorr, pairsCrossCorr = self.__getPairsAutoCorr(pairssList, nChannels)
1881 1880 # pairsArray = numpy.array(pairsList)[pairsCrossCorr]
1882 1881 # pairsSelArray = numpy.array(pairsSelected)
1883 1882 # pairs = []
1884 1883 #
1885 1884 # #Wind estimation pairs obtained
1886 1885 # for i in range(pairsSelArray.shape[0]/2):
1887 1886 # ind1 = numpy.where(numpy.all(pairsArray == pairsSelArray[2*i], axis = 1))[0][0]
1888 1887 # ind2 = numpy.where(numpy.all(pairsArray == pairsSelArray[2*i + 1], axis = 1))[0][0]
1889 1888 # pairs.append((ind1,ind2))
1890 1889
1891 1890 indtau = tau.shape[0]/2
1892 1891 tau1 = tau[:indtau,:]
1893 1892 tau2 = tau[indtau:-1,:]
1894 1893 # tau1 = tau1[pairs,:]
1895 1894 # tau2 = tau2[pairs,:]
1896 1895 phase1 = tau[-1,:]
1897 1896
1898 1897 #---------------------------------------------------------------------
1899 1898 #Metodo Directo
1900 1899 distx, disty, dist, ang = self.__calculateDistance(position_x, position_y, pairs_ccf,azimuth)
1901 1900 winds = self.__calculateVelHorDir(dist, tau1, tau2, ang)
1902 1901 winds = stats.nanmean(winds, axis=0)
1903 1902 #---------------------------------------------------------------------
1904 1903 #Metodo General
1905 1904 # distx, disty, dist = self.calculateDistance(position_x,position_y,pairsCrossCorr, pairsList, azimuth)
1906 1905 # #Calculo Coeficientes de Funcion de Correlacion
1907 1906 # F,G,A,B,H = self.calculateCoef(tau1,tau2,distx,disty,n)
1908 1907 # #Calculo de Velocidades
1909 1908 # winds = self.calculateVelUV(F,G,A,B,H)
1910 1909
1911 1910 #---------------------------------------------------------------------
1912 1911 winds[2,:] = self.__calculateVelVer(phase1, lagTRange, _lambda)
1913 1912 winds = correctFactor*winds
1914 1913 return winds
1915 1914
1916 1915 def __checkTime(self, currentTime, paramInterval, outputInterval):
1917 1916
1918 1917 dataTime = currentTime + paramInterval
1919 1918 deltaTime = dataTime - self.__initime
1920 1919
1921 1920 if deltaTime >= outputInterval or deltaTime < 0:
1922 1921 self.__dataReady = True
1923 1922 return
1924 1923
1925 1924 def techniqueMeteors(self, arrayMeteor, meteorThresh, heightMin, heightMax):
1926 1925 '''
1927 1926 Function that implements winds estimation technique with detected meteors.
1928 1927
1929 1928 Input: Detected meteors, Minimum meteor quantity to wind estimation
1930 1929
1931 1930 Output: Winds estimation (Zonal and Meridional)
1932 1931
1933 1932 Parameters affected: Winds
1934 1933 '''
1935 1934 #Settings
1936 1935 nInt = (heightMax - heightMin)/2
1937 1936 nInt = int(nInt)
1938 1937 winds = numpy.zeros((2,nInt))*numpy.nan
1939 1938
1940 1939 #Filter errors
1941 1940 error = numpy.where(arrayMeteor[:,-1] == 0)[0]
1942 1941 finalMeteor = arrayMeteor[error,:]
1943 1942
1944 1943 #Meteor Histogram
1945 1944 finalHeights = finalMeteor[:,2]
1946 1945 hist = numpy.histogram(finalHeights, bins = nInt, range = (heightMin,heightMax))
1947 1946 nMeteorsPerI = hist[0]
1948 1947 heightPerI = hist[1]
1949 1948
1950 1949 #Sort of meteors
1951 1950 indSort = finalHeights.argsort()
1952 1951 finalMeteor2 = finalMeteor[indSort,:]
1953 1952
1954 1953 # Calculating winds
1955 1954 ind1 = 0
1956 1955 ind2 = 0
1957 1956
1958 1957 for i in range(nInt):
1959 1958 nMet = nMeteorsPerI[i]
1960 1959 ind1 = ind2
1961 1960 ind2 = ind1 + nMet
1962 1961
1963 1962 meteorAux = finalMeteor2[ind1:ind2,:]
1964 1963
1965 1964 if meteorAux.shape[0] >= meteorThresh:
1966 1965 vel = meteorAux[:, 6]
1967 1966 zen = meteorAux[:, 4]*numpy.pi/180
1968 1967 azim = meteorAux[:, 3]*numpy.pi/180
1969 1968
1970 1969 n = numpy.cos(zen)
1971 1970 # m = (1 - n**2)/(1 - numpy.tan(azim)**2)
1972 1971 # l = m*numpy.tan(azim)
1973 1972 l = numpy.sin(zen)*numpy.sin(azim)
1974 1973 m = numpy.sin(zen)*numpy.cos(azim)
1975 1974
1976 1975 A = numpy.vstack((l, m)).transpose()
1977 1976 A1 = numpy.dot(numpy.linalg.inv( numpy.dot(A.transpose(),A) ),A.transpose())
1978 1977 windsAux = numpy.dot(A1, vel)
1979 1978
1980 1979 winds[0,i] = windsAux[0]
1981 1980 winds[1,i] = windsAux[1]
1982 1981
1983 1982 return winds, heightPerI[:-1]
1984 1983
1985 1984 def techniqueNSM_SA(self, **kwargs):
1986 1985 metArray = kwargs['metArray']
1987 1986 heightList = kwargs['heightList']
1988 1987 timeList = kwargs['timeList']
1989 1988
1990 1989 rx_location = kwargs['rx_location']
1991 1990 groupList = kwargs['groupList']
1992 1991 azimuth = kwargs['azimuth']
1993 1992 dfactor = kwargs['dfactor']
1994 1993 k = kwargs['k']
1995 1994
1996 1995 azimuth1, dist = self.__calculateAzimuth1(rx_location, groupList, azimuth)
1997 1996 d = dist*dfactor
1998 1997 #Phase calculation
1999 1998 metArray1 = self.__getPhaseSlope(metArray, heightList, timeList)
2000 1999
2001 2000 metArray1[:,-2] = metArray1[:,-2]*metArray1[:,2]*1000/(k*d[metArray1[:,1].astype(int)]) #angles into velocities
2002 2001
2003 2002 velEst = numpy.zeros((heightList.size,2))*numpy.nan
2004 2003 azimuth1 = azimuth1*numpy.pi/180
2005 2004
2006 2005 for i in range(heightList.size):
2007 2006 h = heightList[i]
2008 2007 indH = numpy.where((metArray1[:,2] == h)&(numpy.abs(metArray1[:,-2]) < 100))[0]
2009 2008 metHeight = metArray1[indH,:]
2010 2009 if metHeight.shape[0] >= 2:
2011 2010 velAux = numpy.asmatrix(metHeight[:,-2]).T #Radial Velocities
2012 2011 iazim = metHeight[:,1].astype(int)
2013 2012 azimAux = numpy.asmatrix(azimuth1[iazim]).T #Azimuths
2014 2013 A = numpy.hstack((numpy.cos(azimAux),numpy.sin(azimAux)))
2015 2014 A = numpy.asmatrix(A)
2016 2015 A1 = numpy.linalg.pinv(A.transpose()*A)*A.transpose()
2017 2016 velHor = numpy.dot(A1,velAux)
2018 2017
2019 2018 velEst[i,:] = numpy.squeeze(velHor)
2020 2019 return velEst
2021 2020
2022 2021 def __getPhaseSlope(self, metArray, heightList, timeList):
2023 2022 meteorList = []
2024 2023 #utctime sec1 height SNR velRad ph0 ph1 ph2 coh0 coh1 coh2
2025 2024 #Putting back together the meteor matrix
2026 2025 utctime = metArray[:,0]
2027 2026 uniqueTime = numpy.unique(utctime)
2028 2027
2029 2028 phaseDerThresh = 0.5
2030 2029 ippSeconds = timeList[1] - timeList[0]
2031 2030 sec = numpy.where(timeList>1)[0][0]
2032 2031 nPairs = metArray.shape[1] - 6
2033 2032 nHeights = len(heightList)
2034 2033
2035 2034 for t in uniqueTime:
2036 2035 metArray1 = metArray[utctime==t,:]
2037 2036 # phaseDerThresh = numpy.pi/4 #reducir Phase thresh
2038 2037 tmet = metArray1[:,1].astype(int)
2039 2038 hmet = metArray1[:,2].astype(int)
2040 2039
2041 2040 metPhase = numpy.zeros((nPairs, heightList.size, timeList.size - 1))
2042 2041 metPhase[:,:] = numpy.nan
2043 2042 metPhase[:,hmet,tmet] = metArray1[:,6:].T
2044 2043
2045 2044 #Delete short trails
2046 2045 metBool = ~numpy.isnan(metPhase[0,:,:])
2047 2046 heightVect = numpy.sum(metBool, axis = 1)
2048 2047 metBool[heightVect<sec,:] = False
2049 2048 metPhase[:,heightVect<sec,:] = numpy.nan
2050 2049
2051 2050 #Derivative
2052 2051 metDer = numpy.abs(metPhase[:,:,1:] - metPhase[:,:,:-1])
2053 2052 phDerAux = numpy.dstack((numpy.full((nPairs,nHeights,1), False, dtype=bool),metDer > phaseDerThresh))
2054 2053 metPhase[phDerAux] = numpy.nan
2055 2054
2056 2055 #--------------------------METEOR DETECTION -----------------------------------------
2057 2056 indMet = numpy.where(numpy.any(metBool,axis=1))[0]
2058 2057
2059 2058 for p in numpy.arange(nPairs):
2060 2059 phase = metPhase[p,:,:]
2061 2060 phDer = metDer[p,:,:]
2062 2061
2063 2062 for h in indMet:
2064 2063 height = heightList[h]
2065 2064 phase1 = phase[h,:] #82
2066 2065 phDer1 = phDer[h,:]
2067 2066
2068 2067 phase1[~numpy.isnan(phase1)] = numpy.unwrap(phase1[~numpy.isnan(phase1)]) #Unwrap
2069 2068
2070 2069 indValid = numpy.where(~numpy.isnan(phase1))[0]
2071 2070 initMet = indValid[0]
2072 2071 endMet = 0
2073 2072
2074 2073 for i in range(len(indValid)-1):
2075 2074
2076 2075 #Time difference
2077 2076 inow = indValid[i]
2078 2077 inext = indValid[i+1]
2079 2078 idiff = inext - inow
2080 2079 #Phase difference
2081 2080 phDiff = numpy.abs(phase1[inext] - phase1[inow])
2082 2081
2083 2082 if idiff>sec or phDiff>numpy.pi/4 or inext==indValid[-1]: #End of Meteor
2084 2083 sizeTrail = inow - initMet + 1
2085 2084 if sizeTrail>3*sec: #Too short meteors
2086 2085 x = numpy.arange(initMet,inow+1)*ippSeconds
2087 2086 y = phase1[initMet:inow+1]
2088 2087 ynnan = ~numpy.isnan(y)
2089 2088 x = x[ynnan]
2090 2089 y = y[ynnan]
2091 2090 slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
2092 2091 ylin = x*slope + intercept
2093 2092 rsq = r_value**2
2094 2093 if rsq > 0.5:
2095 2094 vel = slope#*height*1000/(k*d)
2096 2095 estAux = numpy.array([utctime,p,height, vel, rsq])
2097 2096 meteorList.append(estAux)
2098 2097 initMet = inext
2099 2098 metArray2 = numpy.array(meteorList)
2100 2099
2101 2100 return metArray2
2102 2101
2103 2102 def __calculateAzimuth1(self, rx_location, pairslist, azimuth0):
2104 2103
2105 2104 azimuth1 = numpy.zeros(len(pairslist))
2106 2105 dist = numpy.zeros(len(pairslist))
2107 2106
2108 2107 for i in range(len(rx_location)):
2109 2108 ch0 = pairslist[i][0]
2110 2109 ch1 = pairslist[i][1]
2111 2110
2112 2111 diffX = rx_location[ch0][0] - rx_location[ch1][0]
2113 2112 diffY = rx_location[ch0][1] - rx_location[ch1][1]
2114 2113 azimuth1[i] = numpy.arctan2(diffY,diffX)*180/numpy.pi
2115 2114 dist[i] = numpy.sqrt(diffX**2 + diffY**2)
2116 2115
2117 2116 azimuth1 -= azimuth0
2118 2117 return azimuth1, dist
2119 2118
2120 2119 def techniqueNSM_DBS(self, **kwargs):
2121 2120 metArray = kwargs['metArray']
2122 2121 heightList = kwargs['heightList']
2123 2122 timeList = kwargs['timeList']
2124 2123 azimuth = kwargs['azimuth']
2125 2124 theta_x = numpy.array(kwargs['theta_x'])
2126 2125 theta_y = numpy.array(kwargs['theta_y'])
2127 2126
2128 2127 utctime = metArray[:,0]
2129 2128 cmet = metArray[:,1].astype(int)
2130 2129 hmet = metArray[:,3].astype(int)
2131 2130 SNRmet = metArray[:,4]
2132 2131 vmet = metArray[:,5]
2133 2132 spcmet = metArray[:,6]
2134 2133
2135 2134 nChan = numpy.max(cmet) + 1
2136 2135 nHeights = len(heightList)
2137 2136
2138 2137 azimuth_arr, zenith_arr, dir_cosu, dir_cosv, dir_cosw = self.__calculateAngles(theta_x, theta_y, azimuth)
2139 2138 hmet = heightList[hmet]
2140 2139 h1met = hmet*numpy.cos(zenith_arr[cmet]) #Corrected heights
2141 2140
2142 2141 velEst = numpy.zeros((heightList.size,2))*numpy.nan
2143 2142
2144 2143 for i in range(nHeights - 1):
2145 2144 hmin = heightList[i]
2146 2145 hmax = heightList[i + 1]
2147 2146
2148 2147 thisH = (h1met>=hmin) & (h1met<hmax) & (cmet!=2) & (SNRmet>8) & (vmet<50) & (spcmet<10)
2149 2148 indthisH = numpy.where(thisH)
2150 2149
2151 2150 if numpy.size(indthisH) > 3:
2152 2151
2153 2152 vel_aux = vmet[thisH]
2154 2153 chan_aux = cmet[thisH]
2155 2154 cosu_aux = dir_cosu[chan_aux]
2156 2155 cosv_aux = dir_cosv[chan_aux]
2157 2156 cosw_aux = dir_cosw[chan_aux]
2158 2157
2159 2158 nch = numpy.size(numpy.unique(chan_aux))
2160 2159 if nch > 1:
2161 2160 A = self.__calculateMatA(cosu_aux, cosv_aux, cosw_aux, True)
2162 2161 velEst[i,:] = numpy.dot(A,vel_aux)
2163 2162
2164 2163 return velEst
2165 2164
2166 2165 def run(self, dataOut, technique, nHours=1, hmin=70, hmax=110, **kwargs):
2167 2166
2168 2167 param = dataOut.data_param
2169 2168 if dataOut.abscissaList != None:
2170 2169 absc = dataOut.abscissaList[:-1]
2171 2170 # noise = dataOut.noise
2172 2171 heightList = dataOut.heightList
2173 2172 SNR = dataOut.data_snr
2174 2173
2175 2174 if technique == 'DBS':
2176 2175
2177 2176 kwargs['velRadial'] = param[:,1,:] #Radial velocity
2178 2177 kwargs['heightList'] = heightList
2179 2178 kwargs['SNR'] = SNR
2180 2179
2181 2180 dataOut.data_output, dataOut.heightList, dataOut.data_snr = self.techniqueDBS(kwargs) #DBS Function
2182 2181 dataOut.utctimeInit = dataOut.utctime
2183 2182 dataOut.outputInterval = dataOut.paramInterval
2184 2183
2185 2184 elif technique == 'SA':
2186 2185
2187 2186 #Parameters
2188 2187 # position_x = kwargs['positionX']
2189 2188 # position_y = kwargs['positionY']
2190 2189 # azimuth = kwargs['azimuth']
2191 2190 #
2192 2191 # if kwargs.has_key('crosspairsList'):
2193 2192 # pairs = kwargs['crosspairsList']
2194 2193 # else:
2195 2194 # pairs = None
2196 2195 #
2197 2196 # if kwargs.has_key('correctFactor'):
2198 2197 # correctFactor = kwargs['correctFactor']
2199 2198 # else:
2200 2199 # correctFactor = 1
2201 2200
2202 2201 # tau = dataOut.data_param
2203 2202 # _lambda = dataOut.C/dataOut.frequency
2204 2203 # pairsList = dataOut.groupList
2205 2204 # nChannels = dataOut.nChannels
2206 2205
2207 2206 kwargs['groupList'] = dataOut.groupList
2208 2207 kwargs['tau'] = dataOut.data_param
2209 2208 kwargs['_lambda'] = dataOut.C/dataOut.frequency
2210 2209 # dataOut.data_output = self.techniqueSA(pairs, pairsList, nChannels, tau, azimuth, _lambda, position_x, position_y, absc, correctFactor)
2211 2210 dataOut.data_output = self.techniqueSA(kwargs)
2212 2211 dataOut.utctimeInit = dataOut.utctime
2213 2212 dataOut.outputInterval = dataOut.timeInterval
2214 2213
2215 2214 elif technique == 'Meteors':
2216 2215 dataOut.flagNoData = True
2217 2216 self.__dataReady = False
2218 2217
2219 2218 if 'nHours' in kwargs:
2220 2219 nHours = kwargs['nHours']
2221 2220 else:
2222 2221 nHours = 1
2223 2222
2224 2223 if 'meteorsPerBin' in kwargs:
2225 2224 meteorThresh = kwargs['meteorsPerBin']
2226 2225 else:
2227 2226 meteorThresh = 6
2228 2227
2229 2228 if 'hmin' in kwargs:
2230 2229 hmin = kwargs['hmin']
2231 2230 else: hmin = 70
2232 2231 if 'hmax' in kwargs:
2233 2232 hmax = kwargs['hmax']
2234 2233 else: hmax = 110
2235 2234
2236 2235 dataOut.outputInterval = nHours*3600
2237 2236
2238 2237 if self.__isConfig == False:
2239 2238 # self.__initime = dataOut.datatime.replace(minute = 0, second = 0, microsecond = 03)
2240 2239 #Get Initial LTC time
2241 2240 self.__initime = datetime.datetime.utcfromtimestamp(dataOut.utctime)
2242 2241 self.__initime = (self.__initime.replace(minute = 0, second = 0, microsecond = 0) - datetime.datetime(1970, 1, 1)).total_seconds()
2243 2242
2244 2243 self.__isConfig = True
2245 2244
2246 2245 if self.__buffer is None:
2247 2246 self.__buffer = dataOut.data_param
2248 2247 self.__firstdata = copy.copy(dataOut)
2249 2248
2250 2249 else:
2251 2250 self.__buffer = numpy.vstack((self.__buffer, dataOut.data_param))
2252 2251
2253 2252 self.__checkTime(dataOut.utctime, dataOut.paramInterval, dataOut.outputInterval) #Check if the buffer is ready
2254 2253
2255 2254 if self.__dataReady:
2256 2255 dataOut.utctimeInit = self.__initime
2257 2256
2258 2257 self.__initime += dataOut.outputInterval #to erase time offset
2259 2258
2260 2259 dataOut.data_output, dataOut.heightList = self.techniqueMeteors(self.__buffer, meteorThresh, hmin, hmax)
2261 2260 dataOut.flagNoData = False
2262 2261 self.__buffer = None
2263 2262
2264 2263 elif technique == 'Meteors1':
2265 2264 dataOut.flagNoData = True
2266 2265 self.__dataReady = False
2267 2266
2268 2267 if 'nMins' in kwargs:
2269 2268 nMins = kwargs['nMins']
2270 2269 else: nMins = 20
2271 2270 if 'rx_location' in kwargs:
2272 2271 rx_location = kwargs['rx_location']
2273 2272 else: rx_location = [(0,1),(1,1),(1,0)]
2274 2273 if 'azimuth' in kwargs:
2275 2274 azimuth = kwargs['azimuth']
2276 2275 else: azimuth = 51.06
2277 2276 if 'dfactor' in kwargs:
2278 2277 dfactor = kwargs['dfactor']
2279 2278 if 'mode' in kwargs:
2280 2279 mode = kwargs['mode']
2281 2280 if 'theta_x' in kwargs:
2282 2281 theta_x = kwargs['theta_x']
2283 2282 if 'theta_y' in kwargs:
2284 2283 theta_y = kwargs['theta_y']
2285 2284 else: mode = 'SA'
2286 2285
2287 2286 #Borrar luego esto
2288 2287 if dataOut.groupList is None:
2289 2288 dataOut.groupList = [(0,1),(0,2),(1,2)]
2290 2289 groupList = dataOut.groupList
2291 2290 C = 3e8
2292 2291 freq = 50e6
2293 2292 lamb = C/freq
2294 2293 k = 2*numpy.pi/lamb
2295 2294
2296 2295 timeList = dataOut.abscissaList
2297 2296 heightList = dataOut.heightList
2298 2297
2299 2298 if self.__isConfig == False:
2300 2299 dataOut.outputInterval = nMins*60
2301 2300 # self.__initime = dataOut.datatime.replace(minute = 0, second = 0, microsecond = 03)
2302 2301 #Get Initial LTC time
2303 2302 initime = datetime.datetime.utcfromtimestamp(dataOut.utctime)
2304 2303 minuteAux = initime.minute
2305 2304 minuteNew = int(numpy.floor(minuteAux/nMins)*nMins)
2306 2305 self.__initime = (initime.replace(minute = minuteNew, second = 0, microsecond = 0) - datetime.datetime(1970, 1, 1)).total_seconds()
2307 2306
2308 2307 self.__isConfig = True
2309 2308
2310 2309 if self.__buffer is None:
2311 2310 self.__buffer = dataOut.data_param
2312 2311 self.__firstdata = copy.copy(dataOut)
2313 2312
2314 2313 else:
2315 2314 self.__buffer = numpy.vstack((self.__buffer, dataOut.data_param))
2316 2315
2317 2316 self.__checkTime(dataOut.utctime, dataOut.paramInterval, dataOut.outputInterval) #Check if the buffer is ready
2318 2317
2319 2318 if self.__dataReady:
2320 2319 dataOut.utctimeInit = self.__initime
2321 2320 self.__initime += dataOut.outputInterval #to erase time offset
2322 2321
2323 2322 metArray = self.__buffer
2324 2323 if mode == 'SA':
2325 2324 dataOut.data_output = self.techniqueNSM_SA(rx_location=rx_location, groupList=groupList, azimuth=azimuth, dfactor=dfactor, k=k,metArray=metArray, heightList=heightList,timeList=timeList)
2326 2325 elif mode == 'DBS':
2327 2326 dataOut.data_output = self.techniqueNSM_DBS(metArray=metArray,heightList=heightList,timeList=timeList, azimuth=azimuth, theta_x=theta_x, theta_y=theta_y)
2328 2327 dataOut.data_output = dataOut.data_output.T
2329 2328 dataOut.flagNoData = False
2330 2329 self.__buffer = None
2331 2330
2332 2331 return
2333 2332
2334 2333 class EWDriftsEstimation(Operation):
2335 2334
2336 2335 def __init__(self):
2337 2336 Operation.__init__(self)
2338 2337
2339 2338 def __correctValues(self, heiRang, phi, velRadial, SNR):
2340 2339 listPhi = phi.tolist()
2341 2340 maxid = listPhi.index(max(listPhi))
2342 2341 minid = listPhi.index(min(listPhi))
2343 2342
2344 2343 rango = list(range(len(phi)))
2345 2344 # rango = numpy.delete(rango,maxid)
2346 2345
2347 2346 heiRang1 = heiRang*math.cos(phi[maxid])
2348 2347 heiRangAux = heiRang*math.cos(phi[minid])
2349 2348 indOut = (heiRang1 < heiRangAux[0]).nonzero()
2350 2349 heiRang1 = numpy.delete(heiRang1,indOut)
2351 2350
2352 2351 velRadial1 = numpy.zeros([len(phi),len(heiRang1)])
2353 2352 SNR1 = numpy.zeros([len(phi),len(heiRang1)])
2354 2353
2355 2354 for i in rango:
2356 2355 x = heiRang*math.cos(phi[i])
2357 2356 y1 = velRadial[i,:]
2358 2357 f1 = interpolate.interp1d(x,y1,kind = 'cubic')
2359 2358
2360 2359 x1 = heiRang1
2361 2360 y11 = f1(x1)
2362 2361
2363 2362 y2 = SNR[i,:]
2364 2363 f2 = interpolate.interp1d(x,y2,kind = 'cubic')
2365 2364 y21 = f2(x1)
2366 2365
2367 2366 velRadial1[i,:] = y11
2368 2367 SNR1[i,:] = y21
2369 2368
2370 2369 return heiRang1, velRadial1, SNR1
2371 2370
2372 2371 def run(self, dataOut, zenith, zenithCorrection):
2373 2372 heiRang = dataOut.heightList
2374 2373 velRadial = dataOut.data_param[:,3,:]
2375 2374 SNR = dataOut.data_snr
2376 2375
2377 2376 zenith = numpy.array(zenith)
2378 2377 zenith -= zenithCorrection
2379 2378 zenith *= numpy.pi/180
2380 2379
2381 2380 heiRang1, velRadial1, SNR1 = self.__correctValues(heiRang, numpy.abs(zenith), velRadial, SNR)
2382 2381
2383 2382 alp = zenith[0]
2384 2383 bet = zenith[1]
2385 2384
2386 2385 w_w = velRadial1[0,:]
2387 2386 w_e = velRadial1[1,:]
2388 2387
2389 2388 w = (w_w*numpy.sin(bet) - w_e*numpy.sin(alp))/(numpy.cos(alp)*numpy.sin(bet) - numpy.cos(bet)*numpy.sin(alp))
2390 2389 u = (w_w*numpy.cos(bet) - w_e*numpy.cos(alp))/(numpy.sin(alp)*numpy.cos(bet) - numpy.sin(bet)*numpy.cos(alp))
2391 2390
2392 2391 winds = numpy.vstack((u,w))
2393 2392
2394 2393 dataOut.heightList = heiRang1
2395 2394 dataOut.data_output = winds
2396 2395 dataOut.data_snr = SNR1
2397 2396
2398 2397 dataOut.utctimeInit = dataOut.utctime
2399 2398 dataOut.outputInterval = dataOut.timeInterval
2400 2399 return
2401 2400
2402 2401 #--------------- Non Specular Meteor ----------------
2403 2402
2404 2403 class NonSpecularMeteorDetection(Operation):
2405 2404
2406 2405 def run(self, dataOut, mode, SNRthresh=8, phaseDerThresh=0.5, cohThresh=0.8, allData = False):
2407 2406 data_acf = dataOut.data_pre[0]
2408 2407 data_ccf = dataOut.data_pre[1]
2409 2408 pairsList = dataOut.groupList[1]
2410 2409
2411 2410 lamb = dataOut.C/dataOut.frequency
2412 2411 tSamp = dataOut.ippSeconds*dataOut.nCohInt
2413 2412 paramInterval = dataOut.paramInterval
2414 2413
2415 2414 nChannels = data_acf.shape[0]
2416 2415 nLags = data_acf.shape[1]
2417 2416 nProfiles = data_acf.shape[2]
2418 2417 nHeights = dataOut.nHeights
2419 2418 nCohInt = dataOut.nCohInt
2420 2419 sec = numpy.round(nProfiles/dataOut.paramInterval)
2421 2420 heightList = dataOut.heightList
2422 2421 ippSeconds = dataOut.ippSeconds*dataOut.nCohInt*dataOut.nAvg
2423 2422 utctime = dataOut.utctime
2424 2423
2425 2424 dataOut.abscissaList = numpy.arange(0,paramInterval+ippSeconds,ippSeconds)
2426 2425
2427 2426 #------------------------ SNR --------------------------------------
2428 2427 power = data_acf[:,0,:,:].real
2429 2428 noise = numpy.zeros(nChannels)
2430 2429 SNR = numpy.zeros(power.shape)
2431 2430 for i in range(nChannels):
2432 2431 noise[i] = hildebrand_sekhon(power[i,:], nCohInt)
2433 2432 SNR[i] = (power[i]-noise[i])/noise[i]
2434 2433 SNRm = numpy.nanmean(SNR, axis = 0)
2435 2434 SNRdB = 10*numpy.log10(SNR)
2436 2435
2437 2436 if mode == 'SA':
2438 2437 dataOut.groupList = dataOut.groupList[1]
2439 2438 nPairs = data_ccf.shape[0]
2440 2439 #---------------------- Coherence and Phase --------------------------
2441 2440 phase = numpy.zeros(data_ccf[:,0,:,:].shape)
2442 2441 # phase1 = numpy.copy(phase)
2443 2442 coh1 = numpy.zeros(data_ccf[:,0,:,:].shape)
2444 2443
2445 2444 for p in range(nPairs):
2446 2445 ch0 = pairsList[p][0]
2447 2446 ch1 = pairsList[p][1]
2448 2447 ccf = data_ccf[p,0,:,:]/numpy.sqrt(data_acf[ch0,0,:,:]*data_acf[ch1,0,:,:])
2449 2448 phase[p,:,:] = ndimage.median_filter(numpy.angle(ccf), size = (5,1)) #median filter
2450 2449 # phase1[p,:,:] = numpy.angle(ccf) #median filter
2451 2450 coh1[p,:,:] = ndimage.median_filter(numpy.abs(ccf), 5) #median filter
2452 2451 # coh1[p,:,:] = numpy.abs(ccf) #median filter
2453 2452 coh = numpy.nanmax(coh1, axis = 0)
2454 2453 # struc = numpy.ones((5,1))
2455 2454 # coh = ndimage.morphology.grey_dilation(coh, size=(10,1))
2456 2455 #---------------------- Radial Velocity ----------------------------
2457 2456 phaseAux = numpy.mean(numpy.angle(data_acf[:,1,:,:]), axis = 0)
2458 2457 velRad = phaseAux*lamb/(4*numpy.pi*tSamp)
2459 2458
2460 2459 if allData:
2461 2460 boolMetFin = ~numpy.isnan(SNRm)
2462 2461 # coh[:-1,:] = numpy.nanmean(numpy.abs(phase[:,1:,:] - phase[:,:-1,:]),axis=0)
2463 2462 else:
2464 2463 #------------------------ Meteor mask ---------------------------------
2465 2464 # #SNR mask
2466 2465 # boolMet = (SNRdB>SNRthresh)#|(~numpy.isnan(SNRdB))
2467 2466 #
2468 2467 # #Erase small objects
2469 2468 # boolMet1 = self.__erase_small(boolMet, 2*sec, 5)
2470 2469 #
2471 2470 # auxEEJ = numpy.sum(boolMet1,axis=0)
2472 2471 # indOver = auxEEJ>nProfiles*0.8 #Use this later
2473 2472 # indEEJ = numpy.where(indOver)[0]
2474 2473 # indNEEJ = numpy.where(~indOver)[0]
2475 2474 #
2476 2475 # boolMetFin = boolMet1
2477 2476 #
2478 2477 # if indEEJ.size > 0:
2479 2478 # boolMet1[:,indEEJ] = False #Erase heights with EEJ
2480 2479 #
2481 2480 # boolMet2 = coh > cohThresh
2482 2481 # boolMet2 = self.__erase_small(boolMet2, 2*sec,5)
2483 2482 #
2484 2483 # #Final Meteor mask
2485 2484 # boolMetFin = boolMet1|boolMet2
2486 2485
2487 2486 #Coherence mask
2488 2487 boolMet1 = coh > 0.75
2489 2488 struc = numpy.ones((30,1))
2490 2489 boolMet1 = ndimage.morphology.binary_dilation(boolMet1, structure=struc)
2491 2490
2492 2491 #Derivative mask
2493 2492 derPhase = numpy.nanmean(numpy.abs(phase[:,1:,:] - phase[:,:-1,:]),axis=0)
2494 2493 boolMet2 = derPhase < 0.2
2495 2494 # boolMet2 = ndimage.morphology.binary_opening(boolMet2)
2496 2495 # boolMet2 = ndimage.morphology.binary_closing(boolMet2, structure = numpy.ones((10,1)))
2497 2496 boolMet2 = ndimage.median_filter(boolMet2,size=5)
2498 2497 boolMet2 = numpy.vstack((boolMet2,numpy.full((1,nHeights), True, dtype=bool)))
2499 2498 # #Final mask
2500 2499 # boolMetFin = boolMet2
2501 2500 boolMetFin = boolMet1&boolMet2
2502 2501 # boolMetFin = ndimage.morphology.binary_dilation(boolMetFin)
2503 2502 #Creating data_param
2504 2503 coordMet = numpy.where(boolMetFin)
2505 2504
2506 2505 tmet = coordMet[0]
2507 2506 hmet = coordMet[1]
2508 2507
2509 2508 data_param = numpy.zeros((tmet.size, 6 + nPairs))
2510 2509 data_param[:,0] = utctime
2511 2510 data_param[:,1] = tmet
2512 2511 data_param[:,2] = hmet
2513 2512 data_param[:,3] = SNRm[tmet,hmet]
2514 2513 data_param[:,4] = velRad[tmet,hmet]
2515 2514 data_param[:,5] = coh[tmet,hmet]
2516 2515 data_param[:,6:] = phase[:,tmet,hmet].T
2517 2516
2518 2517 elif mode == 'DBS':
2519 2518 dataOut.groupList = numpy.arange(nChannels)
2520 2519
2521 2520 #Radial Velocities
2522 2521 phase = numpy.angle(data_acf[:,1,:,:])
2523 2522 # phase = ndimage.median_filter(numpy.angle(data_acf[:,1,:,:]), size = (1,5,1))
2524 2523 velRad = phase*lamb/(4*numpy.pi*tSamp)
2525 2524
2526 2525 #Spectral width
2527 2526 # acf1 = ndimage.median_filter(numpy.abs(data_acf[:,1,:,:]), size = (1,5,1))
2528 2527 # acf2 = ndimage.median_filter(numpy.abs(data_acf[:,2,:,:]), size = (1,5,1))
2529 2528 acf1 = data_acf[:,1,:,:]
2530 2529 acf2 = data_acf[:,2,:,:]
2531 2530
2532 2531 spcWidth = (lamb/(2*numpy.sqrt(6)*numpy.pi*tSamp))*numpy.sqrt(numpy.log(acf1/acf2))
2533 2532 # velRad = ndimage.median_filter(velRad, size = (1,5,1))
2534 2533 if allData:
2535 2534 boolMetFin = ~numpy.isnan(SNRdB)
2536 2535 else:
2537 2536 #SNR
2538 2537 boolMet1 = (SNRdB>SNRthresh) #SNR mask
2539 2538 boolMet1 = ndimage.median_filter(boolMet1, size=(1,5,5))
2540 2539
2541 2540 #Radial velocity
2542 2541 boolMet2 = numpy.abs(velRad) < 20
2543 2542 boolMet2 = ndimage.median_filter(boolMet2, (1,5,5))
2544 2543
2545 2544 #Spectral Width
2546 2545 boolMet3 = spcWidth < 30
2547 2546 boolMet3 = ndimage.median_filter(boolMet3, (1,5,5))
2548 2547 # boolMetFin = self.__erase_small(boolMet1, 10,5)
2549 2548 boolMetFin = boolMet1&boolMet2&boolMet3
2550 2549
2551 2550 #Creating data_param
2552 2551 coordMet = numpy.where(boolMetFin)
2553 2552
2554 2553 cmet = coordMet[0]
2555 2554 tmet = coordMet[1]
2556 2555 hmet = coordMet[2]
2557 2556
2558 2557 data_param = numpy.zeros((tmet.size, 7))
2559 2558 data_param[:,0] = utctime
2560 2559 data_param[:,1] = cmet
2561 2560 data_param[:,2] = tmet
2562 2561 data_param[:,3] = hmet
2563 2562 data_param[:,4] = SNR[cmet,tmet,hmet].T
2564 2563 data_param[:,5] = velRad[cmet,tmet,hmet].T
2565 2564 data_param[:,6] = spcWidth[cmet,tmet,hmet].T
2566 2565
2567 2566 # self.dataOut.data_param = data_int
2568 2567 if len(data_param) == 0:
2569 2568 dataOut.flagNoData = True
2570 2569 else:
2571 2570 dataOut.data_param = data_param
2572 2571
2573 2572 def __erase_small(self, binArray, threshX, threshY):
2574 2573 labarray, numfeat = ndimage.measurements.label(binArray)
2575 2574 binArray1 = numpy.copy(binArray)
2576 2575
2577 2576 for i in range(1,numfeat + 1):
2578 2577 auxBin = (labarray==i)
2579 2578 auxSize = auxBin.sum()
2580 2579
2581 2580 x,y = numpy.where(auxBin)
2582 2581 widthX = x.max() - x.min()
2583 2582 widthY = y.max() - y.min()
2584 2583
2585 2584 #width X: 3 seg -> 12.5*3
2586 2585 #width Y:
2587 2586
2588 2587 if (auxSize < 50) or (widthX < threshX) or (widthY < threshY):
2589 2588 binArray1[auxBin] = False
2590 2589
2591 2590 return binArray1
2592 2591
2593 2592 #--------------- Specular Meteor ----------------
2594 2593
2595 2594 class SMDetection(Operation):
2596 2595 '''
2597 2596 Function DetectMeteors()
2598 2597 Project developed with paper:
2599 2598 HOLDSWORTH ET AL. 2004
2600 2599
2601 2600 Input:
2602 2601 self.dataOut.data_pre
2603 2602
2604 2603 centerReceiverIndex: From the channels, which is the center receiver
2605 2604
2606 2605 hei_ref: Height reference for the Beacon signal extraction
2607 2606 tauindex:
2608 2607 predefinedPhaseShifts: Predefined phase offset for the voltge signals
2609 2608
2610 2609 cohDetection: Whether to user Coherent detection or not
2611 2610 cohDet_timeStep: Coherent Detection calculation time step
2612 2611 cohDet_thresh: Coherent Detection phase threshold to correct phases
2613 2612
2614 2613 noise_timeStep: Noise calculation time step
2615 2614 noise_multiple: Noise multiple to define signal threshold
2616 2615
2617 2616 multDet_timeLimit: Multiple Detection Removal time limit in seconds
2618 2617 multDet_rangeLimit: Multiple Detection Removal range limit in km
2619 2618
2620 2619 phaseThresh: Maximum phase difference between receiver to be consider a meteor
2621 2620 SNRThresh: Minimum SNR threshold of the meteor signal to be consider a meteor
2622 2621
2623 2622 hmin: Minimum Height of the meteor to use it in the further wind estimations
2624 2623 hmax: Maximum Height of the meteor to use it in the further wind estimations
2625 2624 azimuth: Azimuth angle correction
2626 2625
2627 2626 Affected:
2628 2627 self.dataOut.data_param
2629 2628
2630 2629 Rejection Criteria (Errors):
2631 2630 0: No error; analysis OK
2632 2631 1: SNR < SNR threshold
2633 2632 2: angle of arrival (AOA) ambiguously determined
2634 2633 3: AOA estimate not feasible
2635 2634 4: Large difference in AOAs obtained from different antenna baselines
2636 2635 5: echo at start or end of time series
2637 2636 6: echo less than 5 examples long; too short for analysis
2638 2637 7: echo rise exceeds 0.3s
2639 2638 8: echo decay time less than twice rise time
2640 2639 9: large power level before echo
2641 2640 10: large power level after echo
2642 2641 11: poor fit to amplitude for estimation of decay time
2643 2642 12: poor fit to CCF phase variation for estimation of radial drift velocity
2644 2643 13: height unresolvable echo: not valid height within 70 to 110 km
2645 2644 14: height ambiguous echo: more then one possible height within 70 to 110 km
2646 2645 15: radial drift velocity or projected horizontal velocity exceeds 200 m/s
2647 2646 16: oscilatory echo, indicating event most likely not an underdense echo
2648 2647
2649 2648 17: phase difference in meteor Reestimation
2650 2649
2651 2650 Data Storage:
2652 2651 Meteors for Wind Estimation (8):
2653 2652 Utc Time | Range Height
2654 2653 Azimuth Zenith errorCosDir
2655 2654 VelRad errorVelRad
2656 2655 Phase0 Phase1 Phase2 Phase3
2657 2656 TypeError
2658 2657
2659 2658 '''
2660 2659
2661 2660 def run(self, dataOut, hei_ref = None, tauindex = 0,
2662 2661 phaseOffsets = None,
2663 2662 cohDetection = False, cohDet_timeStep = 1, cohDet_thresh = 25,
2664 2663 noise_timeStep = 4, noise_multiple = 4,
2665 2664 multDet_timeLimit = 1, multDet_rangeLimit = 3,
2666 2665 phaseThresh = 20, SNRThresh = 5,
2667 2666 hmin = 50, hmax=150, azimuth = 0,
2668 2667 channelPositions = None) :
2669 2668
2670 2669
2671 2670 #Getting Pairslist
2672 2671 if channelPositions is None:
2673 2672 # channelPositions = [(2.5,0), (0,2.5), (0,0), (0,4.5), (-2,0)] #T
2674 2673 channelPositions = [(4.5,2), (2,4.5), (2,2), (2,0), (0,2)] #Estrella
2675 2674 meteorOps = SMOperations()
2676 2675 pairslist0, distances = meteorOps.getPhasePairs(channelPositions)
2677 2676 heiRang = dataOut.heightList
2678 2677 #Get Beacon signal - No Beacon signal anymore
2679 2678 # newheis = numpy.where(self.dataOut.heightList>self.dataOut.radarControllerHeaderObj.Taus[tauindex])
2680 2679 #
2681 2680 # if hei_ref != None:
2682 2681 # newheis = numpy.where(self.dataOut.heightList>hei_ref)
2683 2682 #
2684 2683
2685 2684
2686 2685 #****************REMOVING HARDWARE PHASE DIFFERENCES***************
2687 2686 # see if the user put in pre defined phase shifts
2688 2687 voltsPShift = dataOut.data_pre.copy()
2689 2688
2690 2689 # if predefinedPhaseShifts != None:
2691 2690 # hardwarePhaseShifts = numpy.array(predefinedPhaseShifts)*numpy.pi/180
2692 2691 #
2693 2692 # # elif beaconPhaseShifts:
2694 2693 # # #get hardware phase shifts using beacon signal
2695 2694 # # hardwarePhaseShifts = self.__getHardwarePhaseDiff(self.dataOut.data_pre, pairslist, newheis, 10)
2696 2695 # # hardwarePhaseShifts = numpy.insert(hardwarePhaseShifts,centerReceiverIndex,0)
2697 2696 #
2698 2697 # else:
2699 2698 # hardwarePhaseShifts = numpy.zeros(5)
2700 2699 #
2701 2700 # voltsPShift = numpy.zeros((self.dataOut.data_pre.shape[0],self.dataOut.data_pre.shape[1],self.dataOut.data_pre.shape[2]), dtype = 'complex')
2702 2701 # for i in range(self.dataOut.data_pre.shape[0]):
2703 2702 # voltsPShift[i,:,:] = self.__shiftPhase(self.dataOut.data_pre[i,:,:], hardwarePhaseShifts[i])
2704 2703
2705 2704 #******************END OF REMOVING HARDWARE PHASE DIFFERENCES*********
2706 2705
2707 2706 #Remove DC
2708 2707 voltsDC = numpy.mean(voltsPShift,1)
2709 2708 voltsDC = numpy.mean(voltsDC,1)
2710 2709 for i in range(voltsDC.shape[0]):
2711 2710 voltsPShift[i] = voltsPShift[i] - voltsDC[i]
2712 2711
2713 2712 #Don't considerate last heights, theyre used to calculate Hardware Phase Shift
2714 2713 # voltsPShift = voltsPShift[:,:,:newheis[0][0]]
2715 2714
2716 2715 #************ FIND POWER OF DATA W/COH OR NON COH DETECTION (3.4) **********
2717 2716 #Coherent Detection
2718 2717 if cohDetection:
2719 2718 #use coherent detection to get the net power
2720 2719 cohDet_thresh = cohDet_thresh*numpy.pi/180
2721 2720 voltsPShift = self.__coherentDetection(voltsPShift, cohDet_timeStep, dataOut.timeInterval, pairslist0, cohDet_thresh)
2722 2721
2723 2722 #Non-coherent detection!
2724 2723 powerNet = numpy.nansum(numpy.abs(voltsPShift[:,:,:])**2,0)
2725 2724 #********** END OF COH/NON-COH POWER CALCULATION**********************
2726 2725
2727 2726 #********** FIND THE NOISE LEVEL AND POSSIBLE METEORS ****************
2728 2727 #Get noise
2729 2728 noise, noise1 = self.__getNoise(powerNet, noise_timeStep, dataOut.timeInterval)
2730 2729 # noise = self.getNoise1(powerNet, noise_timeStep, self.dataOut.timeInterval)
2731 2730 #Get signal threshold
2732 2731 signalThresh = noise_multiple*noise
2733 2732 #Meteor echoes detection
2734 2733 listMeteors = self.__findMeteors(powerNet, signalThresh)
2735 2734 #******* END OF NOISE LEVEL AND POSSIBLE METEORS CACULATION **********
2736 2735
2737 2736 #************** REMOVE MULTIPLE DETECTIONS (3.5) ***************************
2738 2737 #Parameters
2739 2738 heiRange = dataOut.heightList
2740 2739 rangeInterval = heiRange[1] - heiRange[0]
2741 2740 rangeLimit = multDet_rangeLimit/rangeInterval
2742 2741 timeLimit = multDet_timeLimit/dataOut.timeInterval
2743 2742 #Multiple detection removals
2744 2743 listMeteors1 = self.__removeMultipleDetections(listMeteors, rangeLimit, timeLimit)
2745 2744 #************ END OF REMOVE MULTIPLE DETECTIONS **********************
2746 2745
2747 2746 #********************* METEOR REESTIMATION (3.7, 3.8, 3.9, 3.10) ********************
2748 2747 #Parameters
2749 2748 phaseThresh = phaseThresh*numpy.pi/180
2750 2749 thresh = [phaseThresh, noise_multiple, SNRThresh]
2751 2750 #Meteor reestimation (Errors N 1, 6, 12, 17)
2752 2751 listMeteors2, listMeteorsPower, listMeteorsVolts = self.__meteorReestimation(listMeteors1, voltsPShift, pairslist0, thresh, noise, dataOut.timeInterval, dataOut.frequency)
2753 2752 # listMeteors2, listMeteorsPower, listMeteorsVolts = self.meteorReestimation3(listMeteors2, listMeteorsPower, listMeteorsVolts, voltsPShift, pairslist, thresh, noise)
2754 2753 #Estimation of decay times (Errors N 7, 8, 11)
2755 2754 listMeteors3 = self.__estimateDecayTime(listMeteors2, listMeteorsPower, dataOut.timeInterval, dataOut.frequency)
2756 2755 #******************* END OF METEOR REESTIMATION *******************
2757 2756
2758 2757 #********************* METEOR PARAMETERS CALCULATION (3.11, 3.12, 3.13) **************************
2759 2758 #Calculating Radial Velocity (Error N 15)
2760 2759 radialStdThresh = 10
2761 2760 listMeteors4 = self.__getRadialVelocity(listMeteors3, listMeteorsVolts, radialStdThresh, pairslist0, dataOut.timeInterval)
2762 2761
2763 2762 if len(listMeteors4) > 0:
2764 2763 #Setting New Array
2765 2764 date = dataOut.utctime
2766 2765 arrayParameters = self.__setNewArrays(listMeteors4, date, heiRang)
2767 2766
2768 2767 #Correcting phase offset
2769 2768 if phaseOffsets != None:
2770 2769 phaseOffsets = numpy.array(phaseOffsets)*numpy.pi/180
2771 2770 arrayParameters[:,8:12] = numpy.unwrap(arrayParameters[:,8:12] + phaseOffsets)
2772 2771
2773 2772 #Second Pairslist
2774 2773 pairsList = []
2775 2774 pairx = (0,1)
2776 2775 pairy = (2,3)
2777 2776 pairsList.append(pairx)
2778 2777 pairsList.append(pairy)
2779 2778
2780 2779 jph = numpy.array([0,0,0,0])
2781 2780 h = (hmin,hmax)
2782 2781 arrayParameters = meteorOps.getMeteorParams(arrayParameters, azimuth, h, pairsList, distances, jph)
2783 2782
2784 2783 # #Calculate AOA (Error N 3, 4)
2785 2784 # #JONES ET AL. 1998
2786 2785 # error = arrayParameters[:,-1]
2787 2786 # AOAthresh = numpy.pi/8
2788 2787 # phases = -arrayParameters[:,9:13]
2789 2788 # arrayParameters[:,4:7], arrayParameters[:,-1] = meteorOps.getAOA(phases, pairsList, error, AOAthresh, azimuth)
2790 2789 #
2791 2790 # #Calculate Heights (Error N 13 and 14)
2792 2791 # error = arrayParameters[:,-1]
2793 2792 # Ranges = arrayParameters[:,2]
2794 2793 # zenith = arrayParameters[:,5]
2795 2794 # arrayParameters[:,3], arrayParameters[:,-1] = meteorOps.getHeights(Ranges, zenith, error, hmin, hmax)
2796 2795 # error = arrayParameters[:,-1]
2797 2796 #********************* END OF PARAMETERS CALCULATION **************************
2798 2797
2799 2798 #***************************+ PASS DATA TO NEXT STEP **********************
2800 2799 # arrayFinal = arrayParameters.reshape((1,arrayParameters.shape[0],arrayParameters.shape[1]))
2801 2800 dataOut.data_param = arrayParameters
2802 2801
2803 2802 if arrayParameters is None:
2804 2803 dataOut.flagNoData = True
2805 2804 else:
2806 2805 dataOut.flagNoData = True
2807 2806
2808 2807 return
2809 2808
2810 2809 def __getHardwarePhaseDiff(self, voltage0, pairslist, newheis, n):
2811 2810
2812 2811 minIndex = min(newheis[0])
2813 2812 maxIndex = max(newheis[0])
2814 2813
2815 2814 voltage = voltage0[:,:,minIndex:maxIndex+1]
2816 2815 nLength = voltage.shape[1]/n
2817 2816 nMin = 0
2818 2817 nMax = 0
2819 2818 phaseOffset = numpy.zeros((len(pairslist),n))
2820 2819
2821 2820 for i in range(n):
2822 2821 nMax += nLength
2823 2822 phaseCCF = -numpy.angle(self.__calculateCCF(voltage[:,nMin:nMax,:], pairslist, [0]))
2824 2823 phaseCCF = numpy.mean(phaseCCF, axis = 2)
2825 2824 phaseOffset[:,i] = phaseCCF.transpose()
2826 2825 nMin = nMax
2827 2826 # phaseDiff, phaseArrival = self.estimatePhaseDifference(voltage, pairslist)
2828 2827
2829 2828 #Remove Outliers
2830 2829 factor = 2
2831 2830 wt = phaseOffset - signal.medfilt(phaseOffset,(1,5))
2832 2831 dw = numpy.std(wt,axis = 1)
2833 2832 dw = dw.reshape((dw.size,1))
2834 2833 ind = numpy.where(numpy.logical_or(wt>dw*factor,wt<-dw*factor))
2835 2834 phaseOffset[ind] = numpy.nan
2836 2835 phaseOffset = stats.nanmean(phaseOffset, axis=1)
2837 2836
2838 2837 return phaseOffset
2839 2838
2840 2839 def __shiftPhase(self, data, phaseShift):
2841 2840 #this will shift the phase of a complex number
2842 2841 dataShifted = numpy.abs(data) * numpy.exp((numpy.angle(data)+phaseShift)*1j)
2843 2842 return dataShifted
2844 2843
2845 2844 def __estimatePhaseDifference(self, array, pairslist):
2846 2845 nChannel = array.shape[0]
2847 2846 nHeights = array.shape[2]
2848 2847 numPairs = len(pairslist)
2849 2848 # phaseCCF = numpy.zeros((nChannel, 5, nHeights))
2850 2849 phaseCCF = numpy.angle(self.__calculateCCF(array, pairslist, [-2,-1,0,1,2]))
2851 2850
2852 2851 #Correct phases
2853 2852 derPhaseCCF = phaseCCF[:,1:,:] - phaseCCF[:,0:-1,:]
2854 2853 indDer = numpy.where(numpy.abs(derPhaseCCF) > numpy.pi)
2855 2854
2856 2855 if indDer[0].shape[0] > 0:
2857 2856 for i in range(indDer[0].shape[0]):
2858 2857 signo = -numpy.sign(derPhaseCCF[indDer[0][i],indDer[1][i],indDer[2][i]])
2859 2858 phaseCCF[indDer[0][i],indDer[1][i]+1:,:] += signo*2*numpy.pi
2860 2859
2861 2860 # for j in range(numSides):
2862 2861 # phaseCCFAux = self.calculateCCF(arrayCenter, arraySides[j,:,:], [-2,1,0,1,2])
2863 2862 # phaseCCF[j,:,:] = numpy.angle(phaseCCFAux)
2864 2863 #
2865 2864 #Linear
2866 2865 phaseInt = numpy.zeros((numPairs,1))
2867 2866 angAllCCF = phaseCCF[:,[0,1,3,4],0]
2868 2867 for j in range(numPairs):
2869 2868 fit = stats.linregress([-2,-1,1,2],angAllCCF[j,:])
2870 2869 phaseInt[j] = fit[1]
2871 2870 #Phase Differences
2872 2871 phaseDiff = phaseInt - phaseCCF[:,2,:]
2873 2872 phaseArrival = phaseInt.reshape(phaseInt.size)
2874 2873
2875 2874 #Dealias
2876 2875 phaseArrival = numpy.angle(numpy.exp(1j*phaseArrival))
2877 2876 # indAlias = numpy.where(phaseArrival > numpy.pi)
2878 2877 # phaseArrival[indAlias] -= 2*numpy.pi
2879 2878 # indAlias = numpy.where(phaseArrival < -numpy.pi)
2880 2879 # phaseArrival[indAlias] += 2*numpy.pi
2881 2880
2882 2881 return phaseDiff, phaseArrival
2883 2882
2884 2883 def __coherentDetection(self, volts, timeSegment, timeInterval, pairslist, thresh):
2885 2884 #this function will run the coherent detection used in Holdworth et al. 2004 and return the net power
2886 2885 #find the phase shifts of each channel over 1 second intervals
2887 2886 #only look at ranges below the beacon signal
2888 2887 numProfPerBlock = numpy.ceil(timeSegment/timeInterval)
2889 2888 numBlocks = int(volts.shape[1]/numProfPerBlock)
2890 2889 numHeights = volts.shape[2]
2891 2890 nChannel = volts.shape[0]
2892 2891 voltsCohDet = volts.copy()
2893 2892
2894 2893 pairsarray = numpy.array(pairslist)
2895 2894 indSides = pairsarray[:,1]
2896 2895 # indSides = numpy.array(range(nChannel))
2897 2896 # indSides = numpy.delete(indSides, indCenter)
2898 2897 #
2899 2898 # listCenter = numpy.array_split(volts[indCenter,:,:], numBlocks, 0)
2900 2899 listBlocks = numpy.array_split(volts, numBlocks, 1)
2901 2900
2902 2901 startInd = 0
2903 2902 endInd = 0
2904 2903
2905 2904 for i in range(numBlocks):
2906 2905 startInd = endInd
2907 2906 endInd = endInd + listBlocks[i].shape[1]
2908 2907
2909 2908 arrayBlock = listBlocks[i]
2910 2909 # arrayBlockCenter = listCenter[i]
2911 2910
2912 2911 #Estimate the Phase Difference
2913 2912 phaseDiff, aux = self.__estimatePhaseDifference(arrayBlock, pairslist)
2914 2913 #Phase Difference RMS
2915 2914 arrayPhaseRMS = numpy.abs(phaseDiff)
2916 2915 phaseRMSaux = numpy.sum(arrayPhaseRMS < thresh,0)
2917 2916 indPhase = numpy.where(phaseRMSaux==4)
2918 2917 #Shifting
2919 2918 if indPhase[0].shape[0] > 0:
2920 2919 for j in range(indSides.size):
2921 2920 arrayBlock[indSides[j],:,indPhase] = self.__shiftPhase(arrayBlock[indSides[j],:,indPhase], phaseDiff[j,indPhase].transpose())
2922 2921 voltsCohDet[:,startInd:endInd,:] = arrayBlock
2923 2922
2924 2923 return voltsCohDet
2925 2924
2926 2925 def __calculateCCF(self, volts, pairslist ,laglist):
2927 2926
2928 2927 nHeights = volts.shape[2]
2929 2928 nPoints = volts.shape[1]
2930 2929 voltsCCF = numpy.zeros((len(pairslist), len(laglist), nHeights),dtype = 'complex')
2931 2930
2932 2931 for i in range(len(pairslist)):
2933 2932 volts1 = volts[pairslist[i][0]]
2934 2933 volts2 = volts[pairslist[i][1]]
2935 2934
2936 2935 for t in range(len(laglist)):
2937 2936 idxT = laglist[t]
2938 2937 if idxT >= 0:
2939 2938 vStacked = numpy.vstack((volts2[idxT:,:],
2940 2939 numpy.zeros((idxT, nHeights),dtype='complex')))
2941 2940 else:
2942 2941 vStacked = numpy.vstack((numpy.zeros((-idxT, nHeights),dtype='complex'),
2943 2942 volts2[:(nPoints + idxT),:]))
2944 2943 voltsCCF[i,t,:] = numpy.sum((numpy.conjugate(volts1)*vStacked),axis=0)
2945 2944
2946 2945 vStacked = None
2947 2946 return voltsCCF
2948 2947
2949 2948 def __getNoise(self, power, timeSegment, timeInterval):
2950 2949 numProfPerBlock = numpy.ceil(timeSegment/timeInterval)
2951 2950 numBlocks = int(power.shape[0]/numProfPerBlock)
2952 2951 numHeights = power.shape[1]
2953 2952
2954 2953 listPower = numpy.array_split(power, numBlocks, 0)
2955 2954 noise = numpy.zeros((power.shape[0], power.shape[1]))
2956 2955 noise1 = numpy.zeros((power.shape[0], power.shape[1]))
2957 2956
2958 2957 startInd = 0
2959 2958 endInd = 0
2960 2959
2961 2960 for i in range(numBlocks): #split por canal
2962 2961 startInd = endInd
2963 2962 endInd = endInd + listPower[i].shape[0]
2964 2963
2965 2964 arrayBlock = listPower[i]
2966 2965 noiseAux = numpy.mean(arrayBlock, 0)
2967 2966 # noiseAux = numpy.median(noiseAux)
2968 2967 # noiseAux = numpy.mean(arrayBlock)
2969 2968 noise[startInd:endInd,:] = noise[startInd:endInd,:] + noiseAux
2970 2969
2971 2970 noiseAux1 = numpy.mean(arrayBlock)
2972 2971 noise1[startInd:endInd,:] = noise1[startInd:endInd,:] + noiseAux1
2973 2972
2974 2973 return noise, noise1
2975 2974
2976 2975 def __findMeteors(self, power, thresh):
2977 2976 nProf = power.shape[0]
2978 2977 nHeights = power.shape[1]
2979 2978 listMeteors = []
2980 2979
2981 2980 for i in range(nHeights):
2982 2981 powerAux = power[:,i]
2983 2982 threshAux = thresh[:,i]
2984 2983
2985 2984 indUPthresh = numpy.where(powerAux > threshAux)[0]
2986 2985 indDNthresh = numpy.where(powerAux <= threshAux)[0]
2987 2986
2988 2987 j = 0
2989 2988
2990 2989 while (j < indUPthresh.size - 2):
2991 2990 if (indUPthresh[j + 2] == indUPthresh[j] + 2):
2992 2991 indDNAux = numpy.where(indDNthresh > indUPthresh[j])
2993 2992 indDNthresh = indDNthresh[indDNAux]
2994 2993
2995 2994 if (indDNthresh.size > 0):
2996 2995 indEnd = indDNthresh[0] - 1
2997 2996 indInit = indUPthresh[j]
2998 2997
2999 2998 meteor = powerAux[indInit:indEnd + 1]
3000 2999 indPeak = meteor.argmax() + indInit
3001 3000 FLA = sum(numpy.conj(meteor)*numpy.hstack((meteor[1:],0)))
3002 3001
3003 3002 listMeteors.append(numpy.array([i,indInit,indPeak,indEnd,FLA])) #CHEQUEAR!!!!!
3004 3003 j = numpy.where(indUPthresh == indEnd)[0] + 1
3005 3004 else: j+=1
3006 3005 else: j+=1
3007 3006
3008 3007 return listMeteors
3009 3008
3010 3009 def __removeMultipleDetections(self,listMeteors, rangeLimit, timeLimit):
3011 3010
3012 3011 arrayMeteors = numpy.asarray(listMeteors)
3013 3012 listMeteors1 = []
3014 3013
3015 3014 while arrayMeteors.shape[0] > 0:
3016 3015 FLAs = arrayMeteors[:,4]
3017 3016 maxFLA = FLAs.argmax()
3018 3017 listMeteors1.append(arrayMeteors[maxFLA,:])
3019 3018
3020 3019 MeteorInitTime = arrayMeteors[maxFLA,1]
3021 3020 MeteorEndTime = arrayMeteors[maxFLA,3]
3022 3021 MeteorHeight = arrayMeteors[maxFLA,0]
3023 3022
3024 3023 #Check neighborhood
3025 3024 maxHeightIndex = MeteorHeight + rangeLimit
3026 3025 minHeightIndex = MeteorHeight - rangeLimit
3027 3026 minTimeIndex = MeteorInitTime - timeLimit
3028 3027 maxTimeIndex = MeteorEndTime + timeLimit
3029 3028
3030 3029 #Check Heights
3031 3030 indHeight = numpy.logical_and(arrayMeteors[:,0] >= minHeightIndex, arrayMeteors[:,0] <= maxHeightIndex)
3032 3031 indTime = numpy.logical_and(arrayMeteors[:,3] >= minTimeIndex, arrayMeteors[:,1] <= maxTimeIndex)
3033 3032 indBoth = numpy.where(numpy.logical_and(indTime,indHeight))
3034 3033
3035 3034 arrayMeteors = numpy.delete(arrayMeteors, indBoth, axis = 0)
3036 3035
3037 3036 return listMeteors1
3038 3037
3039 3038 def __meteorReestimation(self, listMeteors, volts, pairslist, thresh, noise, timeInterval,frequency):
3040 3039 numHeights = volts.shape[2]
3041 3040 nChannel = volts.shape[0]
3042 3041
3043 3042 thresholdPhase = thresh[0]
3044 3043 thresholdNoise = thresh[1]
3045 3044 thresholdDB = float(thresh[2])
3046 3045
3047 3046 thresholdDB1 = 10**(thresholdDB/10)
3048 3047 pairsarray = numpy.array(pairslist)
3049 3048 indSides = pairsarray[:,1]
3050 3049
3051 3050 pairslist1 = list(pairslist)
3052 3051 pairslist1.append((0,1))
3053 3052 pairslist1.append((3,4))
3054 3053
3055 3054 listMeteors1 = []
3056 3055 listPowerSeries = []
3057 3056 listVoltageSeries = []
3058 3057 #volts has the war data
3059 3058
3060 3059 if frequency == 30e6:
3061 3060 timeLag = 45*10**-3
3062 3061 else:
3063 3062 timeLag = 15*10**-3
3064 3063 lag = numpy.ceil(timeLag/timeInterval)
3065 3064
3066 3065 for i in range(len(listMeteors)):
3067 3066
3068 3067 ###################### 3.6 - 3.7 PARAMETERS REESTIMATION #########################
3069 3068 meteorAux = numpy.zeros(16)
3070 3069
3071 3070 #Loading meteor Data (mHeight, mStart, mPeak, mEnd)
3072 3071 mHeight = listMeteors[i][0]
3073 3072 mStart = listMeteors[i][1]
3074 3073 mPeak = listMeteors[i][2]
3075 3074 mEnd = listMeteors[i][3]
3076 3075
3077 3076 #get the volt data between the start and end times of the meteor
3078 3077 meteorVolts = volts[:,mStart:mEnd+1,mHeight]
3079 3078 meteorVolts = meteorVolts.reshape(meteorVolts.shape[0], meteorVolts.shape[1], 1)
3080 3079
3081 3080 #3.6. Phase Difference estimation
3082 3081 phaseDiff, aux = self.__estimatePhaseDifference(meteorVolts, pairslist)
3083 3082
3084 3083 #3.7. Phase difference removal & meteor start, peak and end times reestimated
3085 3084 #meteorVolts0.- all Channels, all Profiles
3086 3085 meteorVolts0 = volts[:,:,mHeight]
3087 3086 meteorThresh = noise[:,mHeight]*thresholdNoise
3088 3087 meteorNoise = noise[:,mHeight]
3089 3088 meteorVolts0[indSides,:] = self.__shiftPhase(meteorVolts0[indSides,:], phaseDiff) #Phase Shifting
3090 3089 powerNet0 = numpy.nansum(numpy.abs(meteorVolts0)**2, axis = 0) #Power
3091 3090
3092 3091 #Times reestimation
3093 3092 mStart1 = numpy.where(powerNet0[:mPeak] < meteorThresh[:mPeak])[0]
3094 3093 if mStart1.size > 0:
3095 3094 mStart1 = mStart1[-1] + 1
3096 3095
3097 3096 else:
3098 3097 mStart1 = mPeak
3099 3098
3100 3099 mEnd1 = numpy.where(powerNet0[mPeak:] < meteorThresh[mPeak:])[0][0] + mPeak - 1
3101 3100 mEndDecayTime1 = numpy.where(powerNet0[mPeak:] < meteorNoise[mPeak:])[0]
3102 3101 if mEndDecayTime1.size == 0:
3103 3102 mEndDecayTime1 = powerNet0.size
3104 3103 else:
3105 3104 mEndDecayTime1 = mEndDecayTime1[0] + mPeak - 1
3106 3105 # mPeak1 = meteorVolts0[mStart1:mEnd1 + 1].argmax()
3107 3106
3108 3107 #meteorVolts1.- all Channels, from start to end
3109 3108 meteorVolts1 = meteorVolts0[:,mStart1:mEnd1 + 1]
3110 3109 meteorVolts2 = meteorVolts0[:,mPeak + lag:mEnd1 + 1]
3111 3110 if meteorVolts2.shape[1] == 0:
3112 3111 meteorVolts2 = meteorVolts0[:,mPeak:mEnd1 + 1]
3113 3112 meteorVolts1 = meteorVolts1.reshape(meteorVolts1.shape[0], meteorVolts1.shape[1], 1)
3114 3113 meteorVolts2 = meteorVolts2.reshape(meteorVolts2.shape[0], meteorVolts2.shape[1], 1)
3115 3114 ##################### END PARAMETERS REESTIMATION #########################
3116 3115
3117 3116 ##################### 3.8 PHASE DIFFERENCE REESTIMATION ########################
3118 3117 # if mEnd1 - mStart1 > 4: #Error Number 6: echo less than 5 samples long; too short for analysis
3119 3118 if meteorVolts2.shape[1] > 0:
3120 3119 #Phase Difference re-estimation
3121 3120 phaseDiff1, phaseDiffint = self.__estimatePhaseDifference(meteorVolts2, pairslist1) #Phase Difference Estimation
3122 3121 # phaseDiff1, phaseDiffint = self.estimatePhaseDifference(meteorVolts2, pairslist)
3123 3122 meteorVolts2 = meteorVolts2.reshape(meteorVolts2.shape[0], meteorVolts2.shape[1])
3124 3123 phaseDiff11 = numpy.reshape(phaseDiff1, (phaseDiff1.shape[0],1))
3125 3124 meteorVolts2[indSides,:] = self.__shiftPhase(meteorVolts2[indSides,:], phaseDiff11[0:4]) #Phase Shifting
3126 3125
3127 3126 #Phase Difference RMS
3128 3127 phaseRMS1 = numpy.sqrt(numpy.mean(numpy.square(phaseDiff1)))
3129 3128 powerNet1 = numpy.nansum(numpy.abs(meteorVolts1[:,:])**2,0)
3130 3129 #Data from Meteor
3131 3130 mPeak1 = powerNet1.argmax() + mStart1
3132 3131 mPeakPower1 = powerNet1.max()
3133 3132 noiseAux = sum(noise[mStart1:mEnd1 + 1,mHeight])
3134 3133 mSNR1 = (sum(powerNet1)-noiseAux)/noiseAux
3135 3134 Meteor1 = numpy.array([mHeight, mStart1, mPeak1, mEnd1, mPeakPower1, mSNR1, phaseRMS1])
3136 3135 Meteor1 = numpy.hstack((Meteor1,phaseDiffint))
3137 3136 PowerSeries = powerNet0[mStart1:mEndDecayTime1 + 1]
3138 3137 #Vectorize
3139 3138 meteorAux[0:7] = [mHeight, mStart1, mPeak1, mEnd1, mPeakPower1, mSNR1, phaseRMS1]
3140 3139 meteorAux[7:11] = phaseDiffint[0:4]
3141 3140
3142 3141 #Rejection Criterions
3143 3142 if phaseRMS1 > thresholdPhase: #Error Number 17: Phase variation
3144 3143 meteorAux[-1] = 17
3145 3144 elif mSNR1 < thresholdDB1: #Error Number 1: SNR < threshold dB
3146 3145 meteorAux[-1] = 1
3147 3146
3148 3147
3149 3148 else:
3150 3149 meteorAux[0:4] = [mHeight, mStart, mPeak, mEnd]
3151 3150 meteorAux[-1] = 6 #Error Number 6: echo less than 5 samples long; too short for analysis
3152 3151 PowerSeries = 0
3153 3152
3154 3153 listMeteors1.append(meteorAux)
3155 3154 listPowerSeries.append(PowerSeries)
3156 3155 listVoltageSeries.append(meteorVolts1)
3157 3156
3158 3157 return listMeteors1, listPowerSeries, listVoltageSeries
3159 3158
3160 3159 def __estimateDecayTime(self, listMeteors, listPower, timeInterval, frequency):
3161 3160
3162 3161 threshError = 10
3163 3162 #Depending if it is 30 or 50 MHz
3164 3163 if frequency == 30e6:
3165 3164 timeLag = 45*10**-3
3166 3165 else:
3167 3166 timeLag = 15*10**-3
3168 3167 lag = numpy.ceil(timeLag/timeInterval)
3169 3168
3170 3169 listMeteors1 = []
3171 3170
3172 3171 for i in range(len(listMeteors)):
3173 3172 meteorPower = listPower[i]
3174 3173 meteorAux = listMeteors[i]
3175 3174
3176 3175 if meteorAux[-1] == 0:
3177 3176
3178 3177 try:
3179 3178 indmax = meteorPower.argmax()
3180 3179 indlag = indmax + lag
3181 3180
3182 3181 y = meteorPower[indlag:]
3183 3182 x = numpy.arange(0, y.size)*timeLag
3184 3183
3185 3184 #first guess
3186 3185 a = y[0]
3187 3186 tau = timeLag
3188 3187 #exponential fit
3189 3188 popt, pcov = optimize.curve_fit(self.__exponential_function, x, y, p0 = [a, tau])
3190 3189 y1 = self.__exponential_function(x, *popt)
3191 3190 #error estimation
3192 3191 error = sum((y - y1)**2)/(numpy.var(y)*(y.size - popt.size))
3193 3192
3194 3193 decayTime = popt[1]
3195 3194 riseTime = indmax*timeInterval
3196 3195 meteorAux[11:13] = [decayTime, error]
3197 3196
3198 3197 #Table items 7, 8 and 11
3199 3198 if (riseTime > 0.3): #Number 7: Echo rise exceeds 0.3s
3200 3199 meteorAux[-1] = 7
3201 3200 elif (decayTime < 2*riseTime) : #Number 8: Echo decay time less than than twice rise time
3202 3201 meteorAux[-1] = 8
3203 3202 if (error > threshError): #Number 11: Poor fit to amplitude for estimation of decay time
3204 3203 meteorAux[-1] = 11
3205 3204
3206 3205
3207 3206 except:
3208 3207 meteorAux[-1] = 11
3209 3208
3210 3209
3211 3210 listMeteors1.append(meteorAux)
3212 3211
3213 3212 return listMeteors1
3214 3213
3215 3214 #Exponential Function
3216 3215
3217 3216 def __exponential_function(self, x, a, tau):
3218 3217 y = a*numpy.exp(-x/tau)
3219 3218 return y
3220 3219
3221 3220 def __getRadialVelocity(self, listMeteors, listVolts, radialStdThresh, pairslist, timeInterval):
3222 3221
3223 3222 pairslist1 = list(pairslist)
3224 3223 pairslist1.append((0,1))
3225 3224 pairslist1.append((3,4))
3226 3225 numPairs = len(pairslist1)
3227 3226 #Time Lag
3228 3227 timeLag = 45*10**-3
3229 3228 c = 3e8
3230 3229 lag = numpy.ceil(timeLag/timeInterval)
3231 3230 freq = 30e6
3232 3231
3233 3232 listMeteors1 = []
3234 3233
3235 3234 for i in range(len(listMeteors)):
3236 3235 meteorAux = listMeteors[i]
3237 3236 if meteorAux[-1] == 0:
3238 3237 mStart = listMeteors[i][1]
3239 3238 mPeak = listMeteors[i][2]
3240 3239 mLag = mPeak - mStart + lag
3241 3240
3242 3241 #get the volt data between the start and end times of the meteor
3243 3242 meteorVolts = listVolts[i]
3244 3243 meteorVolts = meteorVolts.reshape(meteorVolts.shape[0], meteorVolts.shape[1], 1)
3245 3244
3246 3245 #Get CCF
3247 3246 allCCFs = self.__calculateCCF(meteorVolts, pairslist1, [-2,-1,0,1,2])
3248 3247
3249 3248 #Method 2
3250 3249 slopes = numpy.zeros(numPairs)
3251 3250 time = numpy.array([-2,-1,1,2])*timeInterval
3252 3251 angAllCCF = numpy.angle(allCCFs[:,[0,1,3,4],0])
3253 3252
3254 3253 #Correct phases
3255 3254 derPhaseCCF = angAllCCF[:,1:] - angAllCCF[:,0:-1]
3256 3255 indDer = numpy.where(numpy.abs(derPhaseCCF) > numpy.pi)
3257 3256
3258 3257 if indDer[0].shape[0] > 0:
3259 3258 for i in range(indDer[0].shape[0]):
3260 3259 signo = -numpy.sign(derPhaseCCF[indDer[0][i],indDer[1][i]])
3261 3260 angAllCCF[indDer[0][i],indDer[1][i]+1:] += signo*2*numpy.pi
3262 3261
3263 3262 # fit = scipy.stats.linregress(numpy.array([-2,-1,1,2])*timeInterval, numpy.array([phaseLagN2s[i],phaseLagN1s[i],phaseLag1s[i],phaseLag2s[i]]))
3264 3263 for j in range(numPairs):
3265 3264 fit = stats.linregress(time, angAllCCF[j,:])
3266 3265 slopes[j] = fit[0]
3267 3266
3268 3267 #Remove Outlier
3269 3268 # indOut = numpy.argmax(numpy.abs(slopes - numpy.mean(slopes)))
3270 3269 # slopes = numpy.delete(slopes,indOut)
3271 3270 # indOut = numpy.argmax(numpy.abs(slopes - numpy.mean(slopes)))
3272 3271 # slopes = numpy.delete(slopes,indOut)
3273 3272
3274 3273 radialVelocity = -numpy.mean(slopes)*(0.25/numpy.pi)*(c/freq)
3275 3274 radialError = numpy.std(slopes)*(0.25/numpy.pi)*(c/freq)
3276 3275 meteorAux[-2] = radialError
3277 3276 meteorAux[-3] = radialVelocity
3278 3277
3279 3278 #Setting Error
3280 3279 #Number 15: Radial Drift velocity or projected horizontal velocity exceeds 200 m/s
3281 3280 if numpy.abs(radialVelocity) > 200:
3282 3281 meteorAux[-1] = 15
3283 3282 #Number 12: Poor fit to CCF variation for estimation of radial drift velocity
3284 3283 elif radialError > radialStdThresh:
3285 3284 meteorAux[-1] = 12
3286 3285
3287 3286 listMeteors1.append(meteorAux)
3288 3287 return listMeteors1
3289 3288
3290 3289 def __setNewArrays(self, listMeteors, date, heiRang):
3291 3290
3292 3291 #New arrays
3293 3292 arrayMeteors = numpy.array(listMeteors)
3294 3293 arrayParameters = numpy.zeros((len(listMeteors), 13))
3295 3294
3296 3295 #Date inclusion
3297 3296 # date = re.findall(r'\((.*?)\)', date)
3298 3297 # date = date[0].split(',')
3299 3298 # date = map(int, date)
3300 3299 #
3301 3300 # if len(date)<6:
3302 3301 # date.append(0)
3303 3302 #
3304 3303 # date = [date[0]*10000 + date[1]*100 + date[2], date[3]*10000 + date[4]*100 + date[5]]
3305 3304 # arrayDate = numpy.tile(date, (len(listMeteors), 1))
3306 3305 arrayDate = numpy.tile(date, (len(listMeteors)))
3307 3306
3308 3307 #Meteor array
3309 3308 # arrayMeteors[:,0] = heiRang[arrayMeteors[:,0].astype(int)]
3310 3309 # arrayMeteors = numpy.hstack((arrayDate, arrayMeteors))
3311 3310
3312 3311 #Parameters Array
3313 3312 arrayParameters[:,0] = arrayDate #Date
3314 3313 arrayParameters[:,1] = heiRang[arrayMeteors[:,0].astype(int)] #Range
3315 3314 arrayParameters[:,6:8] = arrayMeteors[:,-3:-1] #Radial velocity and its error
3316 3315 arrayParameters[:,8:12] = arrayMeteors[:,7:11] #Phases
3317 3316 arrayParameters[:,-1] = arrayMeteors[:,-1] #Error
3318 3317
3319 3318
3320 3319 return arrayParameters
3321 3320
3322 3321 class CorrectSMPhases(Operation):
3323 3322
3324 3323 def run(self, dataOut, phaseOffsets, hmin = 50, hmax = 150, azimuth = 45, channelPositions = None):
3325 3324
3326 3325 arrayParameters = dataOut.data_param
3327 3326 pairsList = []
3328 3327 pairx = (0,1)
3329 3328 pairy = (2,3)
3330 3329 pairsList.append(pairx)
3331 3330 pairsList.append(pairy)
3332 3331 jph = numpy.zeros(4)
3333 3332
3334 3333 phaseOffsets = numpy.array(phaseOffsets)*numpy.pi/180
3335 3334 # arrayParameters[:,8:12] = numpy.unwrap(arrayParameters[:,8:12] + phaseOffsets)
3336 3335 arrayParameters[:,8:12] = numpy.angle(numpy.exp(1j*(arrayParameters[:,8:12] + phaseOffsets)))
3337 3336
3338 3337 meteorOps = SMOperations()
3339 3338 if channelPositions is None:
3340 3339 # channelPositions = [(2.5,0), (0,2.5), (0,0), (0,4.5), (-2,0)] #T
3341 3340 channelPositions = [(4.5,2), (2,4.5), (2,2), (2,0), (0,2)] #Estrella
3342 3341
3343 3342 pairslist0, distances = meteorOps.getPhasePairs(channelPositions)
3344 3343 h = (hmin,hmax)
3345 3344
3346 3345 arrayParameters = meteorOps.getMeteorParams(arrayParameters, azimuth, h, pairsList, distances, jph)
3347 3346
3348 3347 dataOut.data_param = arrayParameters
3349 3348 return
3350 3349
3351 3350 class SMPhaseCalibration(Operation):
3352 3351
3353 3352 __buffer = None
3354 3353
3355 3354 __initime = None
3356 3355
3357 3356 __dataReady = False
3358 3357
3359 3358 __isConfig = False
3360 3359
3361 3360 def __checkTime(self, currentTime, initTime, paramInterval, outputInterval):
3362 3361
3363 3362 dataTime = currentTime + paramInterval
3364 3363 deltaTime = dataTime - initTime
3365 3364
3366 3365 if deltaTime >= outputInterval or deltaTime < 0:
3367 3366 return True
3368 3367
3369 3368 return False
3370 3369
3371 3370 def __getGammas(self, pairs, d, phases):
3372 3371 gammas = numpy.zeros(2)
3373 3372
3374 3373 for i in range(len(pairs)):
3375 3374
3376 3375 pairi = pairs[i]
3377 3376
3378 3377 phip3 = phases[:,pairi[0]]
3379 3378 d3 = d[pairi[0]]
3380 3379 phip2 = phases[:,pairi[1]]
3381 3380 d2 = d[pairi[1]]
3382 3381 #Calculating gamma
3383 3382 # jdcos = alp1/(k*d1)
3384 3383 # jgamma = numpy.angle(numpy.exp(1j*(d0*alp1/d1 - alp0)))
3385 3384 jgamma = -phip2*d3/d2 - phip3
3386 3385 jgamma = numpy.angle(numpy.exp(1j*jgamma))
3387 3386 # jgamma[jgamma>numpy.pi] -= 2*numpy.pi
3388 3387 # jgamma[jgamma<-numpy.pi] += 2*numpy.pi
3389 3388
3390 3389 #Revised distribution
3391 3390 jgammaArray = numpy.hstack((jgamma,jgamma+0.5*numpy.pi,jgamma-0.5*numpy.pi))
3392 3391
3393 3392 #Histogram
3394 3393 nBins = 64
3395 3394 rmin = -0.5*numpy.pi
3396 3395 rmax = 0.5*numpy.pi
3397 3396 phaseHisto = numpy.histogram(jgammaArray, bins=nBins, range=(rmin,rmax))
3398 3397
3399 3398 meteorsY = phaseHisto[0]
3400 3399 phasesX = phaseHisto[1][:-1]
3401 3400 width = phasesX[1] - phasesX[0]
3402 3401 phasesX += width/2
3403 3402
3404 3403 #Gaussian aproximation
3405 3404 bpeak = meteorsY.argmax()
3406 3405 peak = meteorsY.max()
3407 3406 jmin = bpeak - 5
3408 3407 jmax = bpeak + 5 + 1
3409 3408
3410 3409 if jmin<0:
3411 3410 jmin = 0
3412 3411 jmax = 6
3413 3412 elif jmax > meteorsY.size:
3414 3413 jmin = meteorsY.size - 6
3415 3414 jmax = meteorsY.size
3416 3415
3417 3416 x0 = numpy.array([peak,bpeak,50])
3418 3417 coeff = optimize.leastsq(self.__residualFunction, x0, args=(meteorsY[jmin:jmax], phasesX[jmin:jmax]))
3419 3418
3420 3419 #Gammas
3421 3420 gammas[i] = coeff[0][1]
3422 3421
3423 3422 return gammas
3424 3423
3425 3424 def __residualFunction(self, coeffs, y, t):
3426 3425
3427 3426 return y - self.__gauss_function(t, coeffs)
3428 3427
3429 3428 def __gauss_function(self, t, coeffs):
3430 3429
3431 3430 return coeffs[0]*numpy.exp(-0.5*((t - coeffs[1]) / coeffs[2])**2)
3432 3431
3433 3432 def __getPhases(self, azimuth, h, pairsList, d, gammas, meteorsArray):
3434 3433 meteorOps = SMOperations()
3435 3434 nchan = 4
3436 3435 pairx = pairsList[0] #x es 0
3437 3436 pairy = pairsList[1] #y es 1
3438 3437 center_xangle = 0
3439 3438 center_yangle = 0
3440 3439 range_angle = numpy.array([10*numpy.pi,numpy.pi,numpy.pi/2,numpy.pi/4])
3441 3440 ntimes = len(range_angle)
3442 3441
3443 3442 nstepsx = 20
3444 3443 nstepsy = 20
3445 3444
3446 3445 for iz in range(ntimes):
3447 3446 min_xangle = -range_angle[iz]/2 + center_xangle
3448 3447 max_xangle = range_angle[iz]/2 + center_xangle
3449 3448 min_yangle = -range_angle[iz]/2 + center_yangle
3450 3449 max_yangle = range_angle[iz]/2 + center_yangle
3451 3450
3452 3451 inc_x = (max_xangle-min_xangle)/nstepsx
3453 3452 inc_y = (max_yangle-min_yangle)/nstepsy
3454 3453
3455 3454 alpha_y = numpy.arange(nstepsy)*inc_y + min_yangle
3456 3455 alpha_x = numpy.arange(nstepsx)*inc_x + min_xangle
3457 3456 penalty = numpy.zeros((nstepsx,nstepsy))
3458 3457 jph_array = numpy.zeros((nchan,nstepsx,nstepsy))
3459 3458 jph = numpy.zeros(nchan)
3460 3459
3461 3460 # Iterations looking for the offset
3462 3461 for iy in range(int(nstepsy)):
3463 3462 for ix in range(int(nstepsx)):
3464 3463 d3 = d[pairsList[1][0]]
3465 3464 d2 = d[pairsList[1][1]]
3466 3465 d5 = d[pairsList[0][0]]
3467 3466 d4 = d[pairsList[0][1]]
3468 3467
3469 3468 alp2 = alpha_y[iy] #gamma 1
3470 3469 alp4 = alpha_x[ix] #gamma 0
3471 3470
3472 3471 alp3 = -alp2*d3/d2 - gammas[1]
3473 3472 alp5 = -alp4*d5/d4 - gammas[0]
3474 3473 # jph[pairy[1]] = alpha_y[iy]
3475 3474 # jph[pairy[0]] = -gammas[1] - alpha_y[iy]*d[pairy[1]]/d[pairy[0]]
3476 3475
3477 3476 # jph[pairx[1]] = alpha_x[ix]
3478 3477 # jph[pairx[0]] = -gammas[0] - alpha_x[ix]*d[pairx[1]]/d[pairx[0]]
3479 3478 jph[pairsList[0][1]] = alp4
3480 3479 jph[pairsList[0][0]] = alp5
3481 3480 jph[pairsList[1][0]] = alp3
3482 3481 jph[pairsList[1][1]] = alp2
3483 3482 jph_array[:,ix,iy] = jph
3484 3483 # d = [2.0,2.5,2.5,2.0]
3485 3484 #falta chequear si va a leer bien los meteoros
3486 3485 meteorsArray1 = meteorOps.getMeteorParams(meteorsArray, azimuth, h, pairsList, d, jph)
3487 3486 error = meteorsArray1[:,-1]
3488 3487 ind1 = numpy.where(error==0)[0]
3489 3488 penalty[ix,iy] = ind1.size
3490 3489
3491 3490 i,j = numpy.unravel_index(penalty.argmax(), penalty.shape)
3492 3491 phOffset = jph_array[:,i,j]
3493 3492
3494 3493 center_xangle = phOffset[pairx[1]]
3495 3494 center_yangle = phOffset[pairy[1]]
3496 3495
3497 3496 phOffset = numpy.angle(numpy.exp(1j*jph_array[:,i,j]))
3498 3497 phOffset = phOffset*180/numpy.pi
3499 3498 return phOffset
3500 3499
3501 3500
3502 3501 def run(self, dataOut, hmin, hmax, channelPositions=None, nHours = 1):
3503 3502
3504 3503 dataOut.flagNoData = True
3505 3504 self.__dataReady = False
3506 3505 dataOut.outputInterval = nHours*3600
3507 3506
3508 3507 if self.__isConfig == False:
3509 3508 # self.__initime = dataOut.datatime.replace(minute = 0, second = 0, microsecond = 03)
3510 3509 #Get Initial LTC time
3511 3510 self.__initime = datetime.datetime.utcfromtimestamp(dataOut.utctime)
3512 3511 self.__initime = (self.__initime.replace(minute = 0, second = 0, microsecond = 0) - datetime.datetime(1970, 1, 1)).total_seconds()
3513 3512
3514 3513 self.__isConfig = True
3515 3514
3516 3515 if self.__buffer is None:
3517 3516 self.__buffer = dataOut.data_param.copy()
3518 3517
3519 3518 else:
3520 3519 self.__buffer = numpy.vstack((self.__buffer, dataOut.data_param))
3521 3520
3522 3521 self.__dataReady = self.__checkTime(dataOut.utctime, self.__initime, dataOut.paramInterval, dataOut.outputInterval) #Check if the buffer is ready
3523 3522
3524 3523 if self.__dataReady:
3525 3524 dataOut.utctimeInit = self.__initime
3526 3525 self.__initime += dataOut.outputInterval #to erase time offset
3527 3526
3528 3527 freq = dataOut.frequency
3529 3528 c = dataOut.C #m/s
3530 3529 lamb = c/freq
3531 3530 k = 2*numpy.pi/lamb
3532 3531 azimuth = 0
3533 3532 h = (hmin, hmax)
3534 3533 # pairs = ((0,1),(2,3)) #Estrella
3535 3534 # pairs = ((1,0),(2,3)) #T
3536 3535
3537 3536 if channelPositions is None:
3538 3537 # channelPositions = [(2.5,0), (0,2.5), (0,0), (0,4.5), (-2,0)] #T
3539 3538 channelPositions = [(4.5,2), (2,4.5), (2,2), (2,0), (0,2)] #Estrella
3540 3539 meteorOps = SMOperations()
3541 3540 pairslist0, distances = meteorOps.getPhasePairs(channelPositions)
3542 3541
3543 3542 #Checking correct order of pairs
3544 3543 pairs = []
3545 3544 if distances[1] > distances[0]:
3546 3545 pairs.append((1,0))
3547 3546 else:
3548 3547 pairs.append((0,1))
3549 3548
3550 3549 if distances[3] > distances[2]:
3551 3550 pairs.append((3,2))
3552 3551 else:
3553 3552 pairs.append((2,3))
3554 3553 # distances1 = [-distances[0]*lamb, distances[1]*lamb, -distances[2]*lamb, distances[3]*lamb]
3555 3554
3556 3555 meteorsArray = self.__buffer
3557 3556 error = meteorsArray[:,-1]
3558 3557 boolError = (error==0)|(error==3)|(error==4)|(error==13)|(error==14)
3559 3558 ind1 = numpy.where(boolError)[0]
3560 3559 meteorsArray = meteorsArray[ind1,:]
3561 3560 meteorsArray[:,-1] = 0
3562 3561 phases = meteorsArray[:,8:12]
3563 3562
3564 3563 #Calculate Gammas
3565 3564 gammas = self.__getGammas(pairs, distances, phases)
3566 3565 # gammas = numpy.array([-21.70409463,45.76935864])*numpy.pi/180
3567 3566 #Calculate Phases
3568 3567 phasesOff = self.__getPhases(azimuth, h, pairs, distances, gammas, meteorsArray)
3569 3568 phasesOff = phasesOff.reshape((1,phasesOff.size))
3570 3569 dataOut.data_output = -phasesOff
3571 3570 dataOut.flagNoData = False
3572 3571 self.__buffer = None
3573 3572
3574 3573
3575 3574 return
3576 3575
3577 3576 class SMOperations():
3578 3577
3579 3578 def __init__(self):
3580 3579
3581 3580 return
3582 3581
3583 3582 def getMeteorParams(self, arrayParameters0, azimuth, h, pairsList, distances, jph):
3584 3583
3585 3584 arrayParameters = arrayParameters0.copy()
3586 3585 hmin = h[0]
3587 3586 hmax = h[1]
3588 3587
3589 3588 #Calculate AOA (Error N 3, 4)
3590 3589 #JONES ET AL. 1998
3591 3590 AOAthresh = numpy.pi/8
3592 3591 error = arrayParameters[:,-1]
3593 3592 phases = -arrayParameters[:,8:12] + jph
3594 3593 # phases = numpy.unwrap(phases)
3595 3594 arrayParameters[:,3:6], arrayParameters[:,-1] = self.__getAOA(phases, pairsList, distances, error, AOAthresh, azimuth)
3596 3595
3597 3596 #Calculate Heights (Error N 13 and 14)
3598 3597 error = arrayParameters[:,-1]
3599 3598 Ranges = arrayParameters[:,1]
3600 3599 zenith = arrayParameters[:,4]
3601 3600 arrayParameters[:,2], arrayParameters[:,-1] = self.__getHeights(Ranges, zenith, error, hmin, hmax)
3602 3601
3603 3602 #----------------------- Get Final data ------------------------------------
3604 3603 # error = arrayParameters[:,-1]
3605 3604 # ind1 = numpy.where(error==0)[0]
3606 3605 # arrayParameters = arrayParameters[ind1,:]
3607 3606
3608 3607 return arrayParameters
3609 3608
3610 3609 def __getAOA(self, phases, pairsList, directions, error, AOAthresh, azimuth):
3611 3610
3612 3611 arrayAOA = numpy.zeros((phases.shape[0],3))
3613 3612 cosdir0, cosdir = self.__getDirectionCosines(phases, pairsList,directions)
3614 3613
3615 3614 arrayAOA[:,:2] = self.__calculateAOA(cosdir, azimuth)
3616 3615 cosDirError = numpy.sum(numpy.abs(cosdir0 - cosdir), axis = 1)
3617 3616 arrayAOA[:,2] = cosDirError
3618 3617
3619 3618 azimuthAngle = arrayAOA[:,0]
3620 3619 zenithAngle = arrayAOA[:,1]
3621 3620
3622 3621 #Setting Error
3623 3622 indError = numpy.where(numpy.logical_or(error == 3, error == 4))[0]
3624 3623 error[indError] = 0
3625 3624 #Number 3: AOA not fesible
3626 3625 indInvalid = numpy.where(numpy.logical_and((numpy.logical_or(numpy.isnan(zenithAngle), numpy.isnan(azimuthAngle))),error == 0))[0]
3627 3626 error[indInvalid] = 3
3628 3627 #Number 4: Large difference in AOAs obtained from different antenna baselines
3629 3628 indInvalid = numpy.where(numpy.logical_and(cosDirError > AOAthresh,error == 0))[0]
3630 3629 error[indInvalid] = 4
3631 3630 return arrayAOA, error
3632 3631
3633 3632 def __getDirectionCosines(self, arrayPhase, pairsList, distances):
3634 3633
3635 3634 #Initializing some variables
3636 3635 ang_aux = numpy.array([-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8])*2*numpy.pi
3637 3636 ang_aux = ang_aux.reshape(1,ang_aux.size)
3638 3637
3639 3638 cosdir = numpy.zeros((arrayPhase.shape[0],2))
3640 3639 cosdir0 = numpy.zeros((arrayPhase.shape[0],2))
3641 3640
3642 3641
3643 3642 for i in range(2):
3644 3643 ph0 = arrayPhase[:,pairsList[i][0]]
3645 3644 ph1 = arrayPhase[:,pairsList[i][1]]
3646 3645 d0 = distances[pairsList[i][0]]
3647 3646 d1 = distances[pairsList[i][1]]
3648 3647
3649 3648 ph0_aux = ph0 + ph1
3650 3649 ph0_aux = numpy.angle(numpy.exp(1j*ph0_aux))
3651 3650 # ph0_aux[ph0_aux > numpy.pi] -= 2*numpy.pi
3652 3651 # ph0_aux[ph0_aux < -numpy.pi] += 2*numpy.pi
3653 3652 #First Estimation
3654 3653 cosdir0[:,i] = (ph0_aux)/(2*numpy.pi*(d0 - d1))
3655 3654
3656 3655 #Most-Accurate Second Estimation
3657 3656 phi1_aux = ph0 - ph1
3658 3657 phi1_aux = phi1_aux.reshape(phi1_aux.size,1)
3659 3658 #Direction Cosine 1
3660 3659 cosdir1 = (phi1_aux + ang_aux)/(2*numpy.pi*(d0 + d1))
3661 3660
3662 3661 #Searching the correct Direction Cosine
3663 3662 cosdir0_aux = cosdir0[:,i]
3664 3663 cosdir0_aux = cosdir0_aux.reshape(cosdir0_aux.size,1)
3665 3664 #Minimum Distance
3666 3665 cosDiff = (cosdir1 - cosdir0_aux)**2
3667 3666 indcos = cosDiff.argmin(axis = 1)
3668 3667 #Saving Value obtained
3669 3668 cosdir[:,i] = cosdir1[numpy.arange(len(indcos)),indcos]
3670 3669
3671 3670 return cosdir0, cosdir
3672 3671
3673 3672 def __calculateAOA(self, cosdir, azimuth):
3674 3673 cosdirX = cosdir[:,0]
3675 3674 cosdirY = cosdir[:,1]
3676 3675
3677 3676 zenithAngle = numpy.arccos(numpy.sqrt(1 - cosdirX**2 - cosdirY**2))*180/numpy.pi
3678 3677 azimuthAngle = numpy.arctan2(cosdirX,cosdirY)*180/numpy.pi + azimuth#0 deg north, 90 deg east
3679 3678 angles = numpy.vstack((azimuthAngle, zenithAngle)).transpose()
3680 3679
3681 3680 return angles
3682 3681
3683 3682 def __getHeights(self, Ranges, zenith, error, minHeight, maxHeight):
3684 3683
3685 3684 Ramb = 375 #Ramb = c/(2*PRF)
3686 3685 Re = 6371 #Earth Radius
3687 3686 heights = numpy.zeros(Ranges.shape)
3688 3687
3689 3688 R_aux = numpy.array([0,1,2])*Ramb
3690 3689 R_aux = R_aux.reshape(1,R_aux.size)
3691 3690
3692 3691 Ranges = Ranges.reshape(Ranges.size,1)
3693 3692
3694 3693 Ri = Ranges + R_aux
3695 3694 hi = numpy.sqrt(Re**2 + Ri**2 + (2*Re*numpy.cos(zenith*numpy.pi/180)*Ri.transpose()).transpose()) - Re
3696 3695
3697 3696 #Check if there is a height between 70 and 110 km
3698 3697 h_bool = numpy.sum(numpy.logical_and(hi > minHeight, hi < maxHeight), axis = 1)
3699 3698 ind_h = numpy.where(h_bool == 1)[0]
3700 3699
3701 3700 hCorr = hi[ind_h, :]
3702 3701 ind_hCorr = numpy.where(numpy.logical_and(hi > minHeight, hi < maxHeight))
3703 3702
3704 3703 hCorr = hi[ind_hCorr][:len(ind_h)]
3705 3704 heights[ind_h] = hCorr
3706 3705
3707 3706 #Setting Error
3708 3707 #Number 13: Height unresolvable echo: not valid height within 70 to 110 km
3709 3708 #Number 14: Height ambiguous echo: more than one possible height within 70 to 110 km
3710 3709 indError = numpy.where(numpy.logical_or(error == 13, error == 14))[0]
3711 3710 error[indError] = 0
3712 3711 indInvalid2 = numpy.where(numpy.logical_and(h_bool > 1, error == 0))[0]
3713 3712 error[indInvalid2] = 14
3714 3713 indInvalid1 = numpy.where(numpy.logical_and(h_bool == 0, error == 0))[0]
3715 3714 error[indInvalid1] = 13
3716 3715
3717 3716 return heights, error
3718 3717
3719 3718 def getPhasePairs(self, channelPositions):
3720 3719 chanPos = numpy.array(channelPositions)
3721 3720 listOper = list(itertools.combinations(list(range(5)),2))
3722 3721
3723 3722 distances = numpy.zeros(4)
3724 3723 axisX = []
3725 3724 axisY = []
3726 3725 distX = numpy.zeros(3)
3727 3726 distY = numpy.zeros(3)
3728 3727 ix = 0
3729 3728 iy = 0
3730 3729
3731 3730 pairX = numpy.zeros((2,2))
3732 3731 pairY = numpy.zeros((2,2))
3733 3732
3734 3733 for i in range(len(listOper)):
3735 3734 pairi = listOper[i]
3736 3735
3737 3736 posDif = numpy.abs(chanPos[pairi[0],:] - chanPos[pairi[1],:])
3738 3737
3739 3738 if posDif[0] == 0:
3740 3739 axisY.append(pairi)
3741 3740 distY[iy] = posDif[1]
3742 3741 iy += 1
3743 3742 elif posDif[1] == 0:
3744 3743 axisX.append(pairi)
3745 3744 distX[ix] = posDif[0]
3746 3745 ix += 1
3747 3746
3748 3747 for i in range(2):
3749 3748 if i==0:
3750 3749 dist0 = distX
3751 3750 axis0 = axisX
3752 3751 else:
3753 3752 dist0 = distY
3754 3753 axis0 = axisY
3755 3754
3756 3755 side = numpy.argsort(dist0)[:-1]
3757 3756 axis0 = numpy.array(axis0)[side,:]
3758 3757 chanC = int(numpy.intersect1d(axis0[0,:], axis0[1,:])[0])
3759 3758 axis1 = numpy.unique(numpy.reshape(axis0,4))
3760 3759 side = axis1[axis1 != chanC]
3761 3760 diff1 = chanPos[chanC,i] - chanPos[side[0],i]
3762 3761 diff2 = chanPos[chanC,i] - chanPos[side[1],i]
3763 3762 if diff1<0:
3764 3763 chan2 = side[0]
3765 3764 d2 = numpy.abs(diff1)
3766 3765 chan1 = side[1]
3767 3766 d1 = numpy.abs(diff2)
3768 3767 else:
3769 3768 chan2 = side[1]
3770 3769 d2 = numpy.abs(diff2)
3771 3770 chan1 = side[0]
3772 3771 d1 = numpy.abs(diff1)
3773 3772
3774 3773 if i==0:
3775 3774 chanCX = chanC
3776 3775 chan1X = chan1
3777 3776 chan2X = chan2
3778 3777 distances[0:2] = numpy.array([d1,d2])
3779 3778 else:
3780 3779 chanCY = chanC
3781 3780 chan1Y = chan1
3782 3781 chan2Y = chan2
3783 3782 distances[2:4] = numpy.array([d1,d2])
3784 3783 # axisXsides = numpy.reshape(axisX[ix,:],4)
3785 3784 #
3786 3785 # channelCentX = int(numpy.intersect1d(pairX[0,:], pairX[1,:])[0])
3787 3786 # channelCentY = int(numpy.intersect1d(pairY[0,:], pairY[1,:])[0])
3788 3787 #
3789 3788 # ind25X = numpy.where(pairX[0,:] != channelCentX)[0][0]
3790 3789 # ind20X = numpy.where(pairX[1,:] != channelCentX)[0][0]
3791 3790 # channel25X = int(pairX[0,ind25X])
3792 3791 # channel20X = int(pairX[1,ind20X])
3793 3792 # ind25Y = numpy.where(pairY[0,:] != channelCentY)[0][0]
3794 3793 # ind20Y = numpy.where(pairY[1,:] != channelCentY)[0][0]
3795 3794 # channel25Y = int(pairY[0,ind25Y])
3796 3795 # channel20Y = int(pairY[1,ind20Y])
3797 3796
3798 3797 # pairslist = [(channelCentX, channel25X),(channelCentX, channel20X),(channelCentY,channel25Y),(channelCentY, channel20Y)]
3799 3798 pairslist = [(chanCX, chan1X),(chanCX, chan2X),(chanCY,chan1Y),(chanCY, chan2Y)]
3800 3799
3801 3800 return pairslist, distances
3802 3801 # def __getAOA(self, phases, pairsList, error, AOAthresh, azimuth):
3803 3802 #
3804 3803 # arrayAOA = numpy.zeros((phases.shape[0],3))
3805 3804 # cosdir0, cosdir = self.__getDirectionCosines(phases, pairsList)
3806 3805 #
3807 3806 # arrayAOA[:,:2] = self.__calculateAOA(cosdir, azimuth)
3808 3807 # cosDirError = numpy.sum(numpy.abs(cosdir0 - cosdir), axis = 1)
3809 3808 # arrayAOA[:,2] = cosDirError
3810 3809 #
3811 3810 # azimuthAngle = arrayAOA[:,0]
3812 3811 # zenithAngle = arrayAOA[:,1]
3813 3812 #
3814 3813 # #Setting Error
3815 3814 # #Number 3: AOA not fesible
3816 3815 # indInvalid = numpy.where(numpy.logical_and((numpy.logical_or(numpy.isnan(zenithAngle), numpy.isnan(azimuthAngle))),error == 0))[0]
3817 3816 # error[indInvalid] = 3
3818 3817 # #Number 4: Large difference in AOAs obtained from different antenna baselines
3819 3818 # indInvalid = numpy.where(numpy.logical_and(cosDirError > AOAthresh,error == 0))[0]
3820 3819 # error[indInvalid] = 4
3821 3820 # return arrayAOA, error
3822 3821 #
3823 3822 # def __getDirectionCosines(self, arrayPhase, pairsList):
3824 3823 #
3825 3824 # #Initializing some variables
3826 3825 # ang_aux = numpy.array([-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8])*2*numpy.pi
3827 3826 # ang_aux = ang_aux.reshape(1,ang_aux.size)
3828 3827 #
3829 3828 # cosdir = numpy.zeros((arrayPhase.shape[0],2))
3830 3829 # cosdir0 = numpy.zeros((arrayPhase.shape[0],2))
3831 3830 #
3832 3831 #
3833 3832 # for i in range(2):
3834 3833 # #First Estimation
3835 3834 # phi0_aux = arrayPhase[:,pairsList[i][0]] + arrayPhase[:,pairsList[i][1]]
3836 3835 # #Dealias
3837 3836 # indcsi = numpy.where(phi0_aux > numpy.pi)
3838 3837 # phi0_aux[indcsi] -= 2*numpy.pi
3839 3838 # indcsi = numpy.where(phi0_aux < -numpy.pi)
3840 3839 # phi0_aux[indcsi] += 2*numpy.pi
3841 3840 # #Direction Cosine 0
3842 3841 # cosdir0[:,i] = -(phi0_aux)/(2*numpy.pi*0.5)
3843 3842 #
3844 3843 # #Most-Accurate Second Estimation
3845 3844 # phi1_aux = arrayPhase[:,pairsList[i][0]] - arrayPhase[:,pairsList[i][1]]
3846 3845 # phi1_aux = phi1_aux.reshape(phi1_aux.size,1)
3847 3846 # #Direction Cosine 1
3848 3847 # cosdir1 = -(phi1_aux + ang_aux)/(2*numpy.pi*4.5)
3849 3848 #
3850 3849 # #Searching the correct Direction Cosine
3851 3850 # cosdir0_aux = cosdir0[:,i]
3852 3851 # cosdir0_aux = cosdir0_aux.reshape(cosdir0_aux.size,1)
3853 3852 # #Minimum Distance
3854 3853 # cosDiff = (cosdir1 - cosdir0_aux)**2
3855 3854 # indcos = cosDiff.argmin(axis = 1)
3856 3855 # #Saving Value obtained
3857 3856 # cosdir[:,i] = cosdir1[numpy.arange(len(indcos)),indcos]
3858 3857 #
3859 3858 # return cosdir0, cosdir
3860 3859 #
3861 3860 # def __calculateAOA(self, cosdir, azimuth):
3862 3861 # cosdirX = cosdir[:,0]
3863 3862 # cosdirY = cosdir[:,1]
3864 3863 #
3865 3864 # zenithAngle = numpy.arccos(numpy.sqrt(1 - cosdirX**2 - cosdirY**2))*180/numpy.pi
3866 3865 # azimuthAngle = numpy.arctan2(cosdirX,cosdirY)*180/numpy.pi + azimuth #0 deg north, 90 deg east
3867 3866 # angles = numpy.vstack((azimuthAngle, zenithAngle)).transpose()
3868 3867 #
3869 3868 # return angles
3870 3869 #
3871 3870 # def __getHeights(self, Ranges, zenith, error, minHeight, maxHeight):
3872 3871 #
3873 3872 # Ramb = 375 #Ramb = c/(2*PRF)
3874 3873 # Re = 6371 #Earth Radius
3875 3874 # heights = numpy.zeros(Ranges.shape)
3876 3875 #
3877 3876 # R_aux = numpy.array([0,1,2])*Ramb
3878 3877 # R_aux = R_aux.reshape(1,R_aux.size)
3879 3878 #
3880 3879 # Ranges = Ranges.reshape(Ranges.size,1)
3881 3880 #
3882 3881 # Ri = Ranges + R_aux
3883 3882 # hi = numpy.sqrt(Re**2 + Ri**2 + (2*Re*numpy.cos(zenith*numpy.pi/180)*Ri.transpose()).transpose()) - Re
3884 3883 #
3885 3884 # #Check if there is a height between 70 and 110 km
3886 3885 # h_bool = numpy.sum(numpy.logical_and(hi > minHeight, hi < maxHeight), axis = 1)
3887 3886 # ind_h = numpy.where(h_bool == 1)[0]
3888 3887 #
3889 3888 # hCorr = hi[ind_h, :]
3890 3889 # ind_hCorr = numpy.where(numpy.logical_and(hi > minHeight, hi < maxHeight))
3891 3890 #
3892 3891 # hCorr = hi[ind_hCorr]
3893 3892 # heights[ind_h] = hCorr
3894 3893 #
3895 3894 # #Setting Error
3896 3895 # #Number 13: Height unresolvable echo: not valid height within 70 to 110 km
3897 3896 # #Number 14: Height ambiguous echo: more than one possible height within 70 to 110 km
3898 3897 #
3899 3898 # indInvalid2 = numpy.where(numpy.logical_and(h_bool > 1, error == 0))[0]
3900 3899 # error[indInvalid2] = 14
3901 3900 # indInvalid1 = numpy.where(numpy.logical_and(h_bool == 0, error == 0))[0]
3902 3901 # error[indInvalid1] = 13
3903 3902 #
3904 3903 # return heights, error
3905 3904
3906 3905
3907 3906 class WeatherRadar(Operation):
3908 3907 '''
3909 3908 Function tat implements Weather Radar operations-
3910 3909 Input:
3911 3910 Output:
3912 3911 Parameters affected:
3913 3912 '''
3914 3913 isConfig = False
3915 3914
3916 3915 def __init__(self):
3917 3916 Operation.__init__(self)
3918 3917
3919 3918 def setup(self,dataOut,Pt=0,Gt=0,Gr=0,lambda_=0, aL=0,
3920 3919 tauW= 0,thetaT=0,thetaR=0,Km =0):
3921 3920 self.nCh = dataOut.nChannels
3922 3921 self.nHeis = dataOut.nHeights
3923 3922 deltaHeight = dataOut.heightList[1] - dataOut.heightList[0]
3924 3923 self.Range = numpy.arange(dataOut.nHeights)*deltaHeight + dataOut.heightList[0]
3925 3924 self.Range = self.Range.reshape(1,self.nHeis)
3926 3925 self.Range = numpy.tile(self.Range,[self.nCh,1])
3927 3926 '''-----------1 Constante del Radar----------'''
3928 3927 self.Pt = Pt
3929 3928 self.Gt = Gt
3930 3929 self.Gr = Gr
3931 3930 self.lambda_ = lambda_
3932 3931 self.aL = aL
3933 3932 self.tauW = tauW
3934 3933 self.thetaT = thetaT
3935 3934 self.thetaR = thetaR
3936 3935 self.Km = Km
3937 3936 Numerator = ((4*numpy.pi)**3 * aL**2 * 16 *numpy.log(2))
3938 3937 Denominator = (Pt * Gt * Gr * lambda_**2 * SPEED_OF_LIGHT * tauW * numpy.pi*thetaT*thetaR)
3939 3938 self.RadarConstant = Numerator/Denominator
3940 3939 '''-----------2 Reflectividad del Radar y Factor de Reflectividad------'''
3941 3940 self.n_radar = numpy.zeros((self.nCh,self.nHeis))
3942 3941 self.Z_radar = numpy.zeros((self.nCh,self.nHeis))
3943 3942
3944 3943 def setMoments(self,dataOut,i):
3945 3944
3946 3945 type = dataOut.inputUnit
3947 3946 nCh = dataOut.nChannels
3948 3947 nHeis= dataOut.nHeights
3949 3948 data_param = numpy.zeros((nCh,4,nHeis))
3950 3949 if type == "Voltage":
3951 3950 data_param[:,0,:] = dataOut.dataPP_POW/(dataOut.nCohInt**2)
3952 3951 data_param[:,1,:] = dataOut.dataPP_DOP
3953 3952 data_param[:,2,:] = dataOut.dataPP_WIDTH
3954 3953 data_param[:,3,:] = dataOut.dataPP_SNR
3955 3954 if type == "Spectra":
3956 3955 data_param[:,0,:] = dataOut.data_POW
3957 3956 data_param[:,1,:] = dataOut.data_DOP
3958 3957 data_param[:,2,:] = dataOut.data_WIDTH
3959 3958 def setMoments(self,dataOut,i):
3960 3959 data_param[:,3,:] = dataOut.data_SNR
3961 3960
3962 3961 return data_param[:,i,:]
3963 3962
3964 3963
3965 3964 def run(self,dataOut,Pt=25,Gt=200.0,Gr=50.0,lambda_=0.32, aL=2.5118,
3966 3965 tauW= 4.0e-6,thetaT=0.165,thetaR=0.367,Km =0.93):
3967 3966
3968 3967 if not self.isConfig:
3969 3968 self.setup(dataOut= dataOut,Pt=25,Gt=200.0,Gr=50.0,lambda_=0.32, aL=2.5118,
3970 3969 tauW= 4.0e-6,thetaT=0.165,thetaR=0.367,Km =0.93)
3971 3970 self.isConfig = True
3972 3971 '''-----------------------------Potencia de Radar -Signal S-----------------------------'''
3973 3972 Pr = self.setMoments(dataOut,0)
3974 3973
3975 3974 for R in range(self.nHeis):
3976 3975 self.n_radar[:,R] = self.RadarConstant*Pr[:,R]* (self.Range[:,R])**2
3977 3976
3978 3977 self.Z_radar[:,R] = self.n_radar[:,R]* self.lambda_**4/( numpy.pi**5 * self.Km**2)
3979 3978
3980 3979 '''----------- Factor de Reflectividad Equivalente lamda_ < 10 cm , lamda_= 3.2cm-------'''
3981 3980 Zeh = self.Z_radar
3982 3981 dBZeh = 10*numpy.log10(Zeh)
3983 3982 dataOut.factor_Zeh= dBZeh
3984 3983 self.n_radar = numpy.zeros((self.nCh,self.nHeis))
3985 3984 self.Z_radar = numpy.zeros((self.nCh,self.nHeis))
3986 3985
3987 3986 return dataOut
3988 3987
3989 3988 class PedestalInformation(Operation):
3990 3989 path_ped = None
3991 3990 path_adq = None
3992 3991 t_Interval_p = None
3993 3992 n_Muestras_p = None
3994 3993 isConfig = False
3995 3994 blocksPerfile= None
3996 3995 f_a_p = None
3997 3996 online = None
3998 3997 angulo_adq = None
3999 3998 nro_file = None
4000 3999 nro_key_p = None
4001 4000 tmp = None
4002 4001
4003 4002
4004 4003 def __init__(self):
4005 4004 Operation.__init__(self)
4006 4005
4007 4006 def getfirstFilefromPath(self,path,meta,ext):
4008 4007 validFilelist = []
4009 4008 #print("SEARH",path)
4010 4009 try:
4011 4010 fileList = os.listdir(path)
4012 4011 except:
4013 4012 print("check path - fileList")
4014 4013 if len(fileList)<1:
4015 4014 return None
4016 4015 # meta 1234 567 8-18 BCDE
4017 4016 # H,D,PE YYYY DDD EPOC .ext
4018 4017
4019 4018 for thisFile in fileList:
4020 4019 #print("HI",thisFile)
4021 4020 if meta =="PE":
4022 4021 try:
4023 4022 number= int(thisFile[len(meta)+7:len(meta)+17])
4024 4023 except:
4025 4024 print("There is a file or folder with different format")
4026 4025 if meta == "D":
4027 4026 try:
4028 4027 number= int(thisFile[8:11])
4029 4028 except:
4030 4029 print("There is a file or folder with different format")
4031 4030
4032 4031 if not isNumber(str=number):
4033 4032 continue
4034 4033 if (os.path.splitext(thisFile)[-1].lower() != ext.lower()):
4035 4034 continue
4036 4035 validFilelist.sort()
4037 4036 validFilelist.append(thisFile)
4038 4037 if len(validFilelist)>0:
4039 4038 validFilelist = sorted(validFilelist,key=str.lower)
4040 4039 return validFilelist
4041 4040 return None
4042 4041
4043 4042 def gettimeutcfromDirFilename(self,path,file):
4044 4043 dir_file= path+"/"+file
4045 4044 fp = h5py.File(dir_file,'r')
4046 4045 #epoc = fp['Metadata'].get('utctimeInit')[()]
4047 4046 epoc = fp['Data'].get('utc')[()]
4048 4047 fp.close()
4049 4048 return epoc
4050 4049
4051 4050 def gettimeutcadqfromDirFilename(self,path,file):
4052 4051 dir_file= path+"/"+file
4053 4052 fp = h5py.File(dir_file,'r')
4054 4053 epoc = fp['Metadata'].get('utctimeInit')[()]
4055 4054 #epoc = fp['Data'].get('utc')[()]
4056 4055 fp.close()
4057 4056 return epoc
4058 4057
4059 4058 def getDatavaluefromDirFilename(self,path,file,value):
4060 4059 dir_file= path+"/"+file
4061 4060 fp = h5py.File(dir_file,'r')
4062 4061 array = fp['Data'].get(value)[()]
4063 4062 fp.close()
4064 4063 return array
4065 4064
4066 4065 def getFile_KeyP(self,list_pedestal,list_adq):
4067 4066 print(list_pedestal)
4068 4067 print(list_adq)
4069 4068
4070 4069 def getNROFile(self,utc_adq,utc_ped_list):
4071 4070 c=0
4072 4071 print("insidegetNROFile")
4073 4072 print(utc_adq)
4074 4073 print(len(utc_ped_list))
4075 4074 for i in range(len(utc_ped_list)):
4076 4075 if utc_adq>utc_ped_list[i]:
4077 4076 #print("mayor")
4078 4077 #print("utc_ped_list",utc_ped_list[i])
4079 4078 c +=1
4080 4079
4081 4080 return c-1,utc_ped_list[c-1],utc_ped_list[c]
4082 4081
4083 4082 def verificarNROFILE(self,dataOut,utc_ped,f_a_p,n_Muestras_p):
4084 4083 var =int(f_a_p/n_Muestras_p)
4085 4084 flag=0
4086 4085 for i in range(var):
4087 4086 if dataOut.utctime+i==utc_ped:
4088 4087 flag==1
4089 4088 break
4090 4089 return flag
4091 4090
4092 4091 #def setup_offline(self,dataOut,list_pedestal,list_adq):
4093 4092 def setup_offline(self,dataOut,list_pedestal):
4094 4093
4095 4094 print("SETUP OFFLINE")
4096 4095 print(self.path_ped)
4097 4096 #print(self.path_adq)
4098 4097 print(len(self.list_pedestal))
4099 4098 #print(len(self.list_adq))
4100 4099 utc_ped_list=[]
4101 4100 for i in range(len(self.list_pedestal)):
4102 print(i)
4101 #print(i)# OJO IDENTIFICADOR DE SINCRONISMO
4103 4102 utc_ped_list.append(self.gettimeutcfromDirFilename(path=self.path_ped,file=self.list_pedestal[i]))
4104 4103
4105 4104 #utc_ped_list= utc_ped_list
4106 4105 ###utc_adq = self.gettimeutcadqfromDirFilename(path=self.path_adq,file=self.list_adq[0])
4107 4106 print("dios existe donde esta")
4108 4107
4109 4108 #print("utc_ped_list",utc_ped_list)
4110 4109 ###print("utc_adq",utc_adq)
4111 4110 # utc_adq_dataOut
4112 4111 utc_adq_dataOut =dataOut.utctime
4113 4112 print("Offline-utc_adq_dataout",utc_adq_dataOut)
4114 4113
4115 4114 nro_file,utc_ped,utc_ped_1 = self.getNROFile(utc_adq=utc_adq_dataOut, utc_ped_list= utc_ped_list)
4116 4115
4117 4116 print("nro_file",nro_file,"utc_ped",utc_ped)
4118 4117 print("nro_file",i)
4119 4118 nro_key_p = int((utc_adq_dataOut-utc_ped)/self.t_Interval_p)-1 # ojito al -1 estimado alex
4120 4119 print("nro_key_p",nro_key_p)
4121 4120
4122 4121 ff_pedestal = self.list_pedestal[nro_file]
4123 4122 #angulo = self.getDatavaluefromDirFilename(path=self.path_ped,file=ff_pedestal,value="azimuth")
4124 4123 angulo = self.getDatavaluefromDirFilename(path=self.path_ped,file=ff_pedestal,value="azi_pos")
4125 4124
4126 4125 print("utc_pedestal_init :",utc_ped+nro_key_p*self.t_Interval_p)
4127 4126 print("angulo_array :",angulo[nro_key_p])
4128 4127 self.nro_file = nro_file
4129 4128 self.nro_key_p = nro_key_p
4130 4129
4131 4130 def setup_online(self,dataOut):
4132 4131 utc_adq =dataOut.utctime
4133 4132 print("Online-utc_adq",utc_adq)
4134 4133 print(len(self.list_pedestal))
4135 4134 utc_ped_list=[]
4136 4135 for i in range(len(self.list_pedestal)):
4137 4136 utc_ped_list.append(self.gettimeutcfromDirFilename(path=self.path_ped,file=self.list_pedestal[i]))
4138 4137 print(utc_ped_list[:20])
4139 4138 #print(utc_ped_list[488:498])
4140 4139 print("ultimo UTC-PEDESTAL",utc_ped_list[-1])
4141 4140 nro_file,utc_ped,utc_ped_1 = self.getNROFile(utc_adq=utc_adq, utc_ped_list= utc_ped_list)
4142 4141 print("nro_file",nro_file,"utc_ped",utc_ped,"utc_ped_1",utc_ped_1)
4143 4142 print("name_PEDESTAL",self.list_pedestal[nro_file])
4144 4143 nro_key_p = int((utc_adq-utc_ped)/self.t_Interval_p)-1
4145 4144 print("nro_key_p",nro_key_p)
4146 4145 ff_pedestal = self.list_pedestal[nro_file]
4147 4146 #angulo = self.getDatavaluefromDirFilename(path=self.path_ped,file=ff_pedestal,value="azimuth")
4148 4147 angulo = self.getDatavaluefromDirFilename(path=self.path_ped,file=ff_pedestal,value="azi_pos")
4149 4148
4150 4149 print("utc_pedestal_init :",utc_ped+nro_key_p*self.t_Interval_p)
4151 4150 print("angulo_array :",angulo[nro_key_p])
4152 4151 self.nro_file = nro_file
4153 4152 self.nro_key_p = nro_key_p
4154 4153
4155 4154 #def setup(self,dataOut,path_ped,path_adq,t_Interval_p,n_Muestras_p,blocksPerfile,f_a_p,online):
4156 4155 def setup(self,dataOut,path_ped,t_Interval_p,n_Muestras_p,blocksPerfile,f_a_p,online):
4157 4156 print("SETUP PEDESTAL")
4158 4157 self.__dataReady = False
4159 4158 self.path_ped = path_ped
4160 4159 #self.path_adq = path_adq
4161 4160 self.t_Interval_p = t_Interval_p
4162 4161 self.n_Muestras_p = n_Muestras_p
4163 4162 self.blocksPerfile= blocksPerfile
4164 4163 self.f_a_p = f_a_p
4165 4164 self.online = online
4166 4165 self.angulo_adq = numpy.zeros(self.blocksPerfile)
4167 4166 self.__profIndex = 0
4168 4167 self.tmp = 0
4169 4168 self.c_ped = 0
4170 4169 print(self.path_ped)
4171 4170 #print(self.path_adq)
4172 4171 self.list_pedestal = self.getfirstFilefromPath(path=self.path_ped,meta="PE",ext=".hdf5")
4173 4172 print("LIST NEW", self.list_pedestal[:20])
4174 4173 #self.list_adq = self.getfirstFilefromPath(path=self.path_adq,meta="D",ext=".hdf5")
4175 4174 print("*************Longitud list pedestal****************",len(self.list_pedestal))
4176 4175
4177 4176 if self.online:
4178 4177 print("Enable Online")
4179 4178 self.setup_online(dataOut)
4180 4179 else:
4181 4180 #self.setup_offline(dataOut,list_pedestal=self.list_pedestal,list_adq=self.list_adq)
4182 4181 self.setup_offline(dataOut,list_pedestal=self.list_pedestal)
4183 4182
4184 4183
4185 4184 def setNextFileP(self,dataOut):
4186 4185 if self.online:
4187 4186 data_pedestal = self.setNextFileonline()
4188 4187 else:
4189 4188 data_pedestal = self.setNextFileoffline(dataOut)
4190 4189
4191 4190 return data_pedestal
4192 4191
4193 4192
4194 4193 def setNextFileoffline(self,dataOut):
4195 4194 ##tmp=0
4196 4195 for j in range(self.blocksPerfile):
4197 4196 ###print("NUMERO DEL BLOQUE---->",j)
4198 4197 ###print("nro_key_p",self.nro_key_p)
4199 4198
4200 4199 #iterador = self.nro_key_p +self.f_a_p*(j-tmp)
4201 4200 iterador = self.nro_key_p +self.f_a_p*self.c_ped
4202 4201 self.c_ped = self.c_ped +1
4203 4202
4204 4203 print("iterador------------->",iterador)
4205 4204 if iterador < self.n_Muestras_p:
4206 4205 self.nro_file = self.nro_file
4207 4206 else:
4208 4207 self.nro_file = self.nro_file+1
4209 4208 print("PRUEBA-------------")
4210 4209 utc_ped_setnext=self.gettimeutcfromDirFilename(path=self.path_ped,file=self.list_pedestal[self.nro_file])
4211 4210 utc_adq_setnext=dataOut.utctime
4212 4211 print("utc_pedestal",utc_ped_setnext)
4213 4212 print("utc_adq",utc_adq_setnext)
4214 4213
4215 4214 print("self.c_ped",self.c_ped)
4216 4215 #dif = self.blocksPerfile-(self.nro_key_p+self.f_a_p*(self.c_ped-2))
4217 4216 dif = self.n_Muestras_p-(self.nro_key_p+self.f_a_p*(self.c_ped-2))
4218 4217
4219 4218 self.c_ped = 1
4220 4219 ##tmp = j
4221 4220 ##print("tmp else",tmp)
4222 4221 self.nro_key_p= self.f_a_p-dif
4223 4222 iterador = self.nro_key_p
4224 4223 print("iterador else",iterador)
4225 4224 #self.c_ped = self.c_ped +1
4226 4225
4227 4226 print("nro_file",self.nro_file)
4228 4227 #print("tmp",tmp)
4229 4228 try:
4230 4229 ff_pedestal = self.list_pedestal[self.nro_file]
4231 4230 print("ff_pedestal",ff_pedestal)
4232 4231 except:
4233 4232 print("############# EXCEPCION ######################")
4234 4233 return numpy.ones(self.blocksPerfile)*numpy.nan
4235 4234
4236 4235 #angulo = self.getDatavaluefromDirFilename(path=self.path_ped,file=ff_pedestal,value="azimuth")
4237 4236 angulo = self.getDatavaluefromDirFilename(path=self.path_ped,file=ff_pedestal,value="azi_pos")
4238 4237
4239 4238 self.angulo_adq[j]= angulo[iterador]
4240 4239
4241 4240 return self.angulo_adq
4242 4241
4243 4242 def setNextFileonline(self):
4244 4243 tmp = 0
4245 4244 self.nTries_p = 3
4246 4245 self.delay = 3
4247 4246 ready = 1
4248 4247 for j in range(self.blocksPerfile):
4249 4248 iterador = self.nro_key_p +self.f_a_p*(j-tmp)
4250 4249 if iterador < self.n_Muestras_p:
4251 4250 self.nro_file = self.nro_file
4252 4251 else:
4253 4252 self.nro_file = self.nro_file+1
4254 4253 dif = self.blocksPerfile-(self.nro_key_p+self.f_a_p*(j-tmp-1))
4255 4254 tmp = j
4256 4255 self.nro_key_p= self.f_a_p-dif
4257 4256 iterador = self.nro_key_p
4258 4257 #print("nro_file---------------- :",self.nro_file)
4259 4258 try:
4260 4259 # update list_pedestal
4261 4260 self.list_pedestal = self.getfirstFilefromPath(path=self.path_ped,meta="PE",ext=".hdf5")
4262 4261 ff_pedestal = self.list_pedestal[self.nro_file]
4263 4262 except:
4264 4263 ff_pedestal = None
4265 4264 ready = 0
4266 4265 for nTries_p in range(self.nTries_p):
4267 4266 try:
4268 4267 # update list_pedestal
4269 4268 self.list_pedestal = self.getfirstFilefromPath(path=self.path_ped,meta="PE",ext=".hdf5")
4270 4269 ff_pedestal = self.list_pedestal[self.nro_file]
4271 4270 except:
4272 4271 ff_pedestal = None
4273 4272 if ff_pedestal is not None:
4274 4273 ready=1
4275 4274 break
4276 4275 log.warning("Waiting %0.2f sec for the next file: \"%s\" , try %02d ..." % (self.delay, self.nro_file, nTries_p + 1))
4277 4276 time.sleep(self.delay)
4278 4277 continue
4279 4278 #return numpy.ones(self.blocksPerfile)*numpy.nan
4280 4279
4281 4280 if ready == 1:
4282 4281 #angulo = self.getDatavaluefromDirFilename(path=self.path_ped,file=ff_pedestal,value="azimuth")
4283 4282 angulo = self.getDatavaluefromDirFilename(path=self.path_ped,file=ff_pedestal,value="azi_pos")
4284 4283
4285 4284 else:
4286 4285 print("there is no pedestal file")
4287 4286 angulo = numpy.ones(self.n_Muestras_p)*numpy.nan
4288 4287 self.angulo_adq[j]= angulo[iterador]
4289 4288 ####print("Angulo",self.angulo_adq)
4290 4289 ####print("Angulo",len(self.angulo_adq))
4291 4290 #self.nro_key_p=iterador + self.f_a_p
4292 4291 #if self.nro_key_p< self.n_Muestras_p:
4293 4292 # self.nro_file = self.nro_file
4294 4293 #else:
4295 4294 # self.nro_file = self.nro_file+1
4296 4295 # self.nro_key_p= self.nro_key_p
4297 4296 return self.angulo_adq
4298 4297
4299 4298
4300 4299 #def run(self, dataOut,path_ped,path_adq,t_Interval_p,n_Muestras_p,blocksPerfile,f_a_p,online):
4301 4300 def run(self, dataOut,path_ped,t_Interval_p,n_Muestras_p,blocksPerfile,f_a_p,online):
4302 4301
4303 4302 if not self.isConfig:
4304 4303 print("######################SETUP#########################################")
4305 4304 #self.setup( dataOut, path_ped,path_adq,t_Interval_p,n_Muestras_p,blocksPerfile,f_a_p,online)
4306 4305 self.setup( dataOut, path_ped,t_Interval_p,n_Muestras_p,blocksPerfile,f_a_p,online)
4307 4306 self.isConfig = True
4308 4307
4309 4308 dataOut.flagNoData = True
4310 4309 print("profIndex",self.__profIndex)
4311 4310
4312 4311 if self.__profIndex==0:
4313 4312 angulo_adq = self.setNextFileP(dataOut)
4314 4313 dataOut.azimuth = angulo_adq
4315 4314 print("TIEMPO:",dataOut.utctime)
4316 4315 ##print("####################################################################")
4317 4316 print("angulos",dataOut.azimuth,len(dataOut.azimuth))
4318 4317 self.__dataReady = True
4319 4318 self.__profIndex += 1
4320 4319 print("TIEMPO_bucle:",dataOut.utctime)
4321 4320 print("profIndex",self.__profIndex)
4322 4321 if self.__profIndex== blocksPerfile:
4323 4322 self.__profIndex = 0
4324 4323 if self.__dataReady:
4325 4324 #print(self.__profIndex,dataOut.azimuth[:10])
4326 4325 dataOut.flagNoData = False
4327 4326 return dataOut
4328 4327
4329 4328
4330 4329 class Block360(Operation):
4331 4330 '''
4332 4331 '''
4333 4332 isConfig = False
4334 4333 __profIndex = 0
4335 4334 __initime = None
4336 4335 __lastdatatime = None
4337 4336 __buffer = None
4338 4337 __dataReady = False
4339 4338 n = None
4340 4339 __nch = 0
4341 4340 __nHeis = 0
4342 4341 index = 0
4343 4342 mode = 0
4344 4343
4345 4344 def __init__(self,**kwargs):
4346 4345 Operation.__init__(self,**kwargs)
4347 4346
4348 4347 def setup(self, dataOut, n = None, mode = None):
4349 4348 '''
4350 4349 n= Numero de PRF's de entrada
4351 4350 '''
4352 4351 self.__initime = None
4353 4352 self.__lastdatatime = 0
4354 4353 self.__dataReady = False
4355 4354 self.__buffer = 0
4356 4355 self.__buffer_1D = 0
4357 4356 self.__profIndex = 0
4358 4357 self.index = 0
4359 4358 self.__nch = dataOut.nChannels
4360 4359 self.__nHeis = dataOut.nHeights
4361 4360 ##print("ELVALOR DE n es:", n)
4362 4361 if n == None:
4363 4362 raise ValueError("n should be specified.")
4364 4363
4365 4364 if mode == None:
4366 4365 raise ValueError("mode should be specified.")
4367 4366
4368 4367 if n != None:
4369 4368 if n<1:
4370 4369 print("n should be greater than 2")
4371 4370 raise ValueError("n should be greater than 2")
4372 4371
4373 4372 self.n = n
4374 4373 self.mode = mode
4375 4374 print("self.mode",self.mode)
4376 4375 #print("nHeights")
4377 4376 self.__buffer = numpy.zeros(( dataOut.nChannels,n, dataOut.nHeights))
4378 4377 self.__buffer2= numpy.zeros(n)
4379 4378
4380 4379 def putData(self,data,mode):
4381 4380 '''
4382 4381 Add a profile to he __buffer and increase in one the __profiel Index
4383 4382 '''
4384 4383 #print("line 4049",data.dataPP_POW.shape,data.dataPP_POW[:10])
4385 4384 #print("line 4049",data.azimuth.shape,data.azimuth)
4386 4385 if self.mode==0:
4387 self.__buffer[:,self.__profIndex,:]= data.dataPP_POW
4386 self.__buffer[:,self.__profIndex,:]= data.dataPP_POWER# PRIMER MOMENTO
4388 4387 if self.mode==1:
4389 4388 self.__buffer[:,self.__profIndex,:]= data.data_pow
4390 4389 #print("me casi",self.index,data.azimuth[self.index])
4391 4390 #print(self.__profIndex, self.index , data.azimuth[self.index] )
4392 4391 #print("magic",data.profileIndex)
4393 4392 #print(data.azimuth[self.index])
4394 4393 #print("index",self.index)
4395 4394
4396 4395 self.__buffer2[self.__profIndex] = data.azimuth[self.index]
4397 4396 #print("q pasa")
4398 4397 self.index+=1
4399 4398 #print("index",self.index,data.azimuth[:10])
4400 4399 self.__profIndex += 1
4401 4400 return #Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β· Remove DCΒ·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·
4402 4401
4403 4402 def pushData(self,data):
4404 4403 '''
4405 4404 Return the PULSEPAIR and the profiles used in the operation
4406 4405 Affected : self.__profileIndex
4407 4406 '''
4408 4407 #print("pushData")
4409 4408
4410 4409 data_360 = self.__buffer
4411 4410 data_p = self.__buffer2
4412 4411 n = self.__profIndex
4413 4412
4414 4413 self.__buffer = numpy.zeros((self.__nch, self.n,self.__nHeis))
4415 4414 self.__buffer2 = numpy.zeros(self.n)
4416 4415 self.__profIndex = 0
4417 4416 #print("pushData")
4418 4417 return data_360,n,data_p
4419 4418
4420 4419
4421 4420 def byProfiles(self,dataOut):
4422 4421
4423 4422 self.__dataReady = False
4424 4423 data_360 = None
4425 4424 data_p = None
4426 4425 #print("dataOu",dataOut.dataPP_POW)
4427 4426 self.putData(data=dataOut,mode = self.mode)
4428 4427 #print("profIndex",self.__profIndex)
4429 4428 if self.__profIndex == self.n:
4430 4429 data_360,n,data_p = self.pushData(data=dataOut)
4431 4430 self.__dataReady = True
4432 4431
4433 4432 return data_360,data_p
4434 4433
4435 4434
4436 4435 def blockOp(self, dataOut, datatime= None):
4437 4436 if self.__initime == None:
4438 4437 self.__initime = datatime
4439 4438 data_360,data_p = self.byProfiles(dataOut)
4440 4439 self.__lastdatatime = datatime
4441 4440
4442 4441 if data_360 is None:
4443 4442 return None, None,None
4444 4443
4445 4444 avgdatatime = self.__initime
4446 4445 deltatime = datatime - self.__lastdatatime
4447 4446 self.__initime = datatime
4448 4447 #print(data_360.shape,avgdatatime,data_p.shape)
4449 4448 return data_360,avgdatatime,data_p
4450 4449
4451 4450 def run(self, dataOut,n = None,mode=None,**kwargs):
4452 4451 print("BLOCK 360 HERE WE GO MOMENTOS")
4453 4452 if not self.isConfig:
4454 4453 self.setup(dataOut = dataOut, n = n ,mode= mode ,**kwargs)
4455 4454 self.index = 0
4456 4455 #print("comova",self.isConfig)
4457 4456 self.isConfig = True
4458 4457 if self.index==dataOut.azimuth.shape[0]:
4459 4458 self.index=0
4460 4459 data_360, avgdatatime,data_p = self.blockOp(dataOut, dataOut.utctime)
4461 4460 dataOut.flagNoData = True
4462 4461
4463 4462 if self.__dataReady:
4464 4463 dataOut.data_360 = data_360 # S
4465 4464 ##print("---------------------------------------------------------------------------------")
4466 4465 ##print("---------------------------DATAREADY---------------------------------------------")
4467 4466 ##print("---------------------------------------------------------------------------------")
4468 4467 ##print("data_360",dataOut.data_360.shape)
4469 4468 dataOut.data_azi = data_p
4470 4469 ##print("azi: ",dataOut.data_azi)
4471 4470 #print("jroproc_parameters",data_p[0],data_p[-1])#,data_360.shape,avgdatatime)
4472 4471 dataOut.utctime = avgdatatime
4473 4472 dataOut.flagNoData = False
4474 4473 return dataOut
@@ -1,898 +1,898
1 1 # Copyright (c) 2012-2020 Jicamarca Radio Observatory
2 2 # All rights reserved.
3 3 #
4 4 # Distributed under the terms of the BSD 3-clause license.
5 5 """Spectra processing Unit and operations
6 6
7 7 Here you will find the processing unit `SpectraProc` and several operations
8 8 to work with Spectra data type
9 9 """
10 10
11 11 import time
12 12 import itertools
13 13
14 14 import numpy
15 15
16 16 from schainpy.model.proc.jroproc_base import ProcessingUnit, MPDecorator, Operation
17 17 from schainpy.model.data.jrodata import Spectra
18 18 from schainpy.model.data.jrodata import hildebrand_sekhon
19 19 from schainpy.utils import log
20 20
21 21
22 22 class SpectraProc(ProcessingUnit):
23 23
24 24 def __init__(self):
25 25
26 26 ProcessingUnit.__init__(self)
27 27
28 28 self.buffer = None
29 29 self.firstdatatime = None
30 30 self.profIndex = 0
31 31 self.dataOut = Spectra()
32 32 self.id_min = None
33 33 self.id_max = None
34 34 self.setupReq = False #Agregar a todas las unidades de proc
35 35
36 36 def __updateSpecFromVoltage(self):
37 37
38 38 self.dataOut.timeZone = self.dataIn.timeZone
39 39 self.dataOut.dstFlag = self.dataIn.dstFlag
40 40 self.dataOut.errorCount = self.dataIn.errorCount
41 41 self.dataOut.useLocalTime = self.dataIn.useLocalTime
42 42 try:
43 43 self.dataOut.processingHeaderObj = self.dataIn.processingHeaderObj.copy()
44 44 except:
45 45 pass
46 46 self.dataOut.radarControllerHeaderObj = self.dataIn.radarControllerHeaderObj.copy()
47 47 self.dataOut.systemHeaderObj = self.dataIn.systemHeaderObj.copy()
48 48 self.dataOut.channelList = self.dataIn.channelList
49 49 self.dataOut.heightList = self.dataIn.heightList
50 50 self.dataOut.dtype = numpy.dtype([('real', '<f4'), ('imag', '<f4')])
51 51 self.dataOut.nProfiles = self.dataOut.nFFTPoints
52 52 self.dataOut.flagDiscontinuousBlock = self.dataIn.flagDiscontinuousBlock
53 53 self.dataOut.utctime = self.firstdatatime
54 54 self.dataOut.flagDecodeData = self.dataIn.flagDecodeData
55 55 self.dataOut.flagDeflipData = self.dataIn.flagDeflipData
56 56 self.dataOut.flagShiftFFT = False
57 57 self.dataOut.nCohInt = self.dataIn.nCohInt
58 58 self.dataOut.nIncohInt = 1
59 59 self.dataOut.windowOfFilter = self.dataIn.windowOfFilter
60 60 self.dataOut.frequency = self.dataIn.frequency
61 61 self.dataOut.realtime = self.dataIn.realtime
62 62 self.dataOut.azimuth = self.dataIn.azimuth
63 63 self.dataOut.zenith = self.dataIn.zenith
64 64 self.dataOut.beam.codeList = self.dataIn.beam.codeList
65 65 self.dataOut.beam.azimuthList = self.dataIn.beam.azimuthList
66 66 self.dataOut.beam.zenithList = self.dataIn.beam.zenithList
67 67
68 68 def __getFft(self):
69 69 """
70 70 Convierte valores de Voltaje a Spectra
71 71
72 72 Affected:
73 73 self.dataOut.data_spc
74 74 self.dataOut.data_cspc
75 75 self.dataOut.data_dc
76 76 self.dataOut.heightList
77 77 self.profIndex
78 78 self.buffer
79 79 self.dataOut.flagNoData
80 80 """
81 81 fft_volt = numpy.fft.fft(
82 82 self.buffer, n=self.dataOut.nFFTPoints, axis=1)
83 83 fft_volt = fft_volt.astype(numpy.dtype('complex'))
84 84 dc = fft_volt[:, 0, :]
85 85
86 86 # calculo de self-spectra
87 87 fft_volt = numpy.fft.fftshift(fft_volt, axes=(1,))
88 88 spc = fft_volt * numpy.conjugate(fft_volt)
89 89 spc = spc.real
90 90
91 91 blocksize = 0
92 92 blocksize += dc.size
93 93 blocksize += spc.size
94 94
95 95 cspc = None
96 96 pairIndex = 0
97 97 if self.dataOut.pairsList != None:
98 98 # calculo de cross-spectra
99 99 cspc = numpy.zeros(
100 100 (self.dataOut.nPairs, self.dataOut.nFFTPoints, self.dataOut.nHeights), dtype='complex')
101 101 for pair in self.dataOut.pairsList:
102 102 if pair[0] not in self.dataOut.channelList:
103 103 raise ValueError("Error getting CrossSpectra: pair 0 of %s is not in channelList = %s" % (
104 104 str(pair), str(self.dataOut.channelList)))
105 105 if pair[1] not in self.dataOut.channelList:
106 106 raise ValueError("Error getting CrossSpectra: pair 1 of %s is not in channelList = %s" % (
107 107 str(pair), str(self.dataOut.channelList)))
108 108
109 109 cspc[pairIndex, :, :] = fft_volt[pair[0], :, :] * \
110 110 numpy.conjugate(fft_volt[pair[1], :, :])
111 111 pairIndex += 1
112 112 blocksize += cspc.size
113 113
114 114 self.dataOut.data_spc = spc
115 115 self.dataOut.data_cspc = cspc
116 116 self.dataOut.data_dc = dc
117 117 self.dataOut.blockSize = blocksize
118 118 self.dataOut.flagShiftFFT = False
119 119
120 120 def run(self, nProfiles=None, nFFTPoints=None, pairsList=None, ippFactor=None, shift_fft=False):
121
121
122 122 if self.dataIn.type == "Spectra":
123 123 self.dataOut.copy(self.dataIn)
124 124 if shift_fft:
125 125 #desplaza a la derecha en el eje 2 determinadas posiciones
126 126 shift = int(self.dataOut.nFFTPoints/2)
127 127 self.dataOut.data_spc = numpy.roll(self.dataOut.data_spc, shift , axis=1)
128 128
129 129 if self.dataOut.data_cspc is not None:
130 130 #desplaza a la derecha en el eje 2 determinadas posiciones
131 131 self.dataOut.data_cspc = numpy.roll(self.dataOut.data_cspc, shift, axis=1)
132 132 if pairsList:
133 133 self.__selectPairs(pairsList)
134 134
135 135 elif self.dataIn.type == "Voltage":
136 136
137 137 self.dataOut.flagNoData = True
138 138
139 139 if nFFTPoints == None:
140 140 raise ValueError("This SpectraProc.run() need nFFTPoints input variable")
141 141
142 142 if nProfiles == None:
143 143 nProfiles = nFFTPoints
144 144
145 145 if ippFactor == None:
146 146 self.dataOut.ippFactor = 1
147
147
148 148 self.dataOut.nFFTPoints = nFFTPoints
149 149
150 150 if self.buffer is None:
151 151 self.buffer = numpy.zeros((self.dataIn.nChannels,
152 152 nProfiles,
153 153 self.dataIn.nHeights),
154 154 dtype='complex')
155 155
156 156 if self.dataIn.flagDataAsBlock:
157 157 nVoltProfiles = self.dataIn.data.shape[1]
158 158
159 159 if nVoltProfiles == nProfiles:
160 160 self.buffer = self.dataIn.data.copy()
161 161 self.profIndex = nVoltProfiles
162 162
163 163 elif nVoltProfiles < nProfiles:
164 164
165 165 if self.profIndex == 0:
166 166 self.id_min = 0
167 167 self.id_max = nVoltProfiles
168 168
169 169 self.buffer[:, self.id_min:self.id_max,
170 170 :] = self.dataIn.data
171 171 self.profIndex += nVoltProfiles
172 172 self.id_min += nVoltProfiles
173 173 self.id_max += nVoltProfiles
174 174 else:
175 175 raise ValueError("The type object %s has %d profiles, it should just has %d profiles" % (
176 176 self.dataIn.type, self.dataIn.data.shape[1], nProfiles))
177 177 self.dataOut.flagNoData = True
178 178 else:
179 179 self.buffer[:, self.profIndex, :] = self.dataIn.data.copy()
180 180 self.profIndex += 1
181 181
182 182 if self.firstdatatime == None:
183 183 self.firstdatatime = self.dataIn.utctime
184 184
185 185 if self.profIndex == nProfiles:
186 186 self.__updateSpecFromVoltage()
187 187 if pairsList == None:
188 188 self.dataOut.pairsList = [pair for pair in itertools.combinations(self.dataOut.channelList, 2)]
189 189 else:
190 190 self.dataOut.pairsList = pairsList
191 191 self.__getFft()
192 192 self.dataOut.flagNoData = False
193 193 self.firstdatatime = None
194 194 self.profIndex = 0
195 195 else:
196 196 raise ValueError("The type of input object '%s' is not valid".format(
197 197 self.dataIn.type))
198 198
199 199 def __selectPairs(self, pairsList):
200 200
201 201 if not pairsList:
202 202 return
203 203
204 204 pairs = []
205 205 pairsIndex = []
206 206
207 207 for pair in pairsList:
208 208 if pair[0] not in self.dataOut.channelList or pair[1] not in self.dataOut.channelList:
209 209 continue
210 210 pairs.append(pair)
211 211 pairsIndex.append(pairs.index(pair))
212 212
213 213 self.dataOut.data_cspc = self.dataOut.data_cspc[pairsIndex]
214 214 self.dataOut.pairsList = pairs
215 215
216 216 return
217
217
218 218 def selectFFTs(self, minFFT, maxFFT ):
219 219 """
220 Selecciona un bloque de datos en base a un grupo de valores de puntos FFTs segun el rango
220 Selecciona un bloque de datos en base a un grupo de valores de puntos FFTs segun el rango
221 221 minFFT<= FFT <= maxFFT
222 222 """
223
223
224 224 if (minFFT > maxFFT):
225 225 raise ValueError("Error selecting heights: Height range (%d,%d) is not valid" % (minFFT, maxFFT))
226 226
227 227 if (minFFT < self.dataOut.getFreqRange()[0]):
228 228 minFFT = self.dataOut.getFreqRange()[0]
229 229
230 230 if (maxFFT > self.dataOut.getFreqRange()[-1]):
231 231 maxFFT = self.dataOut.getFreqRange()[-1]
232 232
233 233 minIndex = 0
234 234 maxIndex = 0
235 235 FFTs = self.dataOut.getFreqRange()
236 236
237 237 inda = numpy.where(FFTs >= minFFT)
238 238 indb = numpy.where(FFTs <= maxFFT)
239 239
240 240 try:
241 241 minIndex = inda[0][0]
242 242 except:
243 243 minIndex = 0
244 244
245 245 try:
246 246 maxIndex = indb[0][-1]
247 247 except:
248 248 maxIndex = len(FFTs)
249 249
250 250 self.selectFFTsByIndex(minIndex, maxIndex)
251 251
252 252 return 1
253
253
254 254 def getBeaconSignal(self, tauindex=0, channelindex=0, hei_ref=None):
255 255 newheis = numpy.where(
256 256 self.dataOut.heightList > self.dataOut.radarControllerHeaderObj.Taus[tauindex])
257 257
258 258 if hei_ref != None:
259 259 newheis = numpy.where(self.dataOut.heightList > hei_ref)
260 260
261 261 minIndex = min(newheis[0])
262 262 maxIndex = max(newheis[0])
263 263 data_spc = self.dataOut.data_spc[:, :, minIndex:maxIndex + 1]
264 264 heightList = self.dataOut.heightList[minIndex:maxIndex + 1]
265 265
266 266 # determina indices
267 267 nheis = int(self.dataOut.radarControllerHeaderObj.txB /
268 268 (self.dataOut.heightList[1] - self.dataOut.heightList[0]))
269 269 avg_dB = 10 * \
270 270 numpy.log10(numpy.sum(data_spc[channelindex, :, :], axis=0))
271 271 beacon_dB = numpy.sort(avg_dB)[-nheis:]
272 272 beacon_heiIndexList = []
273 273 for val in avg_dB.tolist():
274 274 if val >= beacon_dB[0]:
275 275 beacon_heiIndexList.append(avg_dB.tolist().index(val))
276 276
277 277 #data_spc = data_spc[:,:,beacon_heiIndexList]
278 278 data_cspc = None
279 279 if self.dataOut.data_cspc is not None:
280 280 data_cspc = self.dataOut.data_cspc[:, :, minIndex:maxIndex + 1]
281 281 #data_cspc = data_cspc[:,:,beacon_heiIndexList]
282 282
283 283 data_dc = None
284 284 if self.dataOut.data_dc is not None:
285 285 data_dc = self.dataOut.data_dc[:, minIndex:maxIndex + 1]
286 286 #data_dc = data_dc[:,beacon_heiIndexList]
287 287
288 288 self.dataOut.data_spc = data_spc
289 289 self.dataOut.data_cspc = data_cspc
290 290 self.dataOut.data_dc = data_dc
291 291 self.dataOut.heightList = heightList
292 292 self.dataOut.beacon_heiIndexList = beacon_heiIndexList
293 293
294 294 return 1
295 295
296 296 def selectFFTsByIndex(self, minIndex, maxIndex):
297 297 """
298
298
299 299 """
300 300
301 301 if (minIndex < 0) or (minIndex > maxIndex):
302 302 raise ValueError("Error selecting heights: Index range (%d,%d) is not valid" % (minIndex, maxIndex))
303 303
304 304 if (maxIndex >= self.dataOut.nProfiles):
305 305 maxIndex = self.dataOut.nProfiles-1
306 306
307 307 #Spectra
308 308 data_spc = self.dataOut.data_spc[:,minIndex:maxIndex+1,:]
309 309
310 310 data_cspc = None
311 311 if self.dataOut.data_cspc is not None:
312 312 data_cspc = self.dataOut.data_cspc[:,minIndex:maxIndex+1,:]
313 313
314 314 data_dc = None
315 315 if self.dataOut.data_dc is not None:
316 316 data_dc = self.dataOut.data_dc[minIndex:maxIndex+1,:]
317 317
318 318 self.dataOut.data_spc = data_spc
319 319 self.dataOut.data_cspc = data_cspc
320 320 self.dataOut.data_dc = data_dc
321
321
322 322 self.dataOut.ippSeconds = self.dataOut.ippSeconds*(self.dataOut.nFFTPoints / numpy.shape(data_cspc)[1])
323 323 self.dataOut.nFFTPoints = numpy.shape(data_cspc)[1]
324 324 self.dataOut.profilesPerBlock = numpy.shape(data_cspc)[1]
325 325
326 326 return 1
327 327
328 328 def getNoise(self, minHei=None, maxHei=None, minVel=None, maxVel=None):
329 329 # validacion de rango
330 330 if minHei == None:
331 331 minHei = self.dataOut.heightList[0]
332 332
333 333 if maxHei == None:
334 334 maxHei = self.dataOut.heightList[-1]
335 335
336 336 if (minHei < self.dataOut.heightList[0]) or (minHei > maxHei):
337 337 print('minHei: %.2f is out of the heights range' % (minHei))
338 338 print('minHei is setting to %.2f' % (self.dataOut.heightList[0]))
339 339 minHei = self.dataOut.heightList[0]
340 340
341 341 if (maxHei > self.dataOut.heightList[-1]) or (maxHei < minHei):
342 342 print('maxHei: %.2f is out of the heights range' % (maxHei))
343 343 print('maxHei is setting to %.2f' % (self.dataOut.heightList[-1]))
344 344 maxHei = self.dataOut.heightList[-1]
345 345
346 346 # validacion de velocidades
347 347 velrange = self.dataOut.getVelRange(1)
348 348
349 349 if minVel == None:
350 350 minVel = velrange[0]
351 351
352 352 if maxVel == None:
353 353 maxVel = velrange[-1]
354 354
355 355 if (minVel < velrange[0]) or (minVel > maxVel):
356 356 print('minVel: %.2f is out of the velocity range' % (minVel))
357 357 print('minVel is setting to %.2f' % (velrange[0]))
358 358 minVel = velrange[0]
359 359
360 360 if (maxVel > velrange[-1]) or (maxVel < minVel):
361 361 print('maxVel: %.2f is out of the velocity range' % (maxVel))
362 362 print('maxVel is setting to %.2f' % (velrange[-1]))
363 363 maxVel = velrange[-1]
364 364
365 365 # seleccion de indices para rango
366 366 minIndex = 0
367 367 maxIndex = 0
368 368 heights = self.dataOut.heightList
369 369
370 370 inda = numpy.where(heights >= minHei)
371 371 indb = numpy.where(heights <= maxHei)
372 372
373 373 try:
374 374 minIndex = inda[0][0]
375 375 except:
376 376 minIndex = 0
377 377
378 378 try:
379 379 maxIndex = indb[0][-1]
380 380 except:
381 381 maxIndex = len(heights)
382 382
383 383 if (minIndex < 0) or (minIndex > maxIndex):
384 384 raise ValueError("some value in (%d,%d) is not valid" % (
385 385 minIndex, maxIndex))
386 386
387 387 if (maxIndex >= self.dataOut.nHeights):
388 388 maxIndex = self.dataOut.nHeights - 1
389 389
390 390 # seleccion de indices para velocidades
391 391 indminvel = numpy.where(velrange >= minVel)
392 392 indmaxvel = numpy.where(velrange <= maxVel)
393 393 try:
394 394 minIndexVel = indminvel[0][0]
395 395 except:
396 396 minIndexVel = 0
397 397
398 398 try:
399 399 maxIndexVel = indmaxvel[0][-1]
400 400 except:
401 401 maxIndexVel = len(velrange)
402 402
403 403 # seleccion del espectro
404 404 data_spc = self.dataOut.data_spc[:,
405 405 minIndexVel:maxIndexVel + 1, minIndex:maxIndex + 1]
406 406 # estimacion de ruido
407 407 noise = numpy.zeros(self.dataOut.nChannels)
408 408
409 409 for channel in range(self.dataOut.nChannels):
410 410 daux = data_spc[channel, :, :]
411 411 sortdata = numpy.sort(daux, axis=None)
412 412 noise[channel] = hildebrand_sekhon(sortdata, self.dataOut.nIncohInt)
413 413
414 414 self.dataOut.noise_estimation = noise.copy()
415 415
416 416 return 1
417 417
418 418 class removeDC(Operation):
419 419
420 420 def run(self, dataOut, mode=2):
421 421 self.dataOut = dataOut
422 422 jspectra = self.dataOut.data_spc
423 423 jcspectra = self.dataOut.data_cspc
424 424
425 425 num_chan = jspectra.shape[0]
426 426 num_hei = jspectra.shape[2]
427 427
428 428 if jcspectra is not None:
429 429 jcspectraExist = True
430 430 num_pairs = jcspectra.shape[0]
431 431 else:
432 432 jcspectraExist = False
433 433
434 434 freq_dc = int(jspectra.shape[1] / 2)
435 435 ind_vel = numpy.array([-2, -1, 1, 2]) + freq_dc
436 436 ind_vel = ind_vel.astype(int)
437 437
438 438 if ind_vel[0] < 0:
439 439 ind_vel[list(range(0, 1))] = ind_vel[list(range(0, 1))] + self.num_prof
440 440
441 441 if mode == 1:
442 442 jspectra[:, freq_dc, :] = (
443 443 jspectra[:, ind_vel[1], :] + jspectra[:, ind_vel[2], :]) / 2 # CORRECCION
444 444
445 445 if jcspectraExist:
446 446 jcspectra[:, freq_dc, :] = (
447 447 jcspectra[:, ind_vel[1], :] + jcspectra[:, ind_vel[2], :]) / 2
448 448
449 449 if mode == 2:
450 450
451 451 vel = numpy.array([-2, -1, 1, 2])
452 452 xx = numpy.zeros([4, 4])
453 453
454 454 for fil in range(4):
455 455 xx[fil, :] = vel[fil]**numpy.asarray(list(range(4)))
456 456
457 457 xx_inv = numpy.linalg.inv(xx)
458 458 xx_aux = xx_inv[0, :]
459 459
460 for ich in range(num_chan):
460 for ich in range(num_chan):
461 461 yy = jspectra[ich, ind_vel, :]
462 462 jspectra[ich, freq_dc, :] = numpy.dot(xx_aux, yy)
463 463
464 464 junkid = jspectra[ich, freq_dc, :] <= 0
465 465 cjunkid = sum(junkid)
466 466
467 467 if cjunkid.any():
468 468 jspectra[ich, freq_dc, junkid.nonzero()] = (
469 469 jspectra[ich, ind_vel[1], junkid] + jspectra[ich, ind_vel[2], junkid]) / 2
470 470
471 471 if jcspectraExist:
472 472 for ip in range(num_pairs):
473 473 yy = jcspectra[ip, ind_vel, :]
474 474 jcspectra[ip, freq_dc, :] = numpy.dot(xx_aux, yy)
475 475
476 476 self.dataOut.data_spc = jspectra
477 477 self.dataOut.data_cspc = jcspectra
478 478
479 479 return self.dataOut
480 480
481 481 class removeInterference(Operation):
482 482
483 483 def removeInterference2(self):
484
484
485 485 cspc = self.dataOut.data_cspc
486 486 spc = self.dataOut.data_spc
487 Heights = numpy.arange(cspc.shape[2])
487 Heights = numpy.arange(cspc.shape[2])
488 488 realCspc = numpy.abs(cspc)
489
489
490 490 for i in range(cspc.shape[0]):
491 491 LinePower= numpy.sum(realCspc[i], axis=0)
492 492 Threshold = numpy.amax(LinePower)-numpy.sort(LinePower)[len(Heights)-int(len(Heights)*0.1)]
493 493 SelectedHeights = Heights[ numpy.where( LinePower < Threshold ) ]
494 494 InterferenceSum = numpy.sum( realCspc[i,:,SelectedHeights], axis=0 )
495 495 InterferenceThresholdMin = numpy.sort(InterferenceSum)[int(len(InterferenceSum)*0.98)]
496 496 InterferenceThresholdMax = numpy.sort(InterferenceSum)[int(len(InterferenceSum)*0.99)]
497
498
497
498
499 499 InterferenceRange = numpy.where( ([InterferenceSum > InterferenceThresholdMin]))# , InterferenceSum < InterferenceThresholdMax]) )
500 500 #InterferenceRange = numpy.where( ([InterferenceRange < InterferenceThresholdMax]))
501 501 if len(InterferenceRange)<int(cspc.shape[1]*0.3):
502 502 cspc[i,InterferenceRange,:] = numpy.NaN
503
503
504 504 self.dataOut.data_cspc = cspc
505
505
506 506 def removeInterference(self, interf = 2, hei_interf = None, nhei_interf = None, offhei_interf = None):
507 507
508 508 jspectra = self.dataOut.data_spc
509 509 jcspectra = self.dataOut.data_cspc
510 510 jnoise = self.dataOut.getNoise()
511 511 num_incoh = self.dataOut.nIncohInt
512 512
513 513 num_channel = jspectra.shape[0]
514 514 num_prof = jspectra.shape[1]
515 515 num_hei = jspectra.shape[2]
516 516
517 517 # hei_interf
518 518 if hei_interf is None:
519 519 count_hei = int(num_hei / 2)
520 520 hei_interf = numpy.asmatrix(list(range(count_hei))) + num_hei - count_hei
521 521 hei_interf = numpy.asarray(hei_interf)[0]
522 522 # nhei_interf
523 523 if (nhei_interf == None):
524 524 nhei_interf = 5
525 525 if (nhei_interf < 1):
526 526 nhei_interf = 1
527 527 if (nhei_interf > count_hei):
528 528 nhei_interf = count_hei
529 529 if (offhei_interf == None):
530 530 offhei_interf = 0
531 531
532 532 ind_hei = list(range(num_hei))
533 533 # mask_prof = numpy.asarray(range(num_prof - 2)) + 1
534 534 # mask_prof[range(num_prof/2 - 1,len(mask_prof))] += 1
535 535 mask_prof = numpy.asarray(list(range(num_prof)))
536 536 num_mask_prof = mask_prof.size
537 537 comp_mask_prof = [0, num_prof / 2]
538 538
539 539 # noise_exist: Determina si la variable jnoise ha sido definida y contiene la informacion del ruido de cada canal
540 540 if (jnoise.size < num_channel or numpy.isnan(jnoise).any()):
541 541 jnoise = numpy.nan
542 542 noise_exist = jnoise[0] < numpy.Inf
543 543
544 544 # Subrutina de Remocion de la Interferencia
545 545 for ich in range(num_channel):
546 546 # Se ordena los espectros segun su potencia (menor a mayor)
547 547 power = jspectra[ich, mask_prof, :]
548 548 power = power[:, hei_interf]
549 549 power = power.sum(axis=0)
550 550 psort = power.ravel().argsort()
551 551
552 552 # Se estima la interferencia promedio en los Espectros de Potencia empleando
553 553 junkspc_interf = jspectra[ich, :, hei_interf[psort[list(range(
554 554 offhei_interf, nhei_interf + offhei_interf))]]]
555 555
556 556 if noise_exist:
557 557 # tmp_noise = jnoise[ich] / num_prof
558 558 tmp_noise = jnoise[ich]
559 559 junkspc_interf = junkspc_interf - tmp_noise
560 560 #junkspc_interf[:,comp_mask_prof] = 0
561 561
562 562 jspc_interf = junkspc_interf.sum(axis=0) / nhei_interf
563 563 jspc_interf = jspc_interf.transpose()
564 564 # Calculando el espectro de interferencia promedio
565 565 noiseid = numpy.where(
566 566 jspc_interf <= tmp_noise / numpy.sqrt(num_incoh))
567 567 noiseid = noiseid[0]
568 568 cnoiseid = noiseid.size
569 569 interfid = numpy.where(
570 570 jspc_interf > tmp_noise / numpy.sqrt(num_incoh))
571 571 interfid = interfid[0]
572 572 cinterfid = interfid.size
573 573
574 574 if (cnoiseid > 0):
575 575 jspc_interf[noiseid] = 0
576 576
577 577 # Expandiendo los perfiles a limpiar
578 578 if (cinterfid > 0):
579 579 new_interfid = (
580 580 numpy.r_[interfid - 1, interfid, interfid + 1] + num_prof) % num_prof
581 581 new_interfid = numpy.asarray(new_interfid)
582 582 new_interfid = {x for x in new_interfid}
583 583 new_interfid = numpy.array(list(new_interfid))
584 584 new_cinterfid = new_interfid.size
585 585 else:
586 586 new_cinterfid = 0
587 587
588 588 for ip in range(new_cinterfid):
589 589 ind = junkspc_interf[:, new_interfid[ip]].ravel().argsort()
590 590 jspc_interf[new_interfid[ip]
591 591 ] = junkspc_interf[ind[nhei_interf // 2], new_interfid[ip]]
592 592
593 593 jspectra[ich, :, ind_hei] = jspectra[ich, :,
594 594 ind_hei] - jspc_interf # Corregir indices
595 595
596 596 # Removiendo la interferencia del punto de mayor interferencia
597 597 ListAux = jspc_interf[mask_prof].tolist()
598 598 maxid = ListAux.index(max(ListAux))
599 599
600 600 if cinterfid > 0:
601 601 for ip in range(cinterfid * (interf == 2) - 1):
602 602 ind = (jspectra[ich, interfid[ip], :] < tmp_noise *
603 603 (1 + 1 / numpy.sqrt(num_incoh))).nonzero()
604 604 cind = len(ind)
605 605
606 606 if (cind > 0):
607 607 jspectra[ich, interfid[ip], ind] = tmp_noise * \
608 608 (1 + (numpy.random.uniform(cind) - 0.5) /
609 609 numpy.sqrt(num_incoh))
610 610
611 611 ind = numpy.array([-2, -1, 1, 2])
612 612 xx = numpy.zeros([4, 4])
613 613
614 614 for id1 in range(4):
615 615 xx[:, id1] = ind[id1]**numpy.asarray(list(range(4)))
616 616
617 617 xx_inv = numpy.linalg.inv(xx)
618 618 xx = xx_inv[:, 0]
619 619 ind = (ind + maxid + num_mask_prof) % num_mask_prof
620 620 yy = jspectra[ich, mask_prof[ind], :]
621 621 jspectra[ich, mask_prof[maxid], :] = numpy.dot(
622 622 yy.transpose(), xx)
623 623
624 624 indAux = (jspectra[ich, :, :] < tmp_noise *
625 625 (1 - 1 / numpy.sqrt(num_incoh))).nonzero()
626 626 jspectra[ich, indAux[0], indAux[1]] = tmp_noise * \
627 627 (1 - 1 / numpy.sqrt(num_incoh))
628 628
629 629 # Remocion de Interferencia en el Cross Spectra
630 630 if jcspectra is None:
631 631 return jspectra, jcspectra
632 632 num_pairs = int(jcspectra.size / (num_prof * num_hei))
633 633 jcspectra = jcspectra.reshape(num_pairs, num_prof, num_hei)
634 634
635 635 for ip in range(num_pairs):
636 636
637 637 #-------------------------------------------
638 638
639 639 cspower = numpy.abs(jcspectra[ip, mask_prof, :])
640 640 cspower = cspower[:, hei_interf]
641 641 cspower = cspower.sum(axis=0)
642 642
643 643 cspsort = cspower.ravel().argsort()
644 644 junkcspc_interf = jcspectra[ip, :, hei_interf[cspsort[list(range(
645 645 offhei_interf, nhei_interf + offhei_interf))]]]
646 646 junkcspc_interf = junkcspc_interf.transpose()
647 647 jcspc_interf = junkcspc_interf.sum(axis=1) / nhei_interf
648 648
649 649 ind = numpy.abs(jcspc_interf[mask_prof]).ravel().argsort()
650 650
651 651 median_real = int(numpy.median(numpy.real(
652 652 junkcspc_interf[mask_prof[ind[list(range(3 * num_prof // 4))]], :])))
653 653 median_imag = int(numpy.median(numpy.imag(
654 654 junkcspc_interf[mask_prof[ind[list(range(3 * num_prof // 4))]], :])))
655 655 comp_mask_prof = [int(e) for e in comp_mask_prof]
656 656 junkcspc_interf[comp_mask_prof, :] = numpy.complex(
657 657 median_real, median_imag)
658 658
659 659 for iprof in range(num_prof):
660 660 ind = numpy.abs(junkcspc_interf[iprof, :]).ravel().argsort()
661 661 jcspc_interf[iprof] = junkcspc_interf[iprof, ind[nhei_interf // 2]]
662 662
663 663 # Removiendo la Interferencia
664 664 jcspectra[ip, :, ind_hei] = jcspectra[ip,
665 665 :, ind_hei] - jcspc_interf
666 666
667 667 ListAux = numpy.abs(jcspc_interf[mask_prof]).tolist()
668 668 maxid = ListAux.index(max(ListAux))
669 669
670 670 ind = numpy.array([-2, -1, 1, 2])
671 671 xx = numpy.zeros([4, 4])
672 672
673 673 for id1 in range(4):
674 674 xx[:, id1] = ind[id1]**numpy.asarray(list(range(4)))
675 675
676 676 xx_inv = numpy.linalg.inv(xx)
677 677 xx = xx_inv[:, 0]
678 678
679 679 ind = (ind + maxid + num_mask_prof) % num_mask_prof
680 680 yy = jcspectra[ip, mask_prof[ind], :]
681 681 jcspectra[ip, mask_prof[maxid], :] = numpy.dot(yy.transpose(), xx)
682 682
683 683 # Guardar Resultados
684 684 self.dataOut.data_spc = jspectra
685 685 self.dataOut.data_cspc = jcspectra
686 686
687 687 return 1
688 688
689 689 def run(self, dataOut, interf = 2,hei_interf = None, nhei_interf = None, offhei_interf = None, mode=1):
690 690
691 691 self.dataOut = dataOut
692 692
693 693 if mode == 1:
694 694 self.removeInterference(interf = 2,hei_interf = None, nhei_interf = None, offhei_interf = None)
695 695 elif mode == 2:
696 696 self.removeInterference2()
697 697
698 698 return self.dataOut
699 699
700 700
701 701 class IncohInt(Operation):
702 702
703 703 __profIndex = 0
704 704 __withOverapping = False
705 705
706 706 __byTime = False
707 707 __initime = None
708 708 __lastdatatime = None
709 709 __integrationtime = None
710 710
711 711 __buffer_spc = None
712 712 __buffer_cspc = None
713 713 __buffer_dc = None
714 714
715 715 __dataReady = False
716 716
717 717 __timeInterval = None
718 718
719 719 n = None
720 720
721 721 def __init__(self):
722 722
723 723 Operation.__init__(self)
724 724
725 725 def setup(self, n=None, timeInterval=None, overlapping=False):
726 726 """
727 727 Set the parameters of the integration class.
728 728
729 729 Inputs:
730 730
731 731 n : Number of coherent integrations
732 732 timeInterval : Time of integration. If the parameter "n" is selected this one does not work
733 733 overlapping :
734 734
735 735 """
736 736
737 737 self.__initime = None
738 738 self.__lastdatatime = 0
739 739
740 740 self.__buffer_spc = 0
741 741 self.__buffer_cspc = 0
742 742 self.__buffer_dc = 0
743 743
744 744 self.__profIndex = 0
745 745 self.__dataReady = False
746 746 self.__byTime = False
747 747
748 748 if n is None and timeInterval is None:
749 749 raise ValueError("n or timeInterval should be specified ...")
750 750
751 751 if n is not None:
752 752 self.n = int(n)
753 753 else:
754
754
755 755 self.__integrationtime = int(timeInterval)
756 756 self.n = None
757 757 self.__byTime = True
758 758
759 759 def putData(self, data_spc, data_cspc, data_dc):
760 760 """
761 761 Add a profile to the __buffer_spc and increase in one the __profileIndex
762 762
763 763 """
764 764
765 765 self.__buffer_spc += data_spc
766 766
767 767 if data_cspc is None:
768 768 self.__buffer_cspc = None
769 769 else:
770 770 self.__buffer_cspc += data_cspc
771 771
772 772 if data_dc is None:
773 773 self.__buffer_dc = None
774 774 else:
775 775 self.__buffer_dc += data_dc
776 776
777 777 self.__profIndex += 1
778 778
779 779 return
780 780
781 781 def pushData(self):
782 782 """
783 783 Return the sum of the last profiles and the profiles used in the sum.
784 784
785 785 Affected:
786 786
787 787 self.__profileIndex
788 788
789 789 """
790 790
791 791 data_spc = self.__buffer_spc
792 792 data_cspc = self.__buffer_cspc
793 793 data_dc = self.__buffer_dc
794 794 n = self.__profIndex
795 795
796 796 self.__buffer_spc = 0
797 797 self.__buffer_cspc = 0
798 798 self.__buffer_dc = 0
799 799 self.__profIndex = 0
800 800
801 801 return data_spc, data_cspc, data_dc, n
802 802
803 803 def byProfiles(self, *args):
804 804
805 805 self.__dataReady = False
806 806 avgdata_spc = None
807 807 avgdata_cspc = None
808 808 avgdata_dc = None
809 809
810 810 self.putData(*args)
811 811
812 812 if self.__profIndex == self.n:
813 813
814 814 avgdata_spc, avgdata_cspc, avgdata_dc, n = self.pushData()
815 815 self.n = n
816 816 self.__dataReady = True
817 817
818 818 return avgdata_spc, avgdata_cspc, avgdata_dc
819 819
820 820 def byTime(self, datatime, *args):
821 821
822 822 self.__dataReady = False
823 823 avgdata_spc = None
824 824 avgdata_cspc = None
825 825 avgdata_dc = None
826 826
827 827 self.putData(*args)
828 828
829 829 if (datatime - self.__initime) >= self.__integrationtime:
830 830 avgdata_spc, avgdata_cspc, avgdata_dc, n = self.pushData()
831 831 self.n = n
832 832 self.__dataReady = True
833 833
834 834 return avgdata_spc, avgdata_cspc, avgdata_dc
835 835
836 836 def integrate(self, datatime, *args):
837 837
838 838 if self.__profIndex == 0:
839 839 self.__initime = datatime
840 840
841 841 if self.__byTime:
842 842 avgdata_spc, avgdata_cspc, avgdata_dc = self.byTime(
843 843 datatime, *args)
844 844 else:
845 845 avgdata_spc, avgdata_cspc, avgdata_dc = self.byProfiles(*args)
846 846
847 847 if not self.__dataReady:
848 848 return None, None, None, None
849 849
850 850 return self.__initime, avgdata_spc, avgdata_cspc, avgdata_dc
851 851
852 852 def run(self, dataOut, n=None, timeInterval=None, overlapping=False):
853 853 if n == 1:
854 854 return dataOut
855
855
856 856 dataOut.flagNoData = True
857 857
858 858 if not self.isConfig:
859 859 self.setup(n, timeInterval, overlapping)
860 860 self.isConfig = True
861 861
862 862 avgdatatime, avgdata_spc, avgdata_cspc, avgdata_dc = self.integrate(dataOut.utctime,
863 863 dataOut.data_spc,
864 864 dataOut.data_cspc,
865 865 dataOut.data_dc)
866 866
867 867 if self.__dataReady:
868 868
869 869 dataOut.data_spc = avgdata_spc
870 870 dataOut.data_cspc = avgdata_cspc
871 dataOut.data_dc = avgdata_dc
871 dataOut.data_dc = avgdata_dc
872 872 dataOut.nIncohInt *= self.n
873 873 dataOut.utctime = avgdatatime
874 874 dataOut.flagNoData = False
875 875
876 876 return dataOut
877 877
878 878 class dopplerFlip(Operation):
879
879
880 880 def run(self, dataOut):
881 881 # arreglo 1: (num_chan, num_profiles, num_heights)
882 self.dataOut = dataOut
882 self.dataOut = dataOut
883 883 # JULIA-oblicua, indice 2
884 884 # arreglo 2: (num_profiles, num_heights)
885 885 jspectra = self.dataOut.data_spc[2]
886 886 jspectra_tmp = numpy.zeros(jspectra.shape)
887 887 num_profiles = jspectra.shape[0]
888 888 freq_dc = int(num_profiles / 2)
889 889 # Flip con for
890 890 for j in range(num_profiles):
891 891 jspectra_tmp[num_profiles-j-1]= jspectra[j]
892 892 # Intercambio perfil de DC con perfil inmediato anterior
893 893 jspectra_tmp[freq_dc-1]= jspectra[freq_dc-1]
894 894 jspectra_tmp[freq_dc]= jspectra[freq_dc]
895 895 # canal modificado es re-escrito en el arreglo de canales
896 896 self.dataOut.data_spc[2] = jspectra_tmp
897 897
898 return self.dataOut No newline at end of file
898 return self.dataOut
@@ -1,216 +1,216
1 1 # Ing. AVP
2 2 # 06/10/2021
3 3 # ARCHIVO DE LECTURA
4 4 import os, sys
5 5 import datetime
6 6 import time
7 7 from schainpy.controller import Project
8 8 #### NOTA###########################################
9 9 # INPUT :
10 10 # VELOCIDAD PARAMETRO : V = 2Β°/seg
11 11 # MODO PULSE PAIR O MOMENTOS: 0 : Pulse Pair ,1 : Momentos
12 12 ######################################################
13 13 ##### PROCESAMIENTO ##################################
14 14 ##### OJO TENER EN CUENTA EL n= para el Pulse Pair ##
15 15 ##### O EL n= nFFTPoints ###
16 16 ######################################################
17 17 ######## BUSCAMOS EL numero de IPP equivalente 1Β°#####
18 18 ######## Sea V la velocidad del Pedestal en Β°/seg#####
19 19 ######## 1Β° sera Recorrido en un tiempo de 1/V ######
20 20 ######## IPP del Radar 400 useg --> 60 Km ############
21 21 ######## n = 1/(V(Β°/seg)*IPP(Km)) , NUMERO DE IPP ##
22 22 ######## n = 1/(V*IPP) #############################
23 23 ######## VELOCIDAD DEL PEDESTAL ######################
24 24 print("SETUP- RADAR METEOROLOGICO")
25 25 V = 10
26 26 mode = 1
27 27 #path = '/DATA_RM/23/6v'
28 28 ####path = '/DATA_RM/TEST_INTEGRACION_2M'
29 29 #path = '/DATA_RM/TEST_19OCTUBRE/10MHZ'
30 30 path = '/DATA_RM/WR_20_OCT'
31 31 #### path_ped='/DATA_RM/TEST_PEDESTAL/P20211012-082745'
32 #### path_ped='/DATA_RM/TEST_PEDESTAL/P20211019-192244'
32 ####path_ped='/DATA_RM/TEST_PEDESTAL/P20211019-192244'
33 33 figpath_pp = "/home/soporte/Pictures/TEST_PP"
34 34 figpath_spec = "/home/soporte/Pictures/TEST_MOM"
35 plot = 1
36 integration = 0
35 plot = 0
36 integration = 1
37 37 save = 0
38 38 if save == 1:
39 39 if mode==0:
40 40 path_save = '/DATA_RM/TEST_HDF5_PP_23/6v'
41 41 path_save = '/DATA_RM/TEST_HDF5_PP'
42 42 path_save = '/DATA_RM/TEST_HDF5_PP_100'
43 43 else:
44 44 path_save = '/DATA_RM/TEST_HDF5_SPEC_23_V2/6v'
45 45
46 46 print("* PATH data ADQ :", path)
47 47 print("* Velocidad Pedestal :",V,"Β°/seg")
48 48 ############################ NRO Perfiles PROCESAMIENTO ###################
49 49 V=V
50 50 IPP=400*1e-6
51 51 n= int(1/(V*IPP))
52 52 print("* n - NRO Perfiles Proc:", n )
53 53 ################################## MODE ###################################
54 54 print("* Modo de Operacion :",mode)
55 55 if mode ==0:
56 56 print("* Met. Seleccionado : Pulse Pair")
57 57 else:
58 58 print("* Met. Momentos : Momentos")
59 59
60 60 ################################## MODE ###################################
61 61 print("* Grabado de datos :",save)
62 62 if save ==1:
63 63 if mode==0:
64 64 ope= "Pulse Pair"
65 65 else:
66 66 ope= "Momentos"
67 67 print("* Path-Save Data -", ope , path_save)
68 68
69 69 print("* Integracion de datos :",integration)
70 70
71 71 time.sleep(15)
72 72 #remotefolder = "/home/wmaster/graficos"
73 73 #######################################################################
74 74 ################# RANGO DE PLOTEO######################################
75 75 dBmin = '1'
76 76 dBmax = '65'
77 77 xmin = '13.2'
78 78 xmax = '13.5'
79 79 ymin = '0'
80 80 ymax = '60'
81 81 #######################################################################
82 82 ########################FECHA##########################################
83 83 str = datetime.date.today()
84 84 today = str.strftime("%Y/%m/%d")
85 85 str2 = str - datetime.timedelta(days=1)
86 86 yesterday = str2.strftime("%Y/%m/%d")
87 87 #######################################################################
88 88 ########################SIGNAL CHAIN ##################################
89 89 #######################################################################
90 90 desc = "USRP_test"
91 91 filename = "USRP_processing.xml"
92 92 controllerObj = Project()
93 93 controllerObj.setup(id = '191', name='Test_USRP', description=desc)
94 94 #######################################################################
95 95 ######################## UNIDAD DE LECTURA#############################
96 96 #######################################################################
97 97 readUnitConfObj = controllerObj.addReadUnit(datatype='DigitalRFReader',
98 98 path=path,
99 99 startDate="2021/01/01",#today,
100 100 endDate="2021/12/30",#today,
101 101 startTime='00:00:00',
102 102 endTime='23:59:59',
103 103 delay=0,
104 104 #set=0,
105 105 online=0,
106 106 walk=1,
107 107 ippKm = 60)
108 108
109 109 opObj11 = readUnitConfObj.addOperation(name='printInfo')
110 110
111 111 procUnitConfObjA = controllerObj.addProcUnit(datatype='VoltageProc', inputId=readUnitConfObj.getId())
112 112
113 113 if mode ==0:
114 114 ####################### METODO PULSE PAIR ######################################################################
115 115 opObj11 = procUnitConfObjA.addOperation(name='PulsePair', optype='other')
116 116 opObj11.addParameter(name='n', value=int(n), format='int')#10 VOY A USAR 250 DADO QUE LA VELOCIDAD ES 10 GRADOS
117 117 #opObj11.addParameter(name='removeDC', value=1, format='int')
118 118 ####################### METODO Parametros ######################################################################
119 119 procUnitConfObjB= controllerObj.addProcUnit(datatype='ParametersProc',inputId=procUnitConfObjA.getId())
120 120 if plot==1:
121 121 opObj11 = procUnitConfObjB.addOperation(name='GenericRTIPlot',optype='external')
122 122 opObj11.addParameter(name='attr_data', value='dataPP_POWER')
123 123 opObj11.addParameter(name='colormap', value='jet')
124 124 opObj11.addParameter(name='xmin', value=xmin)
125 125 opObj11.addParameter(name='xmax', value=xmax)
126 126 opObj11.addParameter(name='zmin', value=dBmin)
127 127 opObj11.addParameter(name='zmax', value=dBmax)
128 128 opObj11.addParameter(name='save', value=figpath_pp)
129 129 opObj11.addParameter(name='showprofile', value=0)
130 130 opObj11.addParameter(name='save_period', value=10)
131 131
132 132 ####################### METODO ESCRITURA #######################################################################
133 133 if save==1:
134 134 opObj10 = procUnitConfObjB.addOperation(name='HDFWriter')
135 135 opObj10.addParameter(name='path',value=path_save)
136 136 #opObj10.addParameter(name='mode',value=0)
137 137 opObj10.addParameter(name='blocksPerFile',value='100',format='int')
138 138 opObj10.addParameter(name='metadataList',value='utctimeInit,timeZone,paramInterval,profileIndex,channelList,heightList,flagDataAsBlock',format='list')
139 139 opObj10.addParameter(name='dataList',value='dataPP_POWER,dataPP_DOP,utctime',format='list')#,format='list'
140 140 if integration==1:
141 141 V=10
142 142 blocksPerfile=360
143 143 print("* Velocidad del Pedestal:",V)
144 144 tmp_blocksPerfile = 100
145 145 f_a_p= int(tmp_blocksPerfile/V)
146 146
147 147 opObj11 = procUnitConfObjB.addOperation(name='PedestalInformation')
148 148 opObj11.addParameter(name='path_ped', value=path_ped)
149 149 #opObj11.addParameter(name='path_adq', value=path_adq)
150 150 opObj11.addParameter(name='t_Interval_p', value='0.01', format='float')
151 151 opObj11.addParameter(name='blocksPerfile', value=blocksPerfile, format='int')
152 152 opObj11.addParameter(name='n_Muestras_p', value='100', format='float')
153 153 opObj11.addParameter(name='f_a_p', value=f_a_p, format='int')
154 154 opObj11.addParameter(name='online', value='0', format='int')
155 155
156 156 opObj11 = procUnitConfObjB.addOperation(name='Block360')
157 157 opObj11.addParameter(name='n', value='10', format='int')
158 158 opObj11.addParameter(name='mode', value=mode, format='int')
159 159
160 160 # este bloque funciona bien con divisores de 360 no olvidar 0 10 20 30 40 60 90 120 180
161 161
162 162 opObj11= procUnitConfObjB.addOperation(name='WeatherPlot',optype='other')
163 163
164 164
165 165 else:
166 166 ####################### METODO SPECTROS ######################################################################
167 167 procUnitConfObjB = controllerObj.addProcUnit(datatype='SpectraProc', inputId=procUnitConfObjA.getId())
168 168 procUnitConfObjB.addParameter(name='nFFTPoints', value=n, format='int')
169 169 procUnitConfObjB.addParameter(name='nProfiles' , value=n, format='int')
170 170
171 171 procUnitConfObjC = controllerObj.addProcUnit(datatype='ParametersProc',inputId=procUnitConfObjB.getId())
172 172 procUnitConfObjC.addOperation(name='SpectralMoments')
173 173 if plot==1:
174 174 dBmin = '1'
175 175 dBmax = '65'
176 176 opObj11 = procUnitConfObjC.addOperation(name='PowerPlot',optype='external')
177 177 opObj11.addParameter(name='xmin', value=xmin)
178 178 opObj11.addParameter(name='xmax', value=xmax)
179 179 opObj11.addParameter(name='zmin', value=dBmin)
180 180 opObj11.addParameter(name='zmax', value=dBmax)
181 181 opObj11.addParameter(name='save', value=figpath_spec)
182 182 opObj11.addParameter(name='showprofile', value=0)
183 183 opObj11.addParameter(name='save_period', value=10)
184 184
185 185 if save==1:
186 186 opObj10 = procUnitConfObjC.addOperation(name='HDFWriter')
187 187 opObj10.addParameter(name='path',value=path_save)
188 188 #opObj10.addParameter(name='mode',value=0)
189 189 opObj10.addParameter(name='blocksPerFile',value='360',format='int')
190 190 #opObj10.addParameter(name='metadataList',value='utctimeInit,heightList,nIncohInt,nCohInt,nProfiles,channelList',format='list')#profileIndex
191 191 opObj10.addParameter(name='metadataList',value='utctimeInit,heightList,nIncohInt,nCohInt,nProfiles,channelList',format='list')#profileIndex
192 192 opObj10.addParameter(name='dataList',value='data_pow,data_dop,utctime',format='list')#,format='list'
193 193
194 194 if integration==1:
195 195 V=10
196 196 blocksPerfile=360
197 197 print("* Velocidad del Pedestal:",V)
198 198 tmp_blocksPerfile = 100
199 199 f_a_p= int(tmp_blocksPerfile/V)
200 200
201 201 opObj11 = procUnitConfObjC.addOperation(name='PedestalInformation')
202 202 opObj11.addParameter(name='path_ped', value=path_ped)
203 203 #opObj11.addParameter(name='path_adq', value=path_adq)
204 204 opObj11.addParameter(name='t_Interval_p', value='0.01', format='float')
205 205 opObj11.addParameter(name='blocksPerfile', value=blocksPerfile, format='int')
206 206 opObj11.addParameter(name='n_Muestras_p', value='100', format='float')
207 207 opObj11.addParameter(name='f_a_p', value=f_a_p, format='int')
208 208 opObj11.addParameter(name='online', value='0', format='int')
209 209
210 210 opObj11 = procUnitConfObjC.addOperation(name='Block360')
211 211 opObj11.addParameter(name='n', value='10', format='int')
212 212 opObj11.addParameter(name='mode', value=mode, format='int')
213 213
214 214 # este bloque funciona bien con divisores de 360 no olvidar 0 10 20 30 40 60 90 120 180
215 215 opObj11= procUnitConfObjC.addOperation(name='WeatherPlot',optype='other')
216 216 controllerObj.start()
@@ -1,217 +1,217
1 1 # Ing. AVP
2 2 # 06/10/2021
3 3 # ARCHIVO DE LECTURA
4 4 import os, sys
5 5 import datetime
6 6 import time
7 7 from schainpy.controller import Project
8 8 #### NOTA###########################################
9 9 # INPUT :
10 10 # VELOCIDAD PARAMETRO : V = 2Β°/seg
11 11 # MODO PULSE PAIR O MOMENTOS: 0 : Pulse Pair ,1 : Momentos
12 12 ######################################################
13 13 ##### PROCESAMIENTO ##################################
14 14 ##### OJO TENER EN CUENTA EL n= para el Pulse Pair ##
15 15 ##### O EL n= nFFTPoints ###
16 16 ######################################################
17 17 ######## BUSCAMOS EL numero de IPP equivalente 1Β°#####
18 18 ######## Sea V la velocidad del Pedestal en Β°/seg#####
19 19 ######## 1Β° sera Recorrido en un tiempo de 1/V ######
20 20 ######## IPP del Radar 400 useg --> 60 Km ############
21 21 ######## n = 1/(V(Β°/seg)*IPP(Km)) , NUMERO DE IPP ##
22 22 ######## n = 1/(V*IPP) #############################
23 23 ######## VELOCIDAD DEL PEDESTAL ######################
24 24 print("SETUP- RADAR METEOROLOGICO")
25 25 V = 10
26 26 mode = 1
27 27 #path = '/DATA_RM/23/6v'
28 28 #path = '/DATA_RM/TEST_INTEGRACION_2M'
29 29 path = '/DATA_RM/WR_20_OCT'
30 30
31 31 #path_ped='/DATA_RM/TEST_PEDESTAL/P20211012-082745'
32 32 path_ped='/DATA_RM/TEST_PEDESTAL/P20211020-131248'
33 33
34 34 figpath_pp = "/home/soporte/Pictures/TEST_PP"
35 35 figpath_mom = "/home/soporte/Pictures/TEST_MOM"
36 36 plot = 0
37 37 integration = 1
38 38 save = 0
39 39 if save == 1:
40 40 if mode==0:
41 41 path_save = '/DATA_RM/TEST_HDF5_PP_23/6v'
42 42 path_save = '/DATA_RM/TEST_HDF5_PP'
43 43 path_save = '/DATA_RM/TEST_HDF5_PP_100'
44 44 else:
45 45 path_save = '/DATA_RM/TEST_HDF5_SPEC_23_V2/6v'
46 46
47 47 print("* PATH data ADQ :", path)
48 48 print("* Velocidad Pedestal :",V,"Β°/seg")
49 49 ############################ NRO Perfiles PROCESAMIENTO ###################
50 50 V=V
51 51 IPP=400*1e-6
52 52 n= int(1/(V*IPP))
53 53 print("* n - NRO Perfiles Proc:", n )
54 54 ################################## MODE ###################################
55 55 print("* Modo de Operacion :",mode)
56 56 if mode ==0:
57 57 print("* Met. Seleccionado : Pulse Pair")
58 58 else:
59 59 print("* Met. Momentos : Momentos")
60 60
61 61 ################################## MODE ###################################
62 62 print("* Grabado de datos :",save)
63 63 if save ==1:
64 64 if mode==0:
65 65 ope= "Pulse Pair"
66 66 else:
67 67 ope= "Momentos"
68 68 print("* Path-Save Data -", ope , path_save)
69 69
70 70 print("* Integracion de datos :",integration)
71 71
72 time.sleep(15)
72 time.sleep(5)
73 73 #remotefolder = "/home/wmaster/graficos"
74 74 #######################################################################
75 75 ################# RANGO DE PLOTEO######################################
76 76 dBmin = '1'
77 77 dBmax = '85'
78 78 xmin = '15'
79 79 xmax = '15.25'
80 80 ymin = '0'
81 81 ymax = '600'
82 82 #######################################################################
83 83 ########################FECHA##########################################
84 84 str = datetime.date.today()
85 85 today = str.strftime("%Y/%m/%d")
86 86 str2 = str - datetime.timedelta(days=1)
87 87 yesterday = str2.strftime("%Y/%m/%d")
88 88 #######################################################################
89 89 ########################SIGNAL CHAIN ##################################
90 90 #######################################################################
91 91 desc = "USRP_test"
92 92 filename = "USRP_processing.xml"
93 93 controllerObj = Project()
94 94 controllerObj.setup(id = '191', name='Test_USRP', description=desc)
95 95 #######################################################################
96 96 ######################## UNIDAD DE LECTURA#############################
97 97 #######################################################################
98 98 readUnitConfObj = controllerObj.addReadUnit(datatype='DigitalRFReader',
99 99 path=path,
100 100 startDate="2021/01/01",#today,
101 101 endDate="2021/12/30",#today,
102 102 startTime='00:00:00',
103 103 endTime='23:59:59',
104 104 delay=0,
105 105 #set=0,
106 106 online=0,
107 107 walk=1,
108 108 ippKm = 60)
109 109
110 110 opObj11 = readUnitConfObj.addOperation(name='printInfo')
111 111
112 112 procUnitConfObjA = controllerObj.addProcUnit(datatype='VoltageProc', inputId=readUnitConfObj.getId())
113 113
114 114 if mode ==0:
115 115 ####################### METODO PULSE PAIR ######################################################################
116 116 opObj11 = procUnitConfObjA.addOperation(name='PulsePair', optype='other')
117 117 opObj11.addParameter(name='n', value=int(n), format='int')#10 VOY A USAR 250 DADO QUE LA VELOCIDAD ES 10 GRADOS
118 118 #opObj11.addParameter(name='removeDC', value=1, format='int')
119 119 ####################### METODO Parametros ######################################################################
120 120 procUnitConfObjB= controllerObj.addProcUnit(datatype='ParametersProc',inputId=procUnitConfObjA.getId())
121 121 if plot==1:
122 122 opObj11 = procUnitConfObjB.addOperation(name='GenericRTIPlot',optype='external')
123 123 opObj11.addParameter(name='attr_data', value='dataPP_POW')
124 124 opObj11.addParameter(name='colormap', value='jet')
125 125 opObj11.addParameter(name='xmin', value=xmin)
126 126 opObj11.addParameter(name='xmax', value=xmax)
127 127 opObj11.addParameter(name='zmin', value=dBmin)
128 128 opObj11.addParameter(name='zmax', value=dBmax)
129 129 opObj11.addParameter(name='save', value=figpath_pp)
130 130 opObj11.addParameter(name='showprofile', value=0)
131 131 opObj11.addParameter(name='save_period', value=50)
132 132
133 133 ####################### METODO ESCRITURA #######################################################################
134 134 if save==1:
135 135 opObj10 = procUnitConfObjB.addOperation(name='HDFWriter')
136 136 opObj10.addParameter(name='path',value=path_save)
137 137 #opObj10.addParameter(name='mode',value=0)
138 138 opObj10.addParameter(name='blocksPerFile',value='100',format='int')
139 139 opObj10.addParameter(name='metadataList',value='utctimeInit,timeZone,paramInterval,profileIndex,channelList,heightList,flagDataAsBlock',format='list')
140 140 opObj10.addParameter(name='dataList',value='dataPP_POW,dataPP_DOP,utctime',format='list')#,format='list'
141 141 if integration==1:
142 142 V=10
143 143 blocksPerfile=360
144 144 print("* Velocidad del Pedestal:",V)
145 145 tmp_blocksPerfile = 100
146 146 f_a_p= int(tmp_blocksPerfile/V)
147 147
148 148 opObj11 = procUnitConfObjB.addOperation(name='PedestalInformation')
149 149 opObj11.addParameter(name='path_ped', value=path_ped)
150 150 #opObj11.addParameter(name='path_adq', value=path_adq)
151 151 opObj11.addParameter(name='t_Interval_p', value='0.01', format='float')
152 152 opObj11.addParameter(name='blocksPerfile', value=blocksPerfile, format='int')
153 153 opObj11.addParameter(name='n_Muestras_p', value='100', format='float')
154 154 opObj11.addParameter(name='f_a_p', value=f_a_p, format='int')
155 155 opObj11.addParameter(name='online', value='0', format='int')
156 156
157 157 opObj11 = procUnitConfObjB.addOperation(name='Block360')
158 158 opObj11.addParameter(name='n', value='10', format='int')
159 159 opObj11.addParameter(name='mode', value=mode, format='int')
160 160
161 161 # este bloque funciona bien con divisores de 360 no olvidar 0 10 20 30 40 60 90 120 180
162 162
163 163 opObj11= procUnitConfObjB.addOperation(name='WeatherPlot',optype='other')
164 164
165 165
166 166 else:
167 167 ####################### METODO SPECTROS ######################################################################
168 168 procUnitConfObjB = controllerObj.addProcUnit(datatype='SpectraProc', inputId=procUnitConfObjA.getId())
169 169 procUnitConfObjB.addParameter(name='nFFTPoints', value=n, format='int')
170 170 procUnitConfObjB.addParameter(name='nProfiles' , value=n, format='int')
171 171
172 172 procUnitConfObjC = controllerObj.addProcUnit(datatype='ParametersProc',inputId=procUnitConfObjB.getId())
173 173 procUnitConfObjC.addOperation(name='SpectralMoments')
174 174 if plot==1:
175 175 dBmin = '1'
176 176 dBmax = '65'
177 177 opObj11 = procUnitConfObjC.addOperation(name='PowerPlot',optype='external')
178 178 opObj11.addParameter(name='xmin', value=xmin)
179 179 opObj11.addParameter(name='xmax', value=xmax)
180 180 opObj11.addParameter(name='zmin', value=dBmin)
181 181 opObj11.addParameter(name='zmax', value=dBmax)
182 182 opObj11.addParameter(name='save', value=figpath_mom)
183 183 opObj11.addParameter(name='showprofile', value=0)
184 184 opObj11.addParameter(name='save_period', value=100)
185 185
186 186 if save==1:
187 187 opObj10 = procUnitConfObjC.addOperation(name='HDFWriter')
188 188 opObj10.addParameter(name='path',value=path_save)
189 189 #opObj10.addParameter(name='mode',value=0)
190 190 opObj10.addParameter(name='blocksPerFile',value='360',format='int')
191 191 #opObj10.addParameter(name='metadataList',value='utctimeInit,heightList,nIncohInt,nCohInt,nProfiles,channelList',format='list')#profileIndex
192 192 opObj10.addParameter(name='metadataList',value='utctimeInit,heightList,nIncohInt,nCohInt,nProfiles,channelList',format='list')#profileIndex
193 193 opObj10.addParameter(name='dataList',value='data_pow,data_dop,utctime',format='list')#,format='list'
194 194
195 195 if integration==1:
196 196 V=10
197 197 blocksPerfile=360
198 198 print("* Velocidad del Pedestal:",V)
199 199 tmp_blocksPerfile = 100
200 200 f_a_p= int(tmp_blocksPerfile/V)
201 201
202 202 opObj11 = procUnitConfObjC.addOperation(name='PedestalInformation')
203 203 opObj11.addParameter(name='path_ped', value=path_ped)
204 204 #opObj11.addParameter(name='path_adq', value=path_adq)
205 205 opObj11.addParameter(name='t_Interval_p', value='0.01', format='float')
206 206 opObj11.addParameter(name='blocksPerfile', value=blocksPerfile, format='int')
207 207 opObj11.addParameter(name='n_Muestras_p', value='100', format='float')
208 208 opObj11.addParameter(name='f_a_p', value=f_a_p, format='int')
209 209 opObj11.addParameter(name='online', value='0', format='int')
210 210
211 211 opObj11 = procUnitConfObjC.addOperation(name='Block360')
212 opObj11.addParameter(name='n', value='30', format='int')
212 opObj11.addParameter(name='n', value='10', format='int')
213 213 opObj11.addParameter(name='mode', value=mode, format='int')
214 214
215 215 # este bloque funciona bien con divisores de 360 no olvidar 0 10 20 30 40 60 90 120 180
216 216 opObj11= procUnitConfObjC.addOperation(name='WeatherPlot',optype='other')
217 217 controllerObj.start()
General Comments 0
You need to be logged in to leave comments. Login now