##// END OF EJS Templates
Add ScopePlot to the new way of plotting data (jroplot_data)
George Yong -
r1202:179ac651dbc0
parent child
Show More
@@ -1,1356 +1,1363
1 1 '''
2 2
3 3 $Author: murco $
4 4 $Id: JROData.py 173 2012-11-20 15:06:21Z murco $
5 5 '''
6 6
7 7 import copy
8 8 import numpy
9 9 import datetime
10 10 import json
11 11
12 12 from schainpy.utils import log
13 13 from .jroheaderIO import SystemHeader, RadarControllerHeader
14 14
15 15
16 16 def getNumpyDtype(dataTypeCode):
17 17
18 18 if dataTypeCode == 0:
19 19 numpyDtype = numpy.dtype([('real', '<i1'), ('imag', '<i1')])
20 20 elif dataTypeCode == 1:
21 21 numpyDtype = numpy.dtype([('real', '<i2'), ('imag', '<i2')])
22 22 elif dataTypeCode == 2:
23 23 numpyDtype = numpy.dtype([('real', '<i4'), ('imag', '<i4')])
24 24 elif dataTypeCode == 3:
25 25 numpyDtype = numpy.dtype([('real', '<i8'), ('imag', '<i8')])
26 26 elif dataTypeCode == 4:
27 27 numpyDtype = numpy.dtype([('real', '<f4'), ('imag', '<f4')])
28 28 elif dataTypeCode == 5:
29 29 numpyDtype = numpy.dtype([('real', '<f8'), ('imag', '<f8')])
30 30 else:
31 31 raise ValueError('dataTypeCode was not defined')
32 32
33 33 return numpyDtype
34 34
35 35
36 36 def getDataTypeCode(numpyDtype):
37 37
38 38 if numpyDtype == numpy.dtype([('real', '<i1'), ('imag', '<i1')]):
39 39 datatype = 0
40 40 elif numpyDtype == numpy.dtype([('real', '<i2'), ('imag', '<i2')]):
41 41 datatype = 1
42 42 elif numpyDtype == numpy.dtype([('real', '<i4'), ('imag', '<i4')]):
43 43 datatype = 2
44 44 elif numpyDtype == numpy.dtype([('real', '<i8'), ('imag', '<i8')]):
45 45 datatype = 3
46 46 elif numpyDtype == numpy.dtype([('real', '<f4'), ('imag', '<f4')]):
47 47 datatype = 4
48 48 elif numpyDtype == numpy.dtype([('real', '<f8'), ('imag', '<f8')]):
49 49 datatype = 5
50 50 else:
51 51 datatype = None
52 52
53 53 return datatype
54 54
55 55
56 56 def hildebrand_sekhon(data, navg):
57 57 """
58 58 This method is for the objective determination of the noise level in Doppler spectra. This
59 59 implementation technique is based on the fact that the standard deviation of the spectral
60 60 densities is equal to the mean spectral density for white Gaussian noise
61 61
62 62 Inputs:
63 63 Data : heights
64 64 navg : numbers of averages
65 65
66 66 Return:
67 67 mean : noise's level
68 68 """
69 69
70 70 sortdata = numpy.sort(data, axis=None)
71 71 lenOfData = len(sortdata)
72 72 nums_min = lenOfData*0.2
73 73
74 74 if nums_min <= 5:
75 75
76 76 nums_min = 5
77 77
78 78 sump = 0.
79 79 sumq = 0.
80 80
81 81 j = 0
82 82 cont = 1
83 83
84 84 while((cont == 1)and(j < lenOfData)):
85 85
86 86 sump += sortdata[j]
87 87 sumq += sortdata[j]**2
88 88
89 89 if j > nums_min:
90 90 rtest = float(j)/(j-1) + 1.0/navg
91 91 if ((sumq*j) > (rtest*sump**2)):
92 92 j = j - 1
93 93 sump = sump - sortdata[j]
94 94 sumq = sumq - sortdata[j]**2
95 95 cont = 0
96 96
97 97 j += 1
98 98
99 99 lnoise = sump / j
100 100
101 101 return lnoise
102 102
103 103
104 104 class Beam:
105 105
106 106 def __init__(self):
107 107 self.codeList = []
108 108 self.azimuthList = []
109 109 self.zenithList = []
110 110
111 111
112 112 class GenericData(object):
113 113
114 114 flagNoData = True
115 115
116 116 def copy(self, inputObj=None):
117 117
118 118 if inputObj == None:
119 119 return copy.deepcopy(self)
120 120
121 121 for key in list(inputObj.__dict__.keys()):
122 122
123 123 attribute = inputObj.__dict__[key]
124 124
125 125 # If this attribute is a tuple or list
126 126 if type(inputObj.__dict__[key]) in (tuple, list):
127 127 self.__dict__[key] = attribute[:]
128 128 continue
129 129
130 130 # If this attribute is another object or instance
131 131 if hasattr(attribute, '__dict__'):
132 132 self.__dict__[key] = attribute.copy()
133 133 continue
134 134
135 135 self.__dict__[key] = inputObj.__dict__[key]
136 136
137 137 def deepcopy(self):
138 138
139 139 return copy.deepcopy(self)
140 140
141 141 def isEmpty(self):
142 142
143 143 return self.flagNoData
144 144
145 145
146 146 class JROData(GenericData):
147 147
148 148 # m_BasicHeader = BasicHeader()
149 149 # m_ProcessingHeader = ProcessingHeader()
150 150
151 151 systemHeaderObj = SystemHeader()
152 152 radarControllerHeaderObj = RadarControllerHeader()
153 153 # data = None
154 154 type = None
155 155 datatype = None # dtype but in string
156 156 # dtype = None
157 157 # nChannels = None
158 158 # nHeights = None
159 159 nProfiles = None
160 160 heightList = None
161 161 channelList = None
162 162 flagDiscontinuousBlock = False
163 163 useLocalTime = False
164 164 utctime = None
165 165 timeZone = None
166 166 dstFlag = None
167 167 errorCount = None
168 168 blocksize = None
169 169 # nCode = None
170 170 # nBaud = None
171 171 # code = None
172 172 flagDecodeData = False # asumo q la data no esta decodificada
173 173 flagDeflipData = False # asumo q la data no esta sin flip
174 174 flagShiftFFT = False
175 175 # ippSeconds = None
176 176 # timeInterval = None
177 177 nCohInt = None
178 178 # noise = None
179 179 windowOfFilter = 1
180 180 # Speed of ligth
181 181 C = 3e8
182 182 frequency = 49.92e6
183 183 realtime = False
184 184 beacon_heiIndexList = None
185 185 last_block = None
186 186 blocknow = None
187 187 azimuth = None
188 188 zenith = None
189 189 beam = Beam()
190 190 profileIndex = None
191 191 error = None
192 192 data = None
193 193 nmodes = None
194 194
195 195 def __str__(self):
196 196
197 197 return '{} - {}'.format(self.type, self.getDatatime())
198 198
199 199 def getNoise(self):
200 200
201 201 raise NotImplementedError
202 202
203 203 def getNChannels(self):
204 204
205 205 return len(self.channelList)
206 206
207 207 def getChannelIndexList(self):
208 208
209 209 return list(range(self.nChannels))
210 210
211 211 def getNHeights(self):
212 212
213 213 return len(self.heightList)
214 214
215 215 def getHeiRange(self, extrapoints=0):
216 216
217 217 heis = self.heightList
218 218 # deltah = self.heightList[1] - self.heightList[0]
219 219 #
220 220 # heis.append(self.heightList[-1])
221 221
222 222 return heis
223 223
224 224 def getDeltaH(self):
225 225
226 226 delta = self.heightList[1] - self.heightList[0]
227 227
228 228 return delta
229 229
230 230 def getltctime(self):
231 231
232 232 if self.useLocalTime:
233 233 return self.utctime - self.timeZone * 60
234 234
235 235 return self.utctime
236 236
237 237 def getDatatime(self):
238 238
239 239 datatimeValue = datetime.datetime.utcfromtimestamp(self.ltctime)
240 240 return datatimeValue
241 241
242 242 def getTimeRange(self):
243 243
244 244 datatime = []
245 245
246 246 datatime.append(self.ltctime)
247 247 datatime.append(self.ltctime + self.timeInterval + 1)
248 248
249 249 datatime = numpy.array(datatime)
250 250
251 251 return datatime
252 252
253 253 def getFmaxTimeResponse(self):
254 254
255 255 period = (10**-6) * self.getDeltaH() / (0.15)
256 256
257 257 PRF = 1. / (period * self.nCohInt)
258 258
259 259 fmax = PRF
260 260
261 261 return fmax
262 262
263 263 def getFmax(self):
264 264 PRF = 1. / (self.ippSeconds * self.nCohInt)
265 265
266 266 fmax = PRF
267 267 return fmax
268 268
269 269 def getVmax(self):
270 270
271 271 _lambda = self.C / self.frequency
272 272
273 273 vmax = self.getFmax() * _lambda / 2
274 274
275 275 return vmax
276 276
277 277 def get_ippSeconds(self):
278 278 '''
279 279 '''
280 280 return self.radarControllerHeaderObj.ippSeconds
281 281
282 282 def set_ippSeconds(self, ippSeconds):
283 283 '''
284 284 '''
285 285
286 286 self.radarControllerHeaderObj.ippSeconds = ippSeconds
287 287
288 288 return
289 289
290 290 def get_dtype(self):
291 291 '''
292 292 '''
293 293 return getNumpyDtype(self.datatype)
294 294
295 295 def set_dtype(self, numpyDtype):
296 296 '''
297 297 '''
298 298
299 299 self.datatype = getDataTypeCode(numpyDtype)
300 300
301 301 def get_code(self):
302 302 '''
303 303 '''
304 304 return self.radarControllerHeaderObj.code
305 305
306 306 def set_code(self, code):
307 307 '''
308 308 '''
309 309 self.radarControllerHeaderObj.code = code
310 310
311 311 return
312 312
313 313 def get_ncode(self):
314 314 '''
315 315 '''
316 316 return self.radarControllerHeaderObj.nCode
317 317
318 318 def set_ncode(self, nCode):
319 319 '''
320 320 '''
321 321 self.radarControllerHeaderObj.nCode = nCode
322 322
323 323 return
324 324
325 325 def get_nbaud(self):
326 326 '''
327 327 '''
328 328 return self.radarControllerHeaderObj.nBaud
329 329
330 330 def set_nbaud(self, nBaud):
331 331 '''
332 332 '''
333 333 self.radarControllerHeaderObj.nBaud = nBaud
334 334
335 335 return
336 336
337 337 nChannels = property(getNChannels, "I'm the 'nChannel' property.")
338 338 channelIndexList = property(
339 339 getChannelIndexList, "I'm the 'channelIndexList' property.")
340 340 nHeights = property(getNHeights, "I'm the 'nHeights' property.")
341 341 #noise = property(getNoise, "I'm the 'nHeights' property.")
342 342 datatime = property(getDatatime, "I'm the 'datatime' property")
343 343 ltctime = property(getltctime, "I'm the 'ltctime' property")
344 344 ippSeconds = property(get_ippSeconds, set_ippSeconds)
345 345 dtype = property(get_dtype, set_dtype)
346 346 # timeInterval = property(getTimeInterval, "I'm the 'timeInterval' property")
347 347 code = property(get_code, set_code)
348 348 nCode = property(get_ncode, set_ncode)
349 349 nBaud = property(get_nbaud, set_nbaud)
350 350
351 351
352 352 class Voltage(JROData):
353 353
354 354 # data es un numpy array de 2 dmensiones (canales, alturas)
355 355 data = None
356 356
357 357 def __init__(self):
358 358 '''
359 359 Constructor
360 360 '''
361 361
362 362 self.useLocalTime = True
363 363 self.radarControllerHeaderObj = RadarControllerHeader()
364 364 self.systemHeaderObj = SystemHeader()
365 365 self.type = "Voltage"
366 366 self.data = None
367 367 # self.dtype = None
368 368 # self.nChannels = 0
369 369 # self.nHeights = 0
370 370 self.nProfiles = None
371 371 self.heightList = None
372 372 self.channelList = None
373 373 # self.channelIndexList = None
374 374 self.flagNoData = True
375 375 self.flagDiscontinuousBlock = False
376 376 self.utctime = None
377 377 self.timeZone = None
378 378 self.dstFlag = None
379 379 self.errorCount = None
380 380 self.nCohInt = None
381 381 self.blocksize = None
382 382 self.flagDecodeData = False # asumo q la data no esta decodificada
383 383 self.flagDeflipData = False # asumo q la data no esta sin flip
384 384 self.flagShiftFFT = False
385 385 self.flagDataAsBlock = False # Asumo que la data es leida perfil a perfil
386 386 self.profileIndex = 0
387 387
388 388 def getNoisebyHildebrand(self, channel=None):
389 389 """
390 390 Determino el nivel de ruido usando el metodo Hildebrand-Sekhon
391 391
392 392 Return:
393 393 noiselevel
394 394 """
395 395
396 396 if channel != None:
397 397 data = self.data[channel]
398 398 nChannels = 1
399 399 else:
400 400 data = self.data
401 401 nChannels = self.nChannels
402 402
403 403 noise = numpy.zeros(nChannels)
404 404 power = data * numpy.conjugate(data)
405 405
406 406 for thisChannel in range(nChannels):
407 407 if nChannels == 1:
408 408 daux = power[:].real
409 409 else:
410 410 daux = power[thisChannel, :].real
411 411 noise[thisChannel] = hildebrand_sekhon(daux, self.nCohInt)
412 412
413 413 return noise
414 414
415 415 def getNoise(self, type=1, channel=None):
416 416
417 417 if type == 1:
418 418 noise = self.getNoisebyHildebrand(channel)
419 419
420 420 return noise
421 421
422 422 def getPower(self, channel=None):
423 423
424 424 if channel != None:
425 425 data = self.data[channel]
426 426 else:
427 427 data = self.data
428 428
429 429 power = data * numpy.conjugate(data)
430 430 powerdB = 10 * numpy.log10(power.real)
431 431 powerdB = numpy.squeeze(powerdB)
432 432
433 433 return powerdB
434 434
435 435 def getTimeInterval(self):
436 436
437 437 timeInterval = self.ippSeconds * self.nCohInt
438 438
439 439 return timeInterval
440 440
441 441 noise = property(getNoise, "I'm the 'nHeights' property.")
442 442 timeInterval = property(getTimeInterval, "I'm the 'timeInterval' property")
443 443
444 444
445 445 class Spectra(JROData):
446 446
447 447 # data spc es un numpy array de 2 dmensiones (canales, perfiles, alturas)
448 448 data_spc = None
449 449 # data cspc es un numpy array de 2 dmensiones (canales, pares, alturas)
450 450 data_cspc = None
451 451 # data dc es un numpy array de 2 dmensiones (canales, alturas)
452 452 data_dc = None
453 453 # data power
454 454 data_pwr = None
455 455 nFFTPoints = None
456 456 # nPairs = None
457 457 pairsList = None
458 458 nIncohInt = None
459 459 wavelength = None # Necesario para cacular el rango de velocidad desde la frecuencia
460 460 nCohInt = None # se requiere para determinar el valor de timeInterval
461 461 ippFactor = None
462 462 profileIndex = 0
463 463 plotting = "spectra"
464 464
465 465 def __init__(self):
466 466 '''
467 467 Constructor
468 468 '''
469 469
470 470 self.useLocalTime = True
471 471 self.radarControllerHeaderObj = RadarControllerHeader()
472 472 self.systemHeaderObj = SystemHeader()
473 473 self.type = "Spectra"
474 474 # self.data = None
475 475 # self.dtype = None
476 476 # self.nChannels = 0
477 477 # self.nHeights = 0
478 478 self.nProfiles = None
479 479 self.heightList = None
480 480 self.channelList = None
481 481 # self.channelIndexList = None
482 482 self.pairsList = None
483 483 self.flagNoData = True
484 484 self.flagDiscontinuousBlock = False
485 485 self.utctime = None
486 486 self.nCohInt = None
487 487 self.nIncohInt = None
488 488 self.blocksize = None
489 489 self.nFFTPoints = None
490 490 self.wavelength = None
491 491 self.flagDecodeData = False # asumo q la data no esta decodificada
492 492 self.flagDeflipData = False # asumo q la data no esta sin flip
493 493 self.flagShiftFFT = False
494 494 self.ippFactor = 1
495 495 #self.noise = None
496 496 self.beacon_heiIndexList = []
497 497 self.noise_estimation = None
498 498
499 499 def getNoisebyHildebrand(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None):
500 500 """
501 501 Determino el nivel de ruido usando el metodo Hildebrand-Sekhon
502 502
503 503 Return:
504 504 noiselevel
505 505 """
506 506
507 507 noise = numpy.zeros(self.nChannels)
508 508
509 509 for channel in range(self.nChannels):
510 510 daux = self.data_spc[channel,
511 511 xmin_index:xmax_index, ymin_index:ymax_index]
512 512 noise[channel] = hildebrand_sekhon(daux, self.nIncohInt)
513 513
514 514 return noise
515 515
516 516 def getNoise(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None):
517 517
518 518 if self.noise_estimation is not None:
519 519 # this was estimated by getNoise Operation defined in jroproc_spectra.py
520 520 return self.noise_estimation
521 521 else:
522 522 noise = self.getNoisebyHildebrand(
523 523 xmin_index, xmax_index, ymin_index, ymax_index)
524 524 return noise
525 525
526 526 def getFreqRangeTimeResponse(self, extrapoints=0):
527 527
528 528 deltafreq = self.getFmaxTimeResponse() / (self.nFFTPoints * self.ippFactor)
529 529 freqrange = deltafreq * \
530 530 (numpy.arange(self.nFFTPoints + extrapoints) -
531 531 self.nFFTPoints / 2.) - deltafreq / 2
532 532
533 533 return freqrange
534 534
535 535 def getAcfRange(self, extrapoints=0):
536 536
537 537 deltafreq = 10. / (self.getFmax() / (self.nFFTPoints * self.ippFactor))
538 538 freqrange = deltafreq * \
539 539 (numpy.arange(self.nFFTPoints + extrapoints) -
540 540 self.nFFTPoints / 2.) - deltafreq / 2
541 541
542 542 return freqrange
543 543
544 544 def getFreqRange(self, extrapoints=0):
545 545
546 546 deltafreq = self.getFmax() / (self.nFFTPoints * self.ippFactor)
547 547 freqrange = deltafreq * \
548 548 (numpy.arange(self.nFFTPoints + extrapoints) -
549 549 self.nFFTPoints / 2.) - deltafreq / 2
550 550
551 551 return freqrange
552 552
553 553 def getVelRange(self, extrapoints=0):
554 554
555 555 deltav = self.getVmax() / (self.nFFTPoints * self.ippFactor)
556 556 velrange = deltav * (numpy.arange(self.nFFTPoints +
557 557 extrapoints) - self.nFFTPoints / 2.)
558 558
559 559 if self.nmodes:
560 560 return velrange/self.nmodes
561 561 else:
562 562 return velrange
563 563
564 564 def getNPairs(self):
565 565
566 566 return len(self.pairsList)
567 567
568 568 def getPairsIndexList(self):
569 569
570 570 return list(range(self.nPairs))
571 571
572 572 def getNormFactor(self):
573 573
574 574 pwcode = 1
575 575
576 576 if self.flagDecodeData:
577 577 pwcode = numpy.sum(self.code[0]**2)
578 578 #normFactor = min(self.nFFTPoints,self.nProfiles)*self.nIncohInt*self.nCohInt*pwcode*self.windowOfFilter
579 579 normFactor = self.nProfiles * self.nIncohInt * \
580 580 self.nCohInt * pwcode * self.windowOfFilter
581 581
582 582 return normFactor
583 583
584 584 def getFlagCspc(self):
585 585
586 586 if self.data_cspc is None:
587 587 return True
588 588
589 589 return False
590 590
591 591 def getFlagDc(self):
592 592
593 593 if self.data_dc is None:
594 594 return True
595 595
596 596 return False
597 597
598 598 def getTimeInterval(self):
599 599
600 600 timeInterval = self.ippSeconds * self.nCohInt * \
601 601 self.nIncohInt * self.nProfiles * self.ippFactor
602 602
603 603 return timeInterval
604 604
605 605 def getPower(self):
606 606
607 607 factor = self.normFactor
608 608 z = self.data_spc / factor
609 609 z = numpy.where(numpy.isfinite(z), z, numpy.NAN)
610 610 avg = numpy.average(z, axis=1)
611 611
612 612 return 10 * numpy.log10(avg)
613 613
614 614 def getCoherence(self, pairsList=None, phase=False):
615 615
616 616 z = []
617 617 if pairsList is None:
618 618 pairsIndexList = self.pairsIndexList
619 619 else:
620 620 pairsIndexList = []
621 621 for pair in pairsList:
622 622 if pair not in self.pairsList:
623 623 raise ValueError("Pair %s is not in dataOut.pairsList" % (
624 624 pair))
625 625 pairsIndexList.append(self.pairsList.index(pair))
626 626 for i in range(len(pairsIndexList)):
627 627 pair = self.pairsList[pairsIndexList[i]]
628 628 ccf = numpy.average(
629 629 self.data_cspc[pairsIndexList[i], :, :], axis=0)
630 630 powa = numpy.average(self.data_spc[pair[0], :, :], axis=0)
631 631 powb = numpy.average(self.data_spc[pair[1], :, :], axis=0)
632 632 avgcoherenceComplex = ccf / numpy.sqrt(powa * powb)
633 633 if phase:
634 634 data = numpy.arctan2(avgcoherenceComplex.imag,
635 635 avgcoherenceComplex.real) * 180 / numpy.pi
636 636 else:
637 637 data = numpy.abs(avgcoherenceComplex)
638 638
639 639 z.append(data)
640 640
641 641 return numpy.array(z)
642 642
643 643 def setValue(self, value):
644 644
645 645 print("This property should not be initialized")
646 646
647 647 return
648 648
649 649 nPairs = property(getNPairs, setValue, "I'm the 'nPairs' property.")
650 650 pairsIndexList = property(
651 651 getPairsIndexList, setValue, "I'm the 'pairsIndexList' property.")
652 652 normFactor = property(getNormFactor, setValue,
653 653 "I'm the 'getNormFactor' property.")
654 654 flag_cspc = property(getFlagCspc, setValue)
655 655 flag_dc = property(getFlagDc, setValue)
656 656 noise = property(getNoise, setValue, "I'm the 'nHeights' property.")
657 657 timeInterval = property(getTimeInterval, setValue,
658 658 "I'm the 'timeInterval' property")
659 659
660 660
661 661 class SpectraHeis(Spectra):
662 662
663 663 data_spc = None
664 664 data_cspc = None
665 665 data_dc = None
666 666 nFFTPoints = None
667 667 # nPairs = None
668 668 pairsList = None
669 669 nCohInt = None
670 670 nIncohInt = None
671 671
672 672 def __init__(self):
673 673
674 674 self.radarControllerHeaderObj = RadarControllerHeader()
675 675
676 676 self.systemHeaderObj = SystemHeader()
677 677
678 678 self.type = "SpectraHeis"
679 679
680 680 # self.dtype = None
681 681
682 682 # self.nChannels = 0
683 683
684 684 # self.nHeights = 0
685 685
686 686 self.nProfiles = None
687 687
688 688 self.heightList = None
689 689
690 690 self.channelList = None
691 691
692 692 # self.channelIndexList = None
693 693
694 694 self.flagNoData = True
695 695
696 696 self.flagDiscontinuousBlock = False
697 697
698 698 # self.nPairs = 0
699 699
700 700 self.utctime = None
701 701
702 702 self.blocksize = None
703 703
704 704 self.profileIndex = 0
705 705
706 706 self.nCohInt = 1
707 707
708 708 self.nIncohInt = 1
709 709
710 710 def getNormFactor(self):
711 711 pwcode = 1
712 712 if self.flagDecodeData:
713 713 pwcode = numpy.sum(self.code[0]**2)
714 714
715 715 normFactor = self.nIncohInt * self.nCohInt * pwcode
716 716
717 717 return normFactor
718 718
719 719 def getTimeInterval(self):
720 720
721 721 timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt
722 722
723 723 return timeInterval
724 724
725 725 normFactor = property(getNormFactor, "I'm the 'getNormFactor' property.")
726 726 timeInterval = property(getTimeInterval, "I'm the 'timeInterval' property")
727 727
728 728
729 729 class Fits(JROData):
730 730
731 731 heightList = None
732 732 channelList = None
733 733 flagNoData = True
734 734 flagDiscontinuousBlock = False
735 735 useLocalTime = False
736 736 utctime = None
737 737 timeZone = None
738 738 # ippSeconds = None
739 739 # timeInterval = None
740 740 nCohInt = None
741 741 nIncohInt = None
742 742 noise = None
743 743 windowOfFilter = 1
744 744 # Speed of ligth
745 745 C = 3e8
746 746 frequency = 49.92e6
747 747 realtime = False
748 748
749 749 def __init__(self):
750 750
751 751 self.type = "Fits"
752 752
753 753 self.nProfiles = None
754 754
755 755 self.heightList = None
756 756
757 757 self.channelList = None
758 758
759 759 # self.channelIndexList = None
760 760
761 761 self.flagNoData = True
762 762
763 763 self.utctime = None
764 764
765 765 self.nCohInt = 1
766 766
767 767 self.nIncohInt = 1
768 768
769 769 self.useLocalTime = True
770 770
771 771 self.profileIndex = 0
772 772
773 773 # self.utctime = None
774 774 # self.timeZone = None
775 775 # self.ltctime = None
776 776 # self.timeInterval = None
777 777 # self.header = None
778 778 # self.data_header = None
779 779 # self.data = None
780 780 # self.datatime = None
781 781 # self.flagNoData = False
782 782 # self.expName = ''
783 783 # self.nChannels = None
784 784 # self.nSamples = None
785 785 # self.dataBlocksPerFile = None
786 786 # self.comments = ''
787 787 #
788 788
789 789 def getltctime(self):
790 790
791 791 if self.useLocalTime:
792 792 return self.utctime - self.timeZone * 60
793 793
794 794 return self.utctime
795 795
796 796 def getDatatime(self):
797 797
798 798 datatime = datetime.datetime.utcfromtimestamp(self.ltctime)
799 799 return datatime
800 800
801 801 def getTimeRange(self):
802 802
803 803 datatime = []
804 804
805 805 datatime.append(self.ltctime)
806 806 datatime.append(self.ltctime + self.timeInterval)
807 807
808 808 datatime = numpy.array(datatime)
809 809
810 810 return datatime
811 811
812 812 def getHeiRange(self):
813 813
814 814 heis = self.heightList
815 815
816 816 return heis
817 817
818 818 def getNHeights(self):
819 819
820 820 return len(self.heightList)
821 821
822 822 def getNChannels(self):
823 823
824 824 return len(self.channelList)
825 825
826 826 def getChannelIndexList(self):
827 827
828 828 return list(range(self.nChannels))
829 829
830 830 def getNoise(self, type=1):
831 831
832 832 #noise = numpy.zeros(self.nChannels)
833 833
834 834 if type == 1:
835 835 noise = self.getNoisebyHildebrand()
836 836
837 837 if type == 2:
838 838 noise = self.getNoisebySort()
839 839
840 840 if type == 3:
841 841 noise = self.getNoisebyWindow()
842 842
843 843 return noise
844 844
845 845 def getTimeInterval(self):
846 846
847 847 timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt
848 848
849 849 return timeInterval
850 850
851 851 def get_ippSeconds(self):
852 852 '''
853 853 '''
854 854 return self.ipp_sec
855 855
856 856
857 857 datatime = property(getDatatime, "I'm the 'datatime' property")
858 858 nHeights = property(getNHeights, "I'm the 'nHeights' property.")
859 859 nChannels = property(getNChannels, "I'm the 'nChannel' property.")
860 860 channelIndexList = property(
861 861 getChannelIndexList, "I'm the 'channelIndexList' property.")
862 862 noise = property(getNoise, "I'm the 'nHeights' property.")
863 863
864 864 ltctime = property(getltctime, "I'm the 'ltctime' property")
865 865 timeInterval = property(getTimeInterval, "I'm the 'timeInterval' property")
866 866 ippSeconds = property(get_ippSeconds, '')
867 867
868 868 class Correlation(JROData):
869 869
870 870 noise = None
871 871 SNR = None
872 872 #--------------------------------------------------
873 873 mode = None
874 874 split = False
875 875 data_cf = None
876 876 lags = None
877 877 lagRange = None
878 878 pairsList = None
879 879 normFactor = None
880 880 #--------------------------------------------------
881 881 # calculateVelocity = None
882 882 nLags = None
883 883 nPairs = None
884 884 nAvg = None
885 885
886 886 def __init__(self):
887 887 '''
888 888 Constructor
889 889 '''
890 890 self.radarControllerHeaderObj = RadarControllerHeader()
891 891
892 892 self.systemHeaderObj = SystemHeader()
893 893
894 894 self.type = "Correlation"
895 895
896 896 self.data = None
897 897
898 898 self.dtype = None
899 899
900 900 self.nProfiles = None
901 901
902 902 self.heightList = None
903 903
904 904 self.channelList = None
905 905
906 906 self.flagNoData = True
907 907
908 908 self.flagDiscontinuousBlock = False
909 909
910 910 self.utctime = None
911 911
912 912 self.timeZone = None
913 913
914 914 self.dstFlag = None
915 915
916 916 self.errorCount = None
917 917
918 918 self.blocksize = None
919 919
920 920 self.flagDecodeData = False # asumo q la data no esta decodificada
921 921
922 922 self.flagDeflipData = False # asumo q la data no esta sin flip
923 923
924 924 self.pairsList = None
925 925
926 926 self.nPoints = None
927 927
928 928 def getPairsList(self):
929 929
930 930 return self.pairsList
931 931
932 932 def getNoise(self, mode=2):
933 933
934 934 indR = numpy.where(self.lagR == 0)[0][0]
935 935 indT = numpy.where(self.lagT == 0)[0][0]
936 936
937 937 jspectra0 = self.data_corr[:, :, indR, :]
938 938 jspectra = copy.copy(jspectra0)
939 939
940 940 num_chan = jspectra.shape[0]
941 941 num_hei = jspectra.shape[2]
942 942
943 943 freq_dc = jspectra.shape[1] / 2
944 944 ind_vel = numpy.array([-2, -1, 1, 2]) + freq_dc
945 945
946 946 if ind_vel[0] < 0:
947 947 ind_vel[list(range(0, 1))] = ind_vel[list(
948 948 range(0, 1))] + self.num_prof
949 949
950 950 if mode == 1:
951 951 jspectra[:, freq_dc, :] = (
952 952 jspectra[:, ind_vel[1], :] + jspectra[:, ind_vel[2], :]) / 2 # CORRECCION
953 953
954 954 if mode == 2:
955 955
956 956 vel = numpy.array([-2, -1, 1, 2])
957 957 xx = numpy.zeros([4, 4])
958 958
959 959 for fil in range(4):
960 960 xx[fil, :] = vel[fil]**numpy.asarray(list(range(4)))
961 961
962 962 xx_inv = numpy.linalg.inv(xx)
963 963 xx_aux = xx_inv[0, :]
964 964
965 965 for ich in range(num_chan):
966 966 yy = jspectra[ich, ind_vel, :]
967 967 jspectra[ich, freq_dc, :] = numpy.dot(xx_aux, yy)
968 968
969 969 junkid = jspectra[ich, freq_dc, :] <= 0
970 970 cjunkid = sum(junkid)
971 971
972 972 if cjunkid.any():
973 973 jspectra[ich, freq_dc, junkid.nonzero()] = (
974 974 jspectra[ich, ind_vel[1], junkid] + jspectra[ich, ind_vel[2], junkid]) / 2
975 975
976 976 noise = jspectra0[:, freq_dc, :] - jspectra[:, freq_dc, :]
977 977
978 978 return noise
979 979
980 980 def getTimeInterval(self):
981 981
982 982 timeInterval = self.ippSeconds * self.nCohInt * self.nProfiles
983 983
984 984 return timeInterval
985 985
986 986 def splitFunctions(self):
987 987
988 988 pairsList = self.pairsList
989 989 ccf_pairs = []
990 990 acf_pairs = []
991 991 ccf_ind = []
992 992 acf_ind = []
993 993 for l in range(len(pairsList)):
994 994 chan0 = pairsList[l][0]
995 995 chan1 = pairsList[l][1]
996 996
997 997 # Obteniendo pares de Autocorrelacion
998 998 if chan0 == chan1:
999 999 acf_pairs.append(chan0)
1000 1000 acf_ind.append(l)
1001 1001 else:
1002 1002 ccf_pairs.append(pairsList[l])
1003 1003 ccf_ind.append(l)
1004 1004
1005 1005 data_acf = self.data_cf[acf_ind]
1006 1006 data_ccf = self.data_cf[ccf_ind]
1007 1007
1008 1008 return acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf
1009 1009
1010 1010 def getNormFactor(self):
1011 1011 acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf = self.splitFunctions()
1012 1012 acf_pairs = numpy.array(acf_pairs)
1013 1013 normFactor = numpy.zeros((self.nPairs, self.nHeights))
1014 1014
1015 1015 for p in range(self.nPairs):
1016 1016 pair = self.pairsList[p]
1017 1017
1018 1018 ch0 = pair[0]
1019 1019 ch1 = pair[1]
1020 1020
1021 1021 ch0_max = numpy.max(data_acf[acf_pairs == ch0, :, :], axis=1)
1022 1022 ch1_max = numpy.max(data_acf[acf_pairs == ch1, :, :], axis=1)
1023 1023 normFactor[p, :] = numpy.sqrt(ch0_max * ch1_max)
1024 1024
1025 1025 return normFactor
1026 1026
1027 1027 timeInterval = property(getTimeInterval, "I'm the 'timeInterval' property")
1028 1028 normFactor = property(getNormFactor, "I'm the 'normFactor property'")
1029 1029
1030 1030
1031 1031 class Parameters(Spectra):
1032 1032
1033 1033 experimentInfo = None # Information about the experiment
1034 1034 # Information from previous data
1035 1035 inputUnit = None # Type of data to be processed
1036 1036 operation = None # Type of operation to parametrize
1037 1037 # normFactor = None #Normalization Factor
1038 1038 groupList = None # List of Pairs, Groups, etc
1039 1039 # Parameters
1040 1040 data_param = None # Parameters obtained
1041 1041 data_pre = None # Data Pre Parametrization
1042 1042 data_SNR = None # Signal to Noise Ratio
1043 1043 # heightRange = None #Heights
1044 1044 abscissaList = None # Abscissa, can be velocities, lags or time
1045 1045 # noise = None #Noise Potency
1046 1046 utctimeInit = None # Initial UTC time
1047 1047 paramInterval = None # Time interval to calculate Parameters in seconds
1048 1048 useLocalTime = True
1049 1049 # Fitting
1050 1050 data_error = None # Error of the estimation
1051 1051 constants = None
1052 1052 library = None
1053 1053 # Output signal
1054 1054 outputInterval = None # Time interval to calculate output signal in seconds
1055 1055 data_output = None # Out signal
1056 1056 nAvg = None
1057 1057 noise_estimation = None
1058 1058 GauSPC = None # Fit gaussian SPC
1059 1059
1060 1060 def __init__(self):
1061 1061 '''
1062 1062 Constructor
1063 1063 '''
1064 1064 self.radarControllerHeaderObj = RadarControllerHeader()
1065 1065
1066 1066 self.systemHeaderObj = SystemHeader()
1067 1067
1068 1068 self.type = "Parameters"
1069 1069
1070 1070 def getTimeRange1(self, interval):
1071 1071
1072 1072 datatime = []
1073 1073
1074 1074 if self.useLocalTime:
1075 1075 time1 = self.utctimeInit - self.timeZone * 60
1076 1076 else:
1077 1077 time1 = self.utctimeInit
1078 1078
1079 1079 datatime.append(time1)
1080 1080 datatime.append(time1 + interval)
1081 1081 datatime = numpy.array(datatime)
1082 1082
1083 1083 return datatime
1084 1084
1085 1085 def getTimeInterval(self):
1086 1086
1087 1087 if hasattr(self, 'timeInterval1'):
1088 1088 return self.timeInterval1
1089 1089 else:
1090 1090 return self.paramInterval
1091 1091
1092 1092 def setValue(self, value):
1093 1093
1094 1094 print("This property should not be initialized")
1095 1095
1096 1096 return
1097 1097
1098 1098 def getNoise(self):
1099 1099
1100 1100 return self.spc_noise
1101 1101
1102 1102 timeInterval = property(getTimeInterval)
1103 1103 noise = property(getNoise, setValue, "I'm the 'Noise' property.")
1104 1104
1105 1105
1106 1106 class PlotterData(object):
1107 1107 '''
1108 1108 Object to hold data to be plotted
1109 1109 '''
1110 1110
1111 1111 MAXNUMX = 100
1112 1112 MAXNUMY = 100
1113 1113
1114 1114 def __init__(self, code, throttle_value, exp_code, buffering=True):
1115 1115
1116 1116 self.throttle = throttle_value
1117 1117 self.exp_code = exp_code
1118 1118 self.buffering = buffering
1119 1119 self.ready = False
1120 1120 self.localtime = False
1121 1121 self.data = {}
1122 1122 self.meta = {}
1123 1123 self.__times = []
1124 1124 self.__heights = []
1125 1125
1126 1126 if 'snr' in code:
1127 1127 self.plottypes = ['snr']
1128 1128 elif code == 'spc':
1129 1129 self.plottypes = ['spc', 'noise', 'rti']
1130 1130 elif code == 'rti':
1131 1131 self.plottypes = ['noise', 'rti']
1132 1132 else:
1133 1133 self.plottypes = [code]
1134 1134
1135 1135 for plot in self.plottypes:
1136 1136 self.data[plot] = {}
1137 1137
1138 1138 def __str__(self):
1139 1139 dum = ['{}{}'.format(key, self.shape(key)) for key in self.data]
1140 1140 return 'Data[{}][{}]'.format(';'.join(dum), len(self.__times))
1141 1141
1142 1142 def __len__(self):
1143 1143 return len(self.__times)
1144 1144
1145 1145 def __getitem__(self, key):
1146
1146
1147 1147 if key not in self.data:
1148 1148 raise KeyError(log.error('Missing key: {}'.format(key)))
1149 1149 if 'spc' in key or not self.buffering:
1150 1150 ret = self.data[key]
1151 elif 'scope' in key:
1152 ret = numpy.array(self.data[key][float(self.tm)])
1151 1153 else:
1152 1154 ret = numpy.array([self.data[key][x] for x in self.times])
1153 1155 if ret.ndim > 1:
1154 1156 ret = numpy.swapaxes(ret, 0, 1)
1155 1157 return ret
1156 1158
1157 1159 def __contains__(self, key):
1158 1160 return key in self.data
1159 1161
1160 1162 def setup(self):
1161 1163 '''
1162 1164 Configure object
1163 1165 '''
1164 1166
1165 1167 self.type = ''
1166 1168 self.ready = False
1167 1169 self.data = {}
1168 1170 self.__times = []
1169 1171 self.__heights = []
1170 1172 self.__all_heights = set()
1171 1173 for plot in self.plottypes:
1172 1174 if 'snr' in plot:
1173 1175 plot = 'snr'
1174 1176 self.data[plot] = {}
1175 1177
1176 1178 if 'spc' in self.data or 'rti' in self.data or 'cspc' in self.data:
1177 1179 self.data['noise'] = {}
1178 1180 if 'noise' not in self.plottypes:
1179 1181 self.plottypes.append('noise')
1180 1182
1181 1183 def shape(self, key):
1182 1184 '''
1183 1185 Get the shape of the one-element data for the given key
1184 1186 '''
1185 1187
1186 1188 if len(self.data[key]):
1187 1189 if 'spc' in key or not self.buffering:
1188 1190 return self.data[key].shape
1189 1191 return self.data[key][self.__times[0]].shape
1190 1192 return (0,)
1191 1193
1192 1194 def update(self, dataOut, tm):
1193 1195 '''
1194 1196 Update data object with new dataOut
1195 1197 '''
1196 1198
1197 1199 if tm in self.__times:
1198 1200 return
1199
1201 self.profileIndex = dataOut.profileIndex
1202 self.tm = tm
1200 1203 self.type = dataOut.type
1201 1204 self.parameters = getattr(dataOut, 'parameters', [])
1202 1205 if hasattr(dataOut, 'pairsList'):
1203 1206 self.pairs = dataOut.pairsList
1204 1207 if hasattr(dataOut, 'meta'):
1205 1208 self.meta = dataOut.meta
1206 1209 self.channels = dataOut.channelList
1207 1210 self.interval = dataOut.getTimeInterval()
1208 1211 self.localtime = dataOut.useLocalTime
1209 1212 if 'spc' in self.plottypes or 'cspc' in self.plottypes:
1210 1213 self.xrange = (dataOut.getFreqRange(1)/1000.,
1211 1214 dataOut.getAcfRange(1), dataOut.getVelRange(1))
1212 1215 self.factor = dataOut.normFactor
1213 1216 self.__heights.append(dataOut.heightList)
1214 1217 self.__all_heights.update(dataOut.heightList)
1215 1218 self.__times.append(tm)
1216 1219
1217 1220 for plot in self.plottypes:
1218 1221 if plot == 'spc':
1219 1222 z = dataOut.data_spc/dataOut.normFactor
1220 1223 buffer = 10*numpy.log10(z)
1221 1224 if plot == 'cspc':
1222 1225 z = dataOut.data_spc/dataOut.normFactor
1223 1226 buffer = (dataOut.data_spc, dataOut.data_cspc)
1224 1227 if plot == 'noise':
1225 1228 buffer = 10*numpy.log10(dataOut.getNoise()/dataOut.normFactor)
1226 1229 if plot == 'rti':
1227 1230 buffer = dataOut.getPower()
1228 1231 if plot == 'snr_db':
1229 1232 buffer = dataOut.data_SNR
1230 1233 if plot == 'snr':
1231 1234 buffer = 10*numpy.log10(dataOut.data_SNR)
1232 1235 if plot == 'dop':
1233 1236 buffer = 10*numpy.log10(dataOut.data_DOP)
1234 1237 if plot == 'mean':
1235 1238 buffer = dataOut.data_MEAN
1236 1239 if plot == 'std':
1237 1240 buffer = dataOut.data_STD
1238 1241 if plot == 'coh':
1239 1242 buffer = dataOut.getCoherence()
1240 1243 if plot == 'phase':
1241 1244 buffer = dataOut.getCoherence(phase=True)
1242 1245 if plot == 'output':
1243 1246 buffer = dataOut.data_output
1244 1247 if plot == 'param':
1245 1248 buffer = dataOut.data_param
1246
1249 if plot == 'scope':
1250 buffer = dataOut.data
1251 self.flagDataAsBlock = dataOut.flagDataAsBlock
1252 self.nProfiles = dataOut.nProfiles
1253
1247 1254 if plot == 'spc':
1248 1255 self.data[plot] = buffer
1249 1256 elif plot == 'cspc':
1250 1257 self.data['spc'] = buffer[0]
1251 1258 self.data['cspc'] = buffer[1]
1252 1259 else:
1253 1260 if self.buffering:
1254 1261 self.data[plot][tm] = buffer
1255 1262 else:
1256 1263 self.data[plot] = buffer
1257 1264
1258 1265 def normalize_heights(self):
1259 1266 '''
1260 1267 Ensure same-dimension of the data for different heighList
1261 1268 '''
1262 1269
1263 1270 H = numpy.array(list(self.__all_heights))
1264 1271 H.sort()
1265 1272 for key in self.data:
1266 1273 shape = self.shape(key)[:-1] + H.shape
1267 1274 for tm, obj in list(self.data[key].items()):
1268 1275 h = self.__heights[self.__times.index(tm)]
1269 1276 if H.size == h.size:
1270 1277 continue
1271 1278 index = numpy.where(numpy.in1d(H, h))[0]
1272 1279 dummy = numpy.zeros(shape) + numpy.nan
1273 1280 if len(shape) == 2:
1274 1281 dummy[:, index] = obj
1275 1282 else:
1276 1283 dummy[index] = obj
1277 1284 self.data[key][tm] = dummy
1278 1285
1279 1286 self.__heights = [H for tm in self.__times]
1280 1287
1281 1288 def jsonify(self, decimate=False):
1282 1289 '''
1283 1290 Convert data to json
1284 1291 '''
1285 1292
1286 1293 data = {}
1287 1294 tm = self.times[-1]
1288 1295 dy = int(self.heights.size/self.MAXNUMY) + 1
1289 1296 for key in self.data:
1290 1297 if key in ('spc', 'cspc') or not self.buffering:
1291 1298 dx = int(self.data[key].shape[1]/self.MAXNUMX) + 1
1292 1299 data[key] = self.roundFloats(
1293 1300 self.data[key][::, ::dx, ::dy].tolist())
1294 1301 else:
1295 1302 data[key] = self.roundFloats(self.data[key][tm].tolist())
1296 1303
1297 1304 ret = {'data': data}
1298 1305 ret['exp_code'] = self.exp_code
1299 1306 ret['time'] = float(tm)
1300 1307 ret['interval'] = float(self.interval)
1301 1308 ret['localtime'] = self.localtime
1302 1309 ret['yrange'] = self.roundFloats(self.heights[::dy].tolist())
1303 1310 if 'spc' in self.data or 'cspc' in self.data:
1304 1311 ret['xrange'] = self.roundFloats(self.xrange[2][::dx].tolist())
1305 1312 else:
1306 1313 ret['xrange'] = []
1307 1314 if hasattr(self, 'pairs'):
1308 1315 ret['pairs'] = [(int(p[0]), int(p[1])) for p in self.pairs]
1309 1316 else:
1310 1317 ret['pairs'] = []
1311 1318
1312 1319 for key, value in list(self.meta.items()):
1313 1320 ret[key] = value
1314 1321
1315 1322 return json.dumps(ret)
1316 1323
1317 1324 @property
1318 1325 def times(self):
1319 1326 '''
1320 1327 Return the list of times of the current data
1321 1328 '''
1322 1329
1323 1330 ret = numpy.array(self.__times)
1324 1331 ret.sort()
1325 1332 return ret
1326 1333
1327 1334 @property
1328 1335 def min_time(self):
1329 1336 '''
1330 1337 Return the minimun time value
1331 1338 '''
1332 1339
1333 1340 return self.times[0]
1334 1341
1335 1342 @property
1336 1343 def max_time(self):
1337 1344 '''
1338 1345 Return the maximun time value
1339 1346 '''
1340 1347
1341 1348 return self.times[-1]
1342 1349
1343 1350 @property
1344 1351 def heights(self):
1345 1352 '''
1346 1353 Return the list of heights of the current data
1347 1354 '''
1348 1355
1349 1356 return numpy.array(self.__heights[-1])
1350 1357
1351 1358 @staticmethod
1352 1359 def roundFloats(obj):
1353 1360 if isinstance(obj, list):
1354 1361 return list(map(PlotterData.roundFloats, obj))
1355 1362 elif isinstance(obj, float):
1356 1363 return round(obj, 2)
@@ -1,799 +1,801
1 1
2 2 import os
3 3 import sys
4 4 import zmq
5 5 import time
6 6 import datetime
7 7 from functools import wraps
8 8 import numpy
9 9 import matplotlib
10 10
11 11 if 'BACKEND' in os.environ:
12 12 matplotlib.use(os.environ['BACKEND'])
13 13 elif 'linux' in sys.platform:
14 14 matplotlib.use("TkAgg")
15 15 elif 'darwin' in sys.platform:
16 16 matplotlib.use('TkAgg')
17 17 else:
18 18 from schainpy.utils import log
19 19 log.warning('Using default Backend="Agg"', 'INFO')
20 20 matplotlib.use('Agg')
21 21
22 22 import matplotlib.pyplot as plt
23 23 from matplotlib.patches import Polygon
24 24 from mpl_toolkits.axes_grid1 import make_axes_locatable
25 25 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
26 26
27 27 from schainpy.model.data.jrodata import PlotterData
28 28 from schainpy.model.proc.jroproc_base import ProcessingUnit, Operation, MPDecorator
29 29 from schainpy.utils import log
30 30
31 31 jet_values = matplotlib.pyplot.get_cmap('jet', 100)(numpy.arange(100))[10:90]
32 32 blu_values = matplotlib.pyplot.get_cmap(
33 33 'seismic_r', 20)(numpy.arange(20))[10:15]
34 34 ncmap = matplotlib.colors.LinearSegmentedColormap.from_list(
35 35 'jro', numpy.vstack((blu_values, jet_values)))
36 36 matplotlib.pyplot.register_cmap(cmap=ncmap)
37 37
38 38 CMAPS = [plt.get_cmap(s) for s in ('jro', 'jet', 'viridis',
39 39 'plasma', 'inferno', 'Greys', 'seismic', 'bwr', 'coolwarm')]
40 40
41 41 EARTH_RADIUS = 6.3710e3
42 42
43 43
44 44 def ll2xy(lat1, lon1, lat2, lon2):
45 45
46 46 p = 0.017453292519943295
47 47 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
48 48 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
49 49 r = 12742 * numpy.arcsin(numpy.sqrt(a))
50 50 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
51 51 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
52 52 theta = -theta + numpy.pi/2
53 53 return r*numpy.cos(theta), r*numpy.sin(theta)
54 54
55 55
56 56 def km2deg(km):
57 57 '''
58 58 Convert distance in km to degrees
59 59 '''
60 60
61 61 return numpy.rad2deg(km/EARTH_RADIUS)
62 62
63 63
64 64 def figpause(interval):
65 65 backend = plt.rcParams['backend']
66 66 if backend in matplotlib.rcsetup.interactive_bk:
67 67 figManager = matplotlib._pylab_helpers.Gcf.get_active()
68 68 if figManager is not None:
69 69 canvas = figManager.canvas
70 70 if canvas.figure.stale:
71 71 canvas.draw()
72 72 try:
73 73 canvas.start_event_loop(interval)
74 74 except:
75 75 pass
76 76 return
77 77
78 78
79 79 def popup(message):
80 80 '''
81 81 '''
82 82
83 83 fig = plt.figure(figsize=(12, 8), facecolor='r')
84 84 text = '\n'.join([s.strip() for s in message.split(':')])
85 85 fig.text(0.01, 0.5, text, ha='left', va='center',
86 86 size='20', weight='heavy', color='w')
87 87 fig.show()
88 88 figpause(1000)
89 89
90 90
91 91 class Throttle(object):
92 92 '''
93 93 Decorator that prevents a function from being called more than once every
94 94 time period.
95 95 To create a function that cannot be called more than once a minute, but
96 96 will sleep until it can be called:
97 97 @Throttle(minutes=1)
98 98 def foo():
99 99 pass
100 100
101 101 for i in range(10):
102 102 foo()
103 103 print "This function has run %s times." % i
104 104 '''
105 105
106 106 def __init__(self, seconds=0, minutes=0, hours=0):
107 107 self.throttle_period = datetime.timedelta(
108 108 seconds=seconds, minutes=minutes, hours=hours
109 109 )
110 110
111 111 self.time_of_last_call = datetime.datetime.min
112 112
113 113 def __call__(self, fn):
114 114 @wraps(fn)
115 115 def wrapper(*args, **kwargs):
116 116 coerce = kwargs.pop('coerce', None)
117 117 if coerce:
118 118 self.time_of_last_call = datetime.datetime.now()
119 119 return fn(*args, **kwargs)
120 120 else:
121 121 now = datetime.datetime.now()
122 122 time_since_last_call = now - self.time_of_last_call
123 123 time_left = self.throttle_period - time_since_last_call
124 124
125 125 if time_left > datetime.timedelta(seconds=0):
126 126 return
127 127
128 128 self.time_of_last_call = datetime.datetime.now()
129 129 return fn(*args, **kwargs)
130 130
131 131 return wrapper
132 132
133 133 def apply_throttle(value):
134 134
135 135 @Throttle(seconds=value)
136 136 def fnThrottled(fn):
137 137 fn()
138 138
139 139 return fnThrottled
140 140
141 141 @MPDecorator
142 142 class Plotter(ProcessingUnit):
143 143 '''
144 144 Proccessing unit to handle plot operations
145 145 '''
146 146
147 147 def __init__(self):
148 148
149 149 ProcessingUnit.__init__(self)
150 150
151 151 def setup(self, **kwargs):
152 152
153 153 self.connections = 0
154 154 self.web_address = kwargs.get('web_server', False)
155 155 self.realtime = kwargs.get('realtime', False)
156 156 self.localtime = kwargs.get('localtime', True)
157 157 self.buffering = kwargs.get('buffering', True)
158 158 self.throttle = kwargs.get('throttle', 2)
159 159 self.exp_code = kwargs.get('exp_code', None)
160 160 self.set_ready = apply_throttle(self.throttle)
161 161 self.dates = []
162 162 self.data = PlotterData(
163 163 self.plots, self.throttle, self.exp_code, self.buffering)
164 164 self.isConfig = True
165 165
166 166 def ready(self):
167 167 '''
168 168 Set dataOut ready
169 169 '''
170 170
171 171 self.data.ready = True
172 172 self.dataOut.data_plt = self.data
173 173
174 174 def run(self, realtime=True, localtime=True, buffering=True,
175 175 throttle=2, exp_code=None, web_server=None):
176 176
177 177 if not self.isConfig:
178 178 self.setup(realtime=realtime, localtime=localtime,
179 179 buffering=buffering, throttle=throttle, exp_code=exp_code,
180 180 web_server=web_server)
181 181
182 182 if self.web_address:
183 183 log.success(
184 184 'Sending to web: {}'.format(self.web_address),
185 185 self.name
186 186 )
187 187 self.context = zmq.Context()
188 188 self.sender_web = self.context.socket(zmq.REQ)
189 189 self.sender_web.connect(self.web_address)
190 190 self.poll = zmq.Poller()
191 191 self.poll.register(self.sender_web, zmq.POLLIN)
192 192 time.sleep(1)
193 193
194 194 # t = Thread(target=self.event_monitor, args=(monitor,))
195 195 # t.start()
196 196
197 197 self.dataOut = self.dataIn
198 198 self.data.ready = False
199 199
200 200 if self.dataOut.flagNoData:
201 201 coerce = True
202 202 else:
203 203 coerce = False
204 204
205 205 if self.dataOut.type == 'Parameters':
206 206 tm = self.dataOut.utctimeInit
207 207 else:
208 208 tm = self.dataOut.utctime
209 209 if self.dataOut.useLocalTime:
210 210 if not self.localtime:
211 211 tm += time.timezone
212 212 dt = datetime.datetime.fromtimestamp(tm).date()
213 213 else:
214 214 if self.localtime:
215 215 tm -= time.timezone
216 216 dt = datetime.datetime.utcfromtimestamp(tm).date()
217 217 if dt not in self.dates:
218 218 if self.data:
219 219 self.ready()
220 220 self.data.setup()
221 221 self.dates.append(dt)
222 222
223 223 self.data.update(self.dataOut, tm)
224 224
225 225 if False: # TODO check when publishers ends
226 226 self.connections -= 1
227 227 if self.connections == 0 and dt in self.dates:
228 228 self.data.ended = True
229 229 self.ready()
230 230 time.sleep(1)
231 231 else:
232 232 if self.realtime:
233 233 self.ready()
234 234 if self.web_address:
235 235 retries = 5
236 236 while True:
237 237 self.sender_web.send(self.data.jsonify())
238 238 socks = dict(self.poll.poll(5000))
239 239 if socks.get(self.sender_web) == zmq.POLLIN:
240 240 reply = self.sender_web.recv_string()
241 241 if reply == 'ok':
242 242 log.log("Response from server ok", self.name)
243 243 break
244 244 else:
245 245 log.warning(
246 246 "Malformed reply from server: {}".format(reply), self.name)
247 247
248 248 else:
249 249 log.warning(
250 250 "No response from server, retrying...", self.name)
251 251 self.sender_web.setsockopt(zmq.LINGER, 0)
252 252 self.sender_web.close()
253 253 self.poll.unregister(self.sender_web)
254 254 retries -= 1
255 255 if retries == 0:
256 256 log.error(
257 257 "Server seems to be offline, abandoning", self.name)
258 258 self.sender_web = self.context.socket(zmq.REQ)
259 259 self.sender_web.connect(self.web_address)
260 260 self.poll.register(self.sender_web, zmq.POLLIN)
261 261 time.sleep(1)
262 262 break
263 263 self.sender_web = self.context.socket(zmq.REQ)
264 264 self.sender_web.connect(self.web_address)
265 265 self.poll.register(self.sender_web, zmq.POLLIN)
266 266 time.sleep(1)
267 267 else:
268 268 self.set_ready(self.ready, coerce=coerce)
269 269
270 270 return
271 271
272 272 def close(self):
273 273 pass
274 274
275 275
276 276 @MPDecorator
277 277 class Plot(Operation):
278 278 '''
279 279 Base class for Schain plotting operations
280 280 '''
281 281
282 282 CODE = 'Figure'
283 283 colormap = 'jro'
284 284 bgcolor = 'white'
285 285 __missing = 1E30
286 286
287 287 __attrs__ = ['show', 'save', 'xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax',
288 288 'zlimits', 'xlabel', 'ylabel', 'xaxis', 'cb_label', 'title',
289 289 'colorbar', 'bgcolor', 'width', 'height', 'localtime', 'oneFigure',
290 290 'showprofile', 'decimation', 'pause']
291 291
292 292 def __init__(self):
293 293
294 294 Operation.__init__(self)
295 295 self.isConfig = False
296 296 self.isPlotConfig = False
297 297
298 298 def __fmtTime(self, x, pos):
299 299 '''
300 300 '''
301 301
302 302 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
303 303
304 304 def __setup(self, **kwargs):
305 305 '''
306 306 Initialize variables
307 307 '''
308 308
309 309 self.figures = []
310 310 self.axes = []
311 311 self.cb_axes = []
312 312 self.localtime = kwargs.pop('localtime', True)
313 313 self.show = kwargs.get('show', True)
314 314 self.save = kwargs.get('save', False)
315 315 self.ftp = kwargs.get('ftp', False)
316 316 self.colormap = kwargs.get('colormap', self.colormap)
317 317 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
318 318 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
319 319 self.colormaps = kwargs.get('colormaps', None)
320 320 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
321 321 self.showprofile = kwargs.get('showprofile', False)
322 322 self.title = kwargs.get('wintitle', self.CODE.upper())
323 323 self.cb_label = kwargs.get('cb_label', None)
324 324 self.cb_labels = kwargs.get('cb_labels', None)
325 325 self.labels = kwargs.get('labels', None)
326 326 self.xaxis = kwargs.get('xaxis', 'frequency')
327 327 self.zmin = kwargs.get('zmin', None)
328 328 self.zmax = kwargs.get('zmax', None)
329 329 self.zlimits = kwargs.get('zlimits', None)
330 330 self.xmin = kwargs.get('xmin', None)
331 331 self.xmax = kwargs.get('xmax', None)
332 332 self.xrange = kwargs.get('xrange', 12)
333 333 self.xscale = kwargs.get('xscale', None)
334 334 self.ymin = kwargs.get('ymin', None)
335 335 self.ymax = kwargs.get('ymax', None)
336 336 self.yscale = kwargs.get('yscale', None)
337 337 self.xlabel = kwargs.get('xlabel', None)
338 338 self.decimation = kwargs.get('decimation', None)
339 339 self.showSNR = kwargs.get('showSNR', False)
340 340 self.oneFigure = kwargs.get('oneFigure', True)
341 341 self.width = kwargs.get('width', None)
342 342 self.height = kwargs.get('height', None)
343 343 self.colorbar = kwargs.get('colorbar', True)
344 344 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
345 345 self.channels = kwargs.get('channels', None)
346 346 self.titles = kwargs.get('titles', [])
347 347 self.polar = False
348 self.type = kwargs.get('type', 'iq')
348 349 self.grid = kwargs.get('grid', False)
349 350 self.pause = kwargs.get('pause', False)
350 351 self.save_labels = kwargs.get('save_labels', None)
351 352 self.realtime = kwargs.get('realtime', True)
352 353 self.buffering = kwargs.get('buffering', True)
353 354 self.throttle = kwargs.get('throttle', 2)
354 355 self.exp_code = kwargs.get('exp_code', None)
355 356 self.__throttle_plot = apply_throttle(self.throttle)
356 357 self.data = PlotterData(
357 358 self.CODE, self.throttle, self.exp_code, self.buffering)
358 359
359 360 def __setup_plot(self):
360 361 '''
361 362 Common setup for all figures, here figures and axes are created
362 363 '''
363 364
364 365 self.setup()
365 366
366 367 self.time_label = 'LT' if self.localtime else 'UTC'
367 368 if self.data.localtime:
368 369 self.getDateTime = datetime.datetime.fromtimestamp
369 370 else:
370 371 self.getDateTime = datetime.datetime.utcfromtimestamp
371 372
372 373 if self.width is None:
373 374 self.width = 8
374 375
375 376 self.figures = []
376 377 self.axes = []
377 378 self.cb_axes = []
378 379 self.pf_axes = []
379 380 self.cmaps = []
380 381
381 382 size = '15%' if self.ncols == 1 else '30%'
382 383 pad = '4%' if self.ncols == 1 else '8%'
383 384
384 385 if self.oneFigure:
385 386 if self.height is None:
386 387 self.height = 1.4 * self.nrows + 1
387 388 fig = plt.figure(figsize=(self.width, self.height),
388 389 edgecolor='k',
389 390 facecolor='w')
390 391 self.figures.append(fig)
391 392 for n in range(self.nplots):
392 393 ax = fig.add_subplot(self.nrows, self.ncols,
393 394 n + 1, polar=self.polar)
394 395 ax.tick_params(labelsize=8)
395 396 ax.firsttime = True
396 397 ax.index = 0
397 398 ax.press = None
398 399 self.axes.append(ax)
399 400 if self.showprofile:
400 401 cax = self.__add_axes(ax, size=size, pad=pad)
401 402 cax.tick_params(labelsize=8)
402 403 self.pf_axes.append(cax)
403 404 else:
404 405 if self.height is None:
405 406 self.height = 3
406 407 for n in range(self.nplots):
407 408 fig = plt.figure(figsize=(self.width, self.height),
408 409 edgecolor='k',
409 410 facecolor='w')
410 411 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
411 412 ax.tick_params(labelsize=8)
412 413 ax.firsttime = True
413 414 ax.index = 0
414 415 ax.press = None
415 416 self.figures.append(fig)
416 417 self.axes.append(ax)
417 418 if self.showprofile:
418 419 cax = self.__add_axes(ax, size=size, pad=pad)
419 420 cax.tick_params(labelsize=8)
420 421 self.pf_axes.append(cax)
421 422
422 423 for n in range(self.nrows):
423 424 if self.colormaps is not None:
424 425 cmap = plt.get_cmap(self.colormaps[n])
425 426 else:
426 427 cmap = plt.get_cmap(self.colormap)
427 428 cmap.set_bad(self.bgcolor, 1.)
428 429 self.cmaps.append(cmap)
429 430
430 431 for fig in self.figures:
431 432 fig.canvas.mpl_connect('key_press_event', self.OnKeyPress)
432 433 fig.canvas.mpl_connect('scroll_event', self.OnBtnScroll)
433 434 fig.canvas.mpl_connect('button_press_event', self.onBtnPress)
434 435 fig.canvas.mpl_connect('motion_notify_event', self.onMotion)
435 436 fig.canvas.mpl_connect('button_release_event', self.onBtnRelease)
436 437 if self.show:
437 438 fig.show()
438 439
439 440 def OnKeyPress(self, event):
440 441 '''
441 442 Event for pressing keys (up, down) change colormap
442 443 '''
443 444 ax = event.inaxes
444 445 if ax in self.axes:
445 446 if event.key == 'down':
446 447 ax.index += 1
447 448 elif event.key == 'up':
448 449 ax.index -= 1
449 450 if ax.index < 0:
450 451 ax.index = len(CMAPS) - 1
451 452 elif ax.index == len(CMAPS):
452 453 ax.index = 0
453 454 cmap = CMAPS[ax.index]
454 455 ax.cbar.set_cmap(cmap)
455 456 ax.cbar.draw_all()
456 457 ax.plt.set_cmap(cmap)
457 458 ax.cbar.patch.figure.canvas.draw()
458 459 self.colormap = cmap.name
459 460
460 461 def OnBtnScroll(self, event):
461 462 '''
462 463 Event for scrolling, scale figure
463 464 '''
464 465 cb_ax = event.inaxes
465 466 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
466 467 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
467 468 pt = ax.cbar.ax.bbox.get_points()[:, 1]
468 469 nrm = ax.cbar.norm
469 470 vmin, vmax, p0, p1, pS = (
470 471 nrm.vmin, nrm.vmax, pt[0], pt[1], event.y)
471 472 scale = 2 if event.step == 1 else 0.5
472 473 point = vmin + (vmax - vmin) / (p1 - p0) * (pS - p0)
473 474 ax.cbar.norm.vmin = point - scale * (point - vmin)
474 475 ax.cbar.norm.vmax = point - scale * (point - vmax)
475 476 ax.plt.set_norm(ax.cbar.norm)
476 477 ax.cbar.draw_all()
477 478 ax.cbar.patch.figure.canvas.draw()
478 479
479 480 def onBtnPress(self, event):
480 481 '''
481 482 Event for mouse button press
482 483 '''
483 484 cb_ax = event.inaxes
484 485 if cb_ax is None:
485 486 return
486 487
487 488 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
488 489 cb_ax.press = event.x, event.y
489 490 else:
490 491 cb_ax.press = None
491 492
492 493 def onMotion(self, event):
493 494 '''
494 495 Event for move inside colorbar
495 496 '''
496 497 cb_ax = event.inaxes
497 498 if cb_ax is None:
498 499 return
499 500 if cb_ax not in [ax.cbar.ax for ax in self.axes if ax.cbar]:
500 501 return
501 502 if cb_ax.press is None:
502 503 return
503 504
504 505 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
505 506 xprev, yprev = cb_ax.press
506 507 dx = event.x - xprev
507 508 dy = event.y - yprev
508 509 cb_ax.press = event.x, event.y
509 510 scale = ax.cbar.norm.vmax - ax.cbar.norm.vmin
510 511 perc = 0.03
511 512
512 513 if event.button == 1:
513 514 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
514 515 ax.cbar.norm.vmax -= (perc * scale) * numpy.sign(dy)
515 516 elif event.button == 3:
516 517 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
517 518 ax.cbar.norm.vmax += (perc * scale) * numpy.sign(dy)
518 519
519 520 ax.cbar.draw_all()
520 521 ax.plt.set_norm(ax.cbar.norm)
521 522 ax.cbar.patch.figure.canvas.draw()
522 523
523 524 def onBtnRelease(self, event):
524 525 '''
525 526 Event for mouse button release
526 527 '''
527 528 cb_ax = event.inaxes
528 529 if cb_ax is not None:
529 530 cb_ax.press = None
530 531
531 532 def __add_axes(self, ax, size='30%', pad='8%'):
532 533 '''
533 534 Add new axes to the given figure
534 535 '''
535 536 divider = make_axes_locatable(ax)
536 537 nax = divider.new_horizontal(size=size, pad=pad)
537 538 ax.figure.add_axes(nax)
538 539 return nax
539 540
540 541 def setup(self):
541 542 '''
542 543 This method should be implemented in the child class, the following
543 544 attributes should be set:
544 545
545 546 self.nrows: number of rows
546 547 self.ncols: number of cols
547 548 self.nplots: number of plots (channels or pairs)
548 549 self.ylabel: label for Y axes
549 550 self.titles: list of axes title
550 551
551 552 '''
552 553 raise NotImplementedError
553 554
554 555 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
555 556 '''
556 557 Create a masked array for missing data
557 558 '''
558 559 if x_buffer.shape[0] < 2:
559 560 return x_buffer, y_buffer, z_buffer
560 561
561 562 deltas = x_buffer[1:] - x_buffer[0:-1]
562 563 x_median = numpy.median(deltas)
563 564
564 565 index = numpy.where(deltas > 5 * x_median)
565 566
566 567 if len(index[0]) != 0:
567 568 z_buffer[::, index[0], ::] = self.__missing
568 569 z_buffer = numpy.ma.masked_inside(z_buffer,
569 570 0.99 * self.__missing,
570 571 1.01 * self.__missing)
571 572
572 573 return x_buffer, y_buffer, z_buffer
573 574
574 575 def decimate(self):
575 576
576 577 # dx = int(len(self.x)/self.__MAXNUMX) + 1
577 578 dy = int(len(self.y) / self.decimation) + 1
578 579
579 580 # x = self.x[::dx]
580 581 x = self.x
581 582 y = self.y[::dy]
582 583 z = self.z[::, ::, ::dy]
583 584
584 585 return x, y, z
585 586
586 587 def format(self):
587 588 '''
588 589 Set min and max values, labels, ticks and titles
589 590 '''
590 591
591 592 if self.xmin is None:
592 593 xmin = self.data.min_time
593 594 else:
594 595 if self.xaxis is 'time':
595 596 dt = self.getDateTime(self.data.min_time)
596 597 xmin = (dt.replace(hour=int(self.xmin), minute=0, second=0) -
597 598 datetime.datetime(1970, 1, 1)).total_seconds()
598 599 if self.data.localtime:
599 600 xmin += time.timezone
600 601 else:
601 602 xmin = self.xmin
602 603
603 604 if self.xmax is None:
604 605 xmax = xmin + self.xrange * 60 * 60
605 606 else:
606 607 if self.xaxis is 'time':
607 608 dt = self.getDateTime(self.data.max_time)
608 609 xmax = (dt.replace(hour=int(self.xmax), minute=59, second=59) -
609 610 datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=1)).total_seconds()
610 611 if self.data.localtime:
611 612 xmax += time.timezone
612 613 else:
613 614 xmax = self.xmax
614
615
615 616 ymin = self.ymin if self.ymin else numpy.nanmin(self.y)
616 617 ymax = self.ymax if self.ymax else numpy.nanmax(self.y)
617 Y = numpy.array([1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000])
618 i = 1 if numpy.where(
619 abs(ymax-ymin) <= Y)[0][0] < 0 else numpy.where(abs(ymax-ymin) <= Y)[0][0]
620 ystep = Y[i] / 10.
621
618 Y = numpy.array([1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000])
619 #i = 1 if numpy.where(
620 # abs(ymax-ymin) <= Y)[0][0] < 0 else numpy.where(abs(ymax-ymin) <= Y)[0][0]
621 #ystep = Y[i] / 10.
622 ystep = round(ymax,-1)//5
622 623 if self.xaxis is not 'time':
623 624 X = numpy.array([0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100,
624 200, 500, 1000, 2000, 5000])/2.
625 200, 500, 1000, 2000, 5000, 10000, 20000, 50000])/2.
626
625 627 i = 1 if numpy.where(
626 628 abs(xmax-xmin) <= X)[0][0] < 0 else numpy.where(abs(xmax-xmin) <= X)[0][0]
627 629 xstep = X[i] / 5.
628 630
629 631 for n, ax in enumerate(self.axes):
630 632 if ax.firsttime:
631 633 ax.set_facecolor(self.bgcolor)
632 634 ax.yaxis.set_major_locator(MultipleLocator(ystep))
633 635 if self.xscale:
634 636 ax.xaxis.set_major_formatter(FuncFormatter(
635 637 lambda x, pos: '{0:g}'.format(x*self.xscale)))
636 638 if self.xscale:
637 639 ax.yaxis.set_major_formatter(FuncFormatter(
638 640 lambda x, pos: '{0:g}'.format(x*self.yscale)))
639 641 if self.xaxis is 'time':
640 642 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
641 643 ax.xaxis.set_major_locator(LinearLocator(9))
642 644 else:
643 645 ax.xaxis.set_major_locator(MultipleLocator(xstep))
644 646 if self.xlabel is not None:
645 647 ax.set_xlabel(self.xlabel)
646 648 ax.set_ylabel(self.ylabel)
647 649 ax.firsttime = False
648 650 if self.showprofile:
649 651 self.pf_axes[n].set_ylim(ymin, ymax)
650 652 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
651 653 self.pf_axes[n].set_xlabel('dB')
652 654 self.pf_axes[n].grid(b=True, axis='x')
653 655 [tick.set_visible(False)
654 656 for tick in self.pf_axes[n].get_yticklabels()]
655 657 if self.colorbar:
656 658 ax.cbar = plt.colorbar(
657 659 ax.plt, ax=ax, fraction=0.05, pad=0.02, aspect=10)
658 660 ax.cbar.ax.tick_params(labelsize=8)
659 661 ax.cbar.ax.press = None
660 662 if self.cb_label:
661 663 ax.cbar.set_label(self.cb_label, size=8)
662 664 elif self.cb_labels:
663 665 ax.cbar.set_label(self.cb_labels[n], size=8)
664 666 else:
665 667 ax.cbar = None
666 668 if self.grid:
667 669 ax.grid(True)
668 670
669 671 if not self.polar:
670 672 ax.set_xlim(xmin, xmax)
671 673 ax.set_ylim(ymin, ymax)
672 674 ax.set_title('{} {} {}'.format(
673 675 self.titles[n],
674 676 self.getDateTime(self.data.max_time).strftime(
675 677 '%H:%M:%S'),
676 678 self.time_label),
677 679 size=8)
678 680 else:
679 681 ax.set_title('{}'.format(self.titles[n]), size=8)
680 682 ax.set_ylim(0, 90)
681 683 ax.set_yticks(numpy.arange(0, 90, 20))
682 684 ax.yaxis.labelpad = 40
683 685
684 686 def clear_figures(self):
685 687 '''
686 688 Reset axes for redraw plots
687 689 '''
688 690
689 691 for ax in self.axes:
690 692 ax.clear()
691 693 ax.firsttime = True
692 694 if ax.cbar:
693 695 ax.cbar.remove()
694 696
695 697 def __plot(self):
696 698 '''
697 699 Main function to plot, format and save figures
698 700 '''
699 701
700 702 #try:
701 703 self.plot()
702 704 self.format()
703 705 #except Exception as e:
704 706 # log.warning('{} Plot could not be updated... check data'.format(
705 707 # self.CODE), self.name)
706 708 # log.error(str(e), '')
707 709 # return
708 710
709 711 for n, fig in enumerate(self.figures):
710 712 if self.nrows == 0 or self.nplots == 0:
711 713 log.warning('No data', self.name)
712 714 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
713 715 fig.canvas.manager.set_window_title(self.CODE)
714 716 continue
715 717
716 718 fig.tight_layout()
717 719 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
718 720 self.getDateTime(self.data.max_time).strftime('%Y/%m/%d')))
719 721 fig.canvas.draw()
720 722
721 723 if self.save:
722 724
723 725 if self.save_labels:
724 726 labels = self.save_labels
725 727 else:
726 728 labels = list(range(self.nrows))
727 729
728 730 if self.oneFigure:
729 731 label = ''
730 732 else:
731 733 label = '-{}'.format(labels[n])
732 734 figname = os.path.join(
733 735 self.save,
734 736 self.CODE,
735 737 '{}{}_{}.png'.format(
736 738 self.CODE,
737 739 label,
738 740 self.getDateTime(self.data.max_time).strftime(
739 741 '%Y%m%d_%H%M%S'),
740 742 )
741 743 )
742 744 log.log('Saving figure: {}'.format(figname), self.name)
743 745 if not os.path.isdir(os.path.dirname(figname)):
744 746 os.makedirs(os.path.dirname(figname))
745 747 fig.savefig(figname)
746 748
747 749 def plot(self):
748 750 '''
749 751 Must be defined in the child class
750 752 '''
751 753 raise NotImplementedError
752 754
753 755 def run(self, dataOut, **kwargs):
754 756
755 757 if dataOut.error:
756 758 coerce = True
757 759 else:
758 760 coerce = False
759 761
760 762 if self.isConfig is False:
761 763 self.__setup(**kwargs)
762 764 self.data.setup()
763 765 self.isConfig = True
764 766
765 767 if dataOut.type == 'Parameters':
766 768 tm = dataOut.utctimeInit
767 769 else:
768 770 tm = dataOut.utctime
769 771
770 772 if dataOut.useLocalTime:
771 773 if not self.localtime:
772 774 tm += time.timezone
773 775 else:
774 776 if self.localtime:
775 777 tm -= time.timezone
776 778
777 779 if self.data and (tm - self.data.min_time) >= self.xrange*60*60:
778 780 self.__plot()
779 781 self.data.setup()
780 782 self.clear_figures()
781 783
782 784 self.data.update(dataOut, tm)
783 785
784 786 if self.isPlotConfig is False:
785 787 self.__setup_plot()
786 788 self.isPlotConfig = True
787 789
788 790 if self.realtime:
789 791 self.__plot()
790 792 else:
791 793 self.__throttle_plot(self.__plot, coerce=coerce)
792 794
793 795 figpause(0.001)
794 796
795 797 def close(self):
796 798
797 799 if self.data and self.pause:
798 800 figpause(10)
799 801
@@ -1,616 +1,748
1 1 '''
2 2 New Plots Operations
3 3
4 4 @author: juan.espinoza@jro.igp.gob.pe
5 5 '''
6 6
7 7
8 8 import time
9 9 import datetime
10 10 import numpy
11 11
12 12 from schainpy.model.graphics.jroplot_base import Plot, plt
13 13 from schainpy.utils import log
14 14
15 15 EARTH_RADIUS = 6.3710e3
16 16
17 17
18 18 def ll2xy(lat1, lon1, lat2, lon2):
19 19
20 20 p = 0.017453292519943295
21 21 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
22 22 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
23 23 r = 12742 * numpy.arcsin(numpy.sqrt(a))
24 24 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
25 25 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
26 26 theta = -theta + numpy.pi/2
27 27 return r*numpy.cos(theta), r*numpy.sin(theta)
28 28
29 29
30 30 def km2deg(km):
31 31 '''
32 32 Convert distance in km to degrees
33 33 '''
34 34
35 35 return numpy.rad2deg(km/EARTH_RADIUS)
36 36
37 37
38 38 class SpectraPlot(Plot):
39 39 '''
40 40 Plot for Spectra data
41 41 '''
42 42
43 43 CODE = 'spc'
44 44 colormap = 'jro'
45 45
46 46 def setup(self):
47 47 self.nplots = len(self.data.channels)
48 48 self.ncols = int(numpy.sqrt(self.nplots) + 0.9)
49 49 self.nrows = int((1.0 * self.nplots / self.ncols) + 0.9)
50 50 self.width = 3.4 * self.ncols
51 51 self.height = 3 * self.nrows
52 52 self.cb_label = 'dB'
53 53 if self.showprofile:
54 54 self.width += 0.8 * self.ncols
55 55
56 56 self.ylabel = 'Range [km]'
57 57
58 58 def plot(self):
59 59 if self.xaxis == "frequency":
60 60 x = self.data.xrange[0]
61 61 self.xlabel = "Frequency (kHz)"
62 62 elif self.xaxis == "time":
63 63 x = self.data.xrange[1]
64 64 self.xlabel = "Time (ms)"
65 65 else:
66 66 x = self.data.xrange[2]
67 67 self.xlabel = "Velocity (m/s)"
68 68
69 69 if self.CODE == 'spc_mean':
70 70 x = self.data.xrange[2]
71 71 self.xlabel = "Velocity (m/s)"
72 72
73 73 self.titles = []
74 74
75 75 y = self.data.heights
76 76 self.y = y
77 77 z = self.data['spc']
78 78
79 79 for n, ax in enumerate(self.axes):
80 80 noise = self.data['noise'][n][-1]
81 81 if self.CODE == 'spc_mean':
82 82 mean = self.data['mean'][n][-1]
83 83 if ax.firsttime:
84 84 self.xmax = self.xmax if self.xmax else numpy.nanmax(x)
85 85 self.xmin = self.xmin if self.xmin else -self.xmax
86 86 self.zmin = self.zmin if self.zmin else numpy.nanmin(z)
87 87 self.zmax = self.zmax if self.zmax else numpy.nanmax(z)
88 88 ax.plt = ax.pcolormesh(x, y, z[n].T,
89 89 vmin=self.zmin,
90 90 vmax=self.zmax,
91 91 cmap=plt.get_cmap(self.colormap)
92 92 )
93 93
94 94 if self.showprofile:
95 95 ax.plt_profile = self.pf_axes[n].plot(
96 96 self.data['rti'][n][-1], y)[0]
97 97 ax.plt_noise = self.pf_axes[n].plot(numpy.repeat(noise, len(y)), y,
98 98 color="k", linestyle="dashed", lw=1)[0]
99 99 if self.CODE == 'spc_mean':
100 100 ax.plt_mean = ax.plot(mean, y, color='k')[0]
101 101 else:
102 102 ax.plt.set_array(z[n].T.ravel())
103 103 if self.showprofile:
104 104 ax.plt_profile.set_data(self.data['rti'][n][-1], y)
105 105 ax.plt_noise.set_data(numpy.repeat(noise, len(y)), y)
106 106 if self.CODE == 'spc_mean':
107 107 ax.plt_mean.set_data(mean, y)
108 108
109 109 self.titles.append('CH {}: {:3.2f}dB'.format(n, noise))
110 110
111 111
112 112 class CrossSpectraPlot(Plot):
113 113
114 114 CODE = 'cspc'
115 115 colormap = 'jet'
116 116 zmin_coh = None
117 117 zmax_coh = None
118 118 zmin_phase = None
119 119 zmax_phase = None
120 120
121 121 def setup(self):
122 122
123 123 self.ncols = 4
124 124 self.nrows = len(self.data.pairs)
125 125 self.nplots = self.nrows * 4
126 126 self.width = 3.4 * self.ncols
127 127 self.height = 3 * self.nrows
128 128 self.ylabel = 'Range [km]'
129 129 self.showprofile = False
130 130
131 131 def plot(self):
132 132
133 133 if self.xaxis == "frequency":
134 134 x = self.data.xrange[0]
135 135 self.xlabel = "Frequency (kHz)"
136 136 elif self.xaxis == "time":
137 137 x = self.data.xrange[1]
138 138 self.xlabel = "Time (ms)"
139 139 else:
140 140 x = self.data.xrange[2]
141 141 self.xlabel = "Velocity (m/s)"
142 142
143 143 self.titles = []
144 144
145 145 y = self.data.heights
146 146 self.y = y
147 147 spc = self.data['spc']
148 148 cspc = self.data['cspc']
149 149
150 150 for n in range(self.nrows):
151 151 noise = self.data['noise'][n][-1]
152 152 pair = self.data.pairs[n]
153 153 ax = self.axes[4 * n]
154 154 spc0 = 10.*numpy.log10(spc[pair[0]]/self.data.factor)
155 155 if ax.firsttime:
156 156 self.xmax = self.xmax if self.xmax else numpy.nanmax(x)
157 157 self.xmin = self.xmin if self.xmin else -self.xmax
158 158 self.zmin = self.zmin if self.zmin else numpy.nanmin(spc)
159 159 self.zmax = self.zmax if self.zmax else numpy.nanmax(spc)
160 160 ax.plt = ax.pcolormesh(x , y , spc0.T,
161 161 vmin=self.zmin,
162 162 vmax=self.zmax,
163 163 cmap=plt.get_cmap(self.colormap)
164 164 )
165 165 else:
166 166 ax.plt.set_array(spc0.T.ravel())
167 167 self.titles.append('CH {}: {:3.2f}dB'.format(pair[0], noise))
168 168
169 169 ax = self.axes[4 * n + 1]
170 170 spc1 = 10.*numpy.log10(spc[pair[1]]/self.data.factor)
171 171 if ax.firsttime:
172 172 ax.plt = ax.pcolormesh(x , y, spc1.T,
173 173 vmin=self.zmin,
174 174 vmax=self.zmax,
175 175 cmap=plt.get_cmap(self.colormap)
176 176 )
177 177 else:
178 178 ax.plt.set_array(spc1.T.ravel())
179 179 self.titles.append('CH {}: {:3.2f}dB'.format(pair[1], noise))
180 180
181 181 out = cspc[n] / numpy.sqrt(spc[pair[0]] * spc[pair[1]])
182 182 coh = numpy.abs(out)
183 183 phase = numpy.arctan2(out.imag, out.real) * 180 / numpy.pi
184 184
185 185 ax = self.axes[4 * n + 2]
186 186 if ax.firsttime:
187 187 ax.plt = ax.pcolormesh(x, y, coh.T,
188 188 vmin=0,
189 189 vmax=1,
190 190 cmap=plt.get_cmap(self.colormap_coh)
191 191 )
192 192 else:
193 193 ax.plt.set_array(coh.T.ravel())
194 194 self.titles.append(
195 195 'Coherence Ch{} * Ch{}'.format(pair[0], pair[1]))
196 196
197 197 ax = self.axes[4 * n + 3]
198 198 if ax.firsttime:
199 199 ax.plt = ax.pcolormesh(x, y, phase.T,
200 200 vmin=-180,
201 201 vmax=180,
202 202 cmap=plt.get_cmap(self.colormap_phase)
203 203 )
204 204 else:
205 205 ax.plt.set_array(phase.T.ravel())
206 206 self.titles.append('Phase CH{} * CH{}'.format(pair[0], pair[1]))
207 207
208 208
209 209 class SpectraMeanPlot(SpectraPlot):
210 210 '''
211 211 Plot for Spectra and Mean
212 212 '''
213 213 CODE = 'spc_mean'
214 214 colormap = 'jro'
215 215
216 216
217 217 class RTIPlot(Plot):
218 218 '''
219 219 Plot for RTI data
220 220 '''
221 221
222 222 CODE = 'rti'
223 223 colormap = 'jro'
224 224
225 225 def setup(self):
226 226 self.xaxis = 'time'
227 227 self.ncols = 1
228 228 self.nrows = len(self.data.channels)
229 229 self.nplots = len(self.data.channels)
230 230 self.ylabel = 'Range [km]'
231 231 self.cb_label = 'dB'
232 232 self.titles = ['{} Channel {}'.format(
233 233 self.CODE.upper(), x) for x in range(self.nrows)]
234 234
235 235 def plot(self):
236 236 self.x = self.data.times
237 237 self.y = self.data.heights
238 238 self.z = self.data[self.CODE]
239 239 self.z = numpy.ma.masked_invalid(self.z)
240 240
241 241 if self.decimation is None:
242 242 x, y, z = self.fill_gaps(self.x, self.y, self.z)
243 243 else:
244 244 x, y, z = self.fill_gaps(*self.decimate())
245 245
246 246 for n, ax in enumerate(self.axes):
247 247 self.zmin = self.zmin if self.zmin else numpy.min(self.z)
248 248 self.zmax = self.zmax if self.zmax else numpy.max(self.z)
249 249 if ax.firsttime:
250 250 ax.plt = ax.pcolormesh(x, y, z[n].T,
251 251 vmin=self.zmin,
252 252 vmax=self.zmax,
253 253 cmap=plt.get_cmap(self.colormap)
254 254 )
255 255 if self.showprofile:
256 256 ax.plot_profile = self.pf_axes[n].plot(
257 257 self.data['rti'][n][-1], self.y)[0]
258 258 ax.plot_noise = self.pf_axes[n].plot(numpy.repeat(self.data['noise'][n][-1], len(self.y)), self.y,
259 259 color="k", linestyle="dashed", lw=1)[0]
260 260 else:
261 261 ax.collections.remove(ax.collections[0])
262 262 ax.plt = ax.pcolormesh(x, y, z[n].T,
263 263 vmin=self.zmin,
264 264 vmax=self.zmax,
265 265 cmap=plt.get_cmap(self.colormap)
266 266 )
267 267 if self.showprofile:
268 268 ax.plot_profile.set_data(self.data['rti'][n][-1], self.y)
269 269 ax.plot_noise.set_data(numpy.repeat(
270 270 self.data['noise'][n][-1], len(self.y)), self.y)
271 271
272 272
273 273 class CoherencePlot(RTIPlot):
274 274 '''
275 275 Plot for Coherence data
276 276 '''
277 277
278 278 CODE = 'coh'
279 279
280 280 def setup(self):
281 281 self.xaxis = 'time'
282 282 self.ncols = 1
283 283 self.nrows = len(self.data.pairs)
284 284 self.nplots = len(self.data.pairs)
285 285 self.ylabel = 'Range [km]'
286 286 if self.CODE == 'coh':
287 287 self.cb_label = ''
288 288 self.titles = [
289 289 'Coherence Map Ch{} * Ch{}'.format(x[0], x[1]) for x in self.data.pairs]
290 290 else:
291 291 self.cb_label = 'Degrees'
292 292 self.titles = [
293 293 'Phase Map Ch{} * Ch{}'.format(x[0], x[1]) for x in self.data.pairs]
294 294
295 295
296 296 class PhasePlot(CoherencePlot):
297 297 '''
298 298 Plot for Phase map data
299 299 '''
300 300
301 301 CODE = 'phase'
302 302 colormap = 'seismic'
303 303
304 304
305 305 class NoisePlot(Plot):
306 306 '''
307 307 Plot for noise
308 308 '''
309 309
310 310 CODE = 'noise'
311 311
312 312 def setup(self):
313 313 self.xaxis = 'time'
314 314 self.ncols = 1
315 315 self.nrows = 1
316 316 self.nplots = 1
317 317 self.ylabel = 'Intensity [dB]'
318 318 self.titles = ['Noise']
319 319 self.colorbar = False
320 320
321 321 def plot(self):
322 322
323 323 x = self.data.times
324 324 xmin = self.data.min_time
325 325 xmax = xmin + self.xrange * 60 * 60
326 326 Y = self.data[self.CODE]
327 327
328 328 if self.axes[0].firsttime:
329 329 for ch in self.data.channels:
330 330 y = Y[ch]
331 331 self.axes[0].plot(x, y, lw=1, label='Ch{}'.format(ch))
332 332 plt.legend()
333 333 else:
334 334 for ch in self.data.channels:
335 335 y = Y[ch]
336 336 self.axes[0].lines[ch].set_data(x, y)
337 337
338 338 self.ymin = numpy.nanmin(Y) - 5
339 339 self.ymax = numpy.nanmax(Y) + 5
340 340
341 341
342 342 class SnrPlot(RTIPlot):
343 343 '''
344 344 Plot for SNR Data
345 345 '''
346 346
347 347 CODE = 'snr'
348 348 colormap = 'jet'
349 349
350 350
351 351 class DopplerPlot(RTIPlot):
352 352 '''
353 353 Plot for DOPPLER Data
354 354 '''
355 355
356 356 CODE = 'dop'
357 357 colormap = 'jet'
358 358
359 359
360 360 class SkyMapPlot(Plot):
361 361 '''
362 362 Plot for meteors detection data
363 363 '''
364 364
365 365 CODE = 'param'
366 366
367 367 def setup(self):
368 368
369 369 self.ncols = 1
370 370 self.nrows = 1
371 371 self.width = 7.2
372 372 self.height = 7.2
373 373 self.nplots = 1
374 374 self.xlabel = 'Zonal Zenith Angle (deg)'
375 375 self.ylabel = 'Meridional Zenith Angle (deg)'
376 376 self.polar = True
377 377 self.ymin = -180
378 378 self.ymax = 180
379 379 self.colorbar = False
380 380
381 381 def plot(self):
382 382
383 383 arrayParameters = numpy.concatenate(self.data['param'])
384 384 error = arrayParameters[:, -1]
385 385 indValid = numpy.where(error == 0)[0]
386 386 finalMeteor = arrayParameters[indValid, :]
387 387 finalAzimuth = finalMeteor[:, 3]
388 388 finalZenith = finalMeteor[:, 4]
389 389
390 390 x = finalAzimuth * numpy.pi / 180
391 391 y = finalZenith
392 392
393 393 ax = self.axes[0]
394 394
395 395 if ax.firsttime:
396 396 ax.plot = ax.plot(x, y, 'bo', markersize=5)[0]
397 397 else:
398 398 ax.plot.set_data(x, y)
399 399
400 400 dt1 = self.getDateTime(self.data.min_time).strftime('%y/%m/%d %H:%M:%S')
401 401 dt2 = self.getDateTime(self.data.max_time).strftime('%y/%m/%d %H:%M:%S')
402 402 title = 'Meteor Detection Sky Map\n %s - %s \n Number of events: %5.0f\n' % (dt1,
403 403 dt2,
404 404 len(x))
405 405 self.titles[0] = title
406 406
407 407
408 408 class ParametersPlot(RTIPlot):
409 409 '''
410 410 Plot for data_param object
411 411 '''
412 412
413 413 CODE = 'param'
414 414 colormap = 'seismic'
415 415
416 416 def setup(self):
417 417 self.xaxis = 'time'
418 418 self.ncols = 1
419 419 self.nrows = self.data.shape(self.CODE)[0]
420 420 self.nplots = self.nrows
421 421 if self.showSNR:
422 422 self.nrows += 1
423 423 self.nplots += 1
424 424
425 425 self.ylabel = 'Height [km]'
426 426 if not self.titles:
427 427 self.titles = self.data.parameters \
428 428 if self.data.parameters else ['Param {}'.format(x) for x in range(self.nrows)]
429 429 if self.showSNR:
430 430 self.titles.append('SNR')
431 431
432 432 def plot(self):
433 433 self.data.normalize_heights()
434 434 self.x = self.data.times
435 435 self.y = self.data.heights
436 436 if self.showSNR:
437 437 self.z = numpy.concatenate(
438 438 (self.data[self.CODE], self.data['snr'])
439 439 )
440 440 else:
441 441 self.z = self.data[self.CODE]
442 442
443 443 self.z = numpy.ma.masked_invalid(self.z)
444 444
445 445 if self.decimation is None:
446 446 x, y, z = self.fill_gaps(self.x, self.y, self.z)
447 447 else:
448 448 x, y, z = self.fill_gaps(*self.decimate())
449 449
450 450 for n, ax in enumerate(self.axes):
451 451
452 452 self.zmax = self.zmax if self.zmax is not None else numpy.max(
453 453 self.z[n])
454 454 self.zmin = self.zmin if self.zmin is not None else numpy.min(
455 455 self.z[n])
456 456
457 457 if ax.firsttime:
458 458 if self.zlimits is not None:
459 459 self.zmin, self.zmax = self.zlimits[n]
460 460
461 461 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
462 462 vmin=self.zmin,
463 463 vmax=self.zmax,
464 464 cmap=self.cmaps[n]
465 465 )
466 466 else:
467 467 if self.zlimits is not None:
468 468 self.zmin, self.zmax = self.zlimits[n]
469 469 ax.collections.remove(ax.collections[0])
470 470 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
471 471 vmin=self.zmin,
472 472 vmax=self.zmax,
473 473 cmap=self.cmaps[n]
474 474 )
475 475
476 476
477 477 class OutputPlot(ParametersPlot):
478 478 '''
479 479 Plot data_output object
480 480 '''
481 481
482 482 CODE = 'output'
483 483 colormap = 'seismic'
484 484
485 485
486 486 class PolarMapPlot(Plot):
487 487 '''
488 488 Plot for weather radar
489 489 '''
490 490
491 491 CODE = 'param'
492 492 colormap = 'seismic'
493 493
494 494 def setup(self):
495 495 self.ncols = 1
496 496 self.nrows = 1
497 497 self.width = 9
498 498 self.height = 8
499 499 self.mode = self.data.meta['mode']
500 500 if self.channels is not None:
501 501 self.nplots = len(self.channels)
502 502 self.nrows = len(self.channels)
503 503 else:
504 504 self.nplots = self.data.shape(self.CODE)[0]
505 505 self.nrows = self.nplots
506 506 self.channels = list(range(self.nplots))
507 507 if self.mode == 'E':
508 508 self.xlabel = 'Longitude'
509 509 self.ylabel = 'Latitude'
510 510 else:
511 511 self.xlabel = 'Range (km)'
512 512 self.ylabel = 'Height (km)'
513 513 self.bgcolor = 'white'
514 514 self.cb_labels = self.data.meta['units']
515 515 self.lat = self.data.meta['latitude']
516 516 self.lon = self.data.meta['longitude']
517 517 self.xmin, self.xmax = float(
518 518 km2deg(self.xmin) + self.lon), float(km2deg(self.xmax) + self.lon)
519 519 self.ymin, self.ymax = float(
520 520 km2deg(self.ymin) + self.lat), float(km2deg(self.ymax) + self.lat)
521 521 # self.polar = True
522 522
523 523 def plot(self):
524 524
525 525 for n, ax in enumerate(self.axes):
526 526 data = self.data['param'][self.channels[n]]
527 527
528 528 zeniths = numpy.linspace(
529 529 0, self.data.meta['max_range'], data.shape[1])
530 530 if self.mode == 'E':
531 531 azimuths = -numpy.radians(self.data.heights)+numpy.pi/2
532 532 r, theta = numpy.meshgrid(zeniths, azimuths)
533 533 x, y = r*numpy.cos(theta)*numpy.cos(numpy.radians(self.data.meta['elevation'])), r*numpy.sin(
534 534 theta)*numpy.cos(numpy.radians(self.data.meta['elevation']))
535 535 x = km2deg(x) + self.lon
536 536 y = km2deg(y) + self.lat
537 537 else:
538 538 azimuths = numpy.radians(self.data.heights)
539 539 r, theta = numpy.meshgrid(zeniths, azimuths)
540 540 x, y = r*numpy.cos(theta), r*numpy.sin(theta)
541 541 self.y = zeniths
542 542
543 543 if ax.firsttime:
544 544 if self.zlimits is not None:
545 545 self.zmin, self.zmax = self.zlimits[n]
546 546 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
547 547 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
548 548 vmin=self.zmin,
549 549 vmax=self.zmax,
550 550 cmap=self.cmaps[n])
551 551 else:
552 552 if self.zlimits is not None:
553 553 self.zmin, self.zmax = self.zlimits[n]
554 554 ax.collections.remove(ax.collections[0])
555 555 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
556 556 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
557 557 vmin=self.zmin,
558 558 vmax=self.zmax,
559 559 cmap=self.cmaps[n])
560 560
561 561 if self.mode == 'A':
562 562 continue
563 563
564 564 # plot district names
565 565 f = open('/data/workspace/schain_scripts/distrito.csv')
566 566 for line in f:
567 567 label, lon, lat = [s.strip() for s in line.split(',') if s]
568 568 lat = float(lat)
569 569 lon = float(lon)
570 570 # ax.plot(lon, lat, '.b', ms=2)
571 571 ax.text(lon, lat, label.decode('utf8'), ha='center',
572 572 va='bottom', size='8', color='black')
573 573
574 574 # plot limites
575 575 limites = []
576 576 tmp = []
577 577 for line in open('/data/workspace/schain_scripts/lima.csv'):
578 578 if '#' in line:
579 579 if tmp:
580 580 limites.append(tmp)
581 581 tmp = []
582 582 continue
583 583 values = line.strip().split(',')
584 584 tmp.append((float(values[0]), float(values[1])))
585 585 for points in limites:
586 586 ax.add_patch(
587 587 Polygon(points, ec='k', fc='none', ls='--', lw=0.5))
588 588
589 589 # plot Cuencas
590 590 for cuenca in ('rimac', 'lurin', 'mala', 'chillon', 'chilca', 'chancay-huaral'):
591 591 f = open('/data/workspace/schain_scripts/{}.csv'.format(cuenca))
592 592 values = [line.strip().split(',') for line in f]
593 593 points = [(float(s[0]), float(s[1])) for s in values]
594 594 ax.add_patch(Polygon(points, ec='b', fc='none'))
595 595
596 596 # plot grid
597 597 for r in (15, 30, 45, 60):
598 598 ax.add_artist(plt.Circle((self.lon, self.lat),
599 599 km2deg(r), color='0.6', fill=False, lw=0.2))
600 600 ax.text(
601 601 self.lon + (km2deg(r))*numpy.cos(60*numpy.pi/180),
602 602 self.lat + (km2deg(r))*numpy.sin(60*numpy.pi/180),
603 603 '{}km'.format(r),
604 604 ha='center', va='bottom', size='8', color='0.6', weight='heavy')
605 605
606 606 if self.mode == 'E':
607 607 title = 'El={}$^\circ$'.format(self.data.meta['elevation'])
608 608 label = 'E{:02d}'.format(int(self.data.meta['elevation']))
609 609 else:
610 610 title = 'Az={}$^\circ$'.format(self.data.meta['azimuth'])
611 611 label = 'A{:02d}'.format(int(self.data.meta['azimuth']))
612 612
613 613 self.save_labels = ['{}-{}'.format(lbl, label) for lbl in self.labels]
614 614 self.titles = ['{} {}'.format(
615 615 self.data.parameters[x], title) for x in self.channels]
616 No newline at end of file
616
617 class ScopePlot(Plot):
618
619 '''
620 Plot for Scope
621 '''
622
623 CODE = 'scope'
624
625 def setup(self):
626
627 self.xaxis = 'Range (Km)'
628 self.ncols = 1
629 self.nrows = 1
630 self.nplots = 1
631 self.ylabel = 'Intensity [dB]'
632 self.titles = ['Scope']
633 self.colorbar = False
634 colspan = 3
635 rowspan = 1
636
637 def plot_iq(self, x, y, channelIndexList, thisDatetime, wintitle):
638
639 yreal = y[channelIndexList,:].real
640 yimag = y[channelIndexList,:].imag
641 title = wintitle + " Scope: %s" %(thisDatetime.strftime("%d-%b-%Y"))
642 self.xlabel = "Range (Km)"
643 self.ylabel = "Intensity - IQ"
644
645 self.y = yreal
646 self.x = x
647 self.xmin = min(x)
648 self.xmax = max(x)
649
650
651 self.titles[0] = title
652
653 for i,ax in enumerate(self.axes):
654 title = "Channel %d" %(i)
655 if ax.firsttime:
656 ax.plt_r = ax.plot(x, yreal[i,:], color='b')[0]
657 ax.plt_i = ax.plot(x, yimag[i,:], color='r')[0]
658 else:
659 #pass
660 ax.plt_r.set_data(x, yreal[i,:])
661 ax.plt_i.set_data(x, yimag[i,:])
662
663 def plot_power(self, x, y, channelIndexList, thisDatetime, wintitle):
664 y = y[channelIndexList,:] * numpy.conjugate(y[channelIndexList,:])
665 yreal = y.real
666 self.y = yreal
667 title = wintitle + " Scope: %s" %(thisDatetime.strftime("%d-%b-%Y"))
668 self.xlabel = "Range (Km)"
669 self.ylabel = "Intensity"
670 self.xmin = min(x)
671 self.xmax = max(x)
672
673
674 self.titles[0] = title
675
676 for i,ax in enumerate(self.axes):
677 title = "Channel %d" %(i)
678
679 ychannel = yreal[i,:]
680
681 if ax.firsttime:
682 ax.plt_r = ax.plot(x, ychannel)[0]
683 else:
684 #pass
685 ax.plt_r.set_data(x, ychannel)
686
687
688 def plot(self):
689
690 if self.channels:
691 channels = self.channels
692 else:
693 channels = self.data.channels
694
695
696
697 thisDatetime = datetime.datetime.utcfromtimestamp(self.data.times[-1])
698
699 scope = self.data['scope']
700
701
702 if self.data.flagDataAsBlock:
703
704 for i in range(self.data.nProfiles):
705
706 wintitle1 = " [Profile = %d] " %i
707
708 if self.type == "power":
709 self.plot_power(self.data.heights,
710 scope[:,i,:],
711 channels,
712 thisDatetime,
713 wintitle1
714 )
715
716 if self.type == "iq":
717 self.plot_iq(self.data.heights,
718 scope[:,i,:],
719 channels,
720 thisDatetime,
721 wintitle1
722 )
723
724
725
726
727
728 else:
729 wintitle = " [Profile = %d] " %self.data.profileIndex
730
731 if self.type == "power":
732 self.plot_power(self.data.heights,
733 scope,
734 channels,
735 thisDatetime,
736 wintitle
737 )
738
739 if self.type == "iq":
740 self.plot_iq(self.data.heights,
741 scope,
742 channels,
743 thisDatetime,
744 wintitle
745 )
746
747
748 No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now