##// END OF EJS Templates
Add georef for realtime
Juan C. Espinoza -
r1498:675eff1be3dc
parent child
Show More
@@ -1,1069 +1,1074
1 # Copyright (c) 2012-2020 Jicamarca Radio Observatory
1 # Copyright (c) 2012-2020 Jicamarca Radio Observatory
2 # All rights reserved.
2 # All rights reserved.
3 #
3 #
4 # Distributed under the terms of the BSD 3-clause license.
4 # Distributed under the terms of the BSD 3-clause license.
5 """Definition of diferent Data objects for different types of data
5 """Definition of diferent Data objects for different types of data
6
6
7 Here you will find the diferent data objects for the different types
7 Here you will find the diferent data objects for the different types
8 of data, this data objects must be used as dataIn or dataOut objects in
8 of data, this data objects must be used as dataIn or dataOut objects in
9 processing units and operations. Currently the supported data objects are:
9 processing units and operations. Currently the supported data objects are:
10 Voltage, Spectra, SpectraHeis, Fits, Correlation and Parameters
10 Voltage, Spectra, SpectraHeis, Fits, Correlation and Parameters
11 """
11 """
12
12
13 import copy
13 import copy
14 import numpy
14 import numpy
15 import datetime
15 import datetime
16 import json
16 import json
17
17
18 import schainpy.admin
18 import schainpy.admin
19 from schainpy.utils import log
19 from schainpy.utils import log
20 from .jroheaderIO import SystemHeader, RadarControllerHeader
20 from .jroheaderIO import SystemHeader, RadarControllerHeader
21 from schainpy.model.data import _noise
21 from schainpy.model.data import _noise
22
22
23
23
24 def getNumpyDtype(dataTypeCode):
24 def getNumpyDtype(dataTypeCode):
25
25
26 if dataTypeCode == 0:
26 if dataTypeCode == 0:
27 numpyDtype = numpy.dtype([('real', '<i1'), ('imag', '<i1')])
27 numpyDtype = numpy.dtype([('real', '<i1'), ('imag', '<i1')])
28 elif dataTypeCode == 1:
28 elif dataTypeCode == 1:
29 numpyDtype = numpy.dtype([('real', '<i2'), ('imag', '<i2')])
29 numpyDtype = numpy.dtype([('real', '<i2'), ('imag', '<i2')])
30 elif dataTypeCode == 2:
30 elif dataTypeCode == 2:
31 numpyDtype = numpy.dtype([('real', '<i4'), ('imag', '<i4')])
31 numpyDtype = numpy.dtype([('real', '<i4'), ('imag', '<i4')])
32 elif dataTypeCode == 3:
32 elif dataTypeCode == 3:
33 numpyDtype = numpy.dtype([('real', '<i8'), ('imag', '<i8')])
33 numpyDtype = numpy.dtype([('real', '<i8'), ('imag', '<i8')])
34 elif dataTypeCode == 4:
34 elif dataTypeCode == 4:
35 numpyDtype = numpy.dtype([('real', '<f4'), ('imag', '<f4')])
35 numpyDtype = numpy.dtype([('real', '<f4'), ('imag', '<f4')])
36 elif dataTypeCode == 5:
36 elif dataTypeCode == 5:
37 numpyDtype = numpy.dtype([('real', '<f8'), ('imag', '<f8')])
37 numpyDtype = numpy.dtype([('real', '<f8'), ('imag', '<f8')])
38 else:
38 else:
39 raise ValueError('dataTypeCode was not defined')
39 raise ValueError('dataTypeCode was not defined')
40
40
41 return numpyDtype
41 return numpyDtype
42
42
43
43
44 def getDataTypeCode(numpyDtype):
44 def getDataTypeCode(numpyDtype):
45
45
46 if numpyDtype == numpy.dtype([('real', '<i1'), ('imag', '<i1')]):
46 if numpyDtype == numpy.dtype([('real', '<i1'), ('imag', '<i1')]):
47 datatype = 0
47 datatype = 0
48 elif numpyDtype == numpy.dtype([('real', '<i2'), ('imag', '<i2')]):
48 elif numpyDtype == numpy.dtype([('real', '<i2'), ('imag', '<i2')]):
49 datatype = 1
49 datatype = 1
50 elif numpyDtype == numpy.dtype([('real', '<i4'), ('imag', '<i4')]):
50 elif numpyDtype == numpy.dtype([('real', '<i4'), ('imag', '<i4')]):
51 datatype = 2
51 datatype = 2
52 elif numpyDtype == numpy.dtype([('real', '<i8'), ('imag', '<i8')]):
52 elif numpyDtype == numpy.dtype([('real', '<i8'), ('imag', '<i8')]):
53 datatype = 3
53 datatype = 3
54 elif numpyDtype == numpy.dtype([('real', '<f4'), ('imag', '<f4')]):
54 elif numpyDtype == numpy.dtype([('real', '<f4'), ('imag', '<f4')]):
55 datatype = 4
55 datatype = 4
56 elif numpyDtype == numpy.dtype([('real', '<f8'), ('imag', '<f8')]):
56 elif numpyDtype == numpy.dtype([('real', '<f8'), ('imag', '<f8')]):
57 datatype = 5
57 datatype = 5
58 else:
58 else:
59 datatype = None
59 datatype = None
60
60
61 return datatype
61 return datatype
62
62
63
63
64 def hildebrand_sekhon(data, navg):
64 def hildebrand_sekhon(data, navg):
65 """
65 """
66 This method is for the objective determination of the noise level in Doppler spectra. This
66 This method is for the objective determination of the noise level in Doppler spectra. This
67 implementation technique is based on the fact that the standard deviation of the spectral
67 implementation technique is based on the fact that the standard deviation of the spectral
68 densities is equal to the mean spectral density for white Gaussian noise
68 densities is equal to the mean spectral density for white Gaussian noise
69
69
70 Inputs:
70 Inputs:
71 Data : heights
71 Data : heights
72 navg : numbers of averages
72 navg : numbers of averages
73
73
74 Return:
74 Return:
75 mean : noise's level
75 mean : noise's level
76 """
76 """
77
77
78 sortdata = numpy.sort(data, axis=None)
78 sortdata = numpy.sort(data, axis=None)
79 '''
79 '''
80 lenOfData = len(sortdata)
80 lenOfData = len(sortdata)
81 nums_min = lenOfData*0.2
81 nums_min = lenOfData*0.2
82
82
83 if nums_min <= 5:
83 if nums_min <= 5:
84
84
85 nums_min = 5
85 nums_min = 5
86
86
87 sump = 0.
87 sump = 0.
88 sumq = 0.
88 sumq = 0.
89
89
90 j = 0
90 j = 0
91 cont = 1
91 cont = 1
92
92
93 while((cont == 1)and(j < lenOfData)):
93 while((cont == 1)and(j < lenOfData)):
94
94
95 sump += sortdata[j]
95 sump += sortdata[j]
96 sumq += sortdata[j]**2
96 sumq += sortdata[j]**2
97
97
98 if j > nums_min:
98 if j > nums_min:
99 rtest = float(j)/(j-1) + 1.0/navg
99 rtest = float(j)/(j-1) + 1.0/navg
100 if ((sumq*j) > (rtest*sump**2)):
100 if ((sumq*j) > (rtest*sump**2)):
101 j = j - 1
101 j = j - 1
102 sump = sump - sortdata[j]
102 sump = sump - sortdata[j]
103 sumq = sumq - sortdata[j]**2
103 sumq = sumq - sortdata[j]**2
104 cont = 0
104 cont = 0
105
105
106 j += 1
106 j += 1
107
107
108 lnoise = sump / j
108 lnoise = sump / j
109 '''
109 '''
110 return _noise.hildebrand_sekhon(sortdata, navg)
110 return _noise.hildebrand_sekhon(sortdata, navg)
111
111
112
112
113 class Beam:
113 class Beam:
114
114
115 def __init__(self):
115 def __init__(self):
116 self.codeList = []
116 self.codeList = []
117 self.azimuthList = []
117 self.azimuthList = []
118 self.zenithList = []
118 self.zenithList = []
119
119
120
120
121 class GenericData(object):
121 class GenericData(object):
122
122
123 flagNoData = True
123 flagNoData = True
124
124
125 def copy(self, inputObj=None):
125 def copy(self, inputObj=None):
126
126
127 if inputObj == None:
127 if inputObj == None:
128 return copy.deepcopy(self)
128 return copy.deepcopy(self)
129
129
130 for key in list(inputObj.__dict__.keys()):
130 for key in list(inputObj.__dict__.keys()):
131
131
132 attribute = inputObj.__dict__[key]
132 attribute = inputObj.__dict__[key]
133
133
134 # If this attribute is a tuple or list
134 # If this attribute is a tuple or list
135 if type(inputObj.__dict__[key]) in (tuple, list):
135 if type(inputObj.__dict__[key]) in (tuple, list):
136 self.__dict__[key] = attribute[:]
136 self.__dict__[key] = attribute[:]
137 continue
137 continue
138
138
139 # If this attribute is another object or instance
139 # If this attribute is another object or instance
140 if hasattr(attribute, '__dict__'):
140 if hasattr(attribute, '__dict__'):
141 self.__dict__[key] = attribute.copy()
141 self.__dict__[key] = attribute.copy()
142 continue
142 continue
143
143
144 self.__dict__[key] = inputObj.__dict__[key]
144 self.__dict__[key] = inputObj.__dict__[key]
145
145
146 def deepcopy(self):
146 def deepcopy(self):
147
147
148 return copy.deepcopy(self)
148 return copy.deepcopy(self)
149
149
150 def isEmpty(self):
150 def isEmpty(self):
151
151
152 return self.flagNoData
152 return self.flagNoData
153
153
154 def isReady(self):
154 def isReady(self):
155
155
156 return not self.flagNoData
156 return not self.flagNoData
157
157
158
158
159 class JROData(GenericData):
159 class JROData(GenericData):
160
160
161 systemHeaderObj = SystemHeader()
161 systemHeaderObj = SystemHeader()
162 radarControllerHeaderObj = RadarControllerHeader()
162 radarControllerHeaderObj = RadarControllerHeader()
163 type = None
163 type = None
164 datatype = None # dtype but in string
164 datatype = None # dtype but in string
165 nProfiles = None
165 nProfiles = None
166 heightList = None
166 heightList = None
167 channelList = None
167 channelList = None
168 flagDiscontinuousBlock = False
168 flagDiscontinuousBlock = False
169 useLocalTime = False
169 useLocalTime = False
170 utctime = None
170 utctime = None
171 timeZone = None
171 timeZone = None
172 dstFlag = None
172 dstFlag = None
173 errorCount = None
173 errorCount = None
174 blocksize = None
174 blocksize = None
175 flagDecodeData = False # asumo q la data no esta decodificada
175 flagDecodeData = False # asumo q la data no esta decodificada
176 flagDeflipData = False # asumo q la data no esta sin flip
176 flagDeflipData = False # asumo q la data no esta sin flip
177 flagShiftFFT = False
177 flagShiftFFT = False
178 nCohInt = None
178 nCohInt = None
179 windowOfFilter = 1
179 windowOfFilter = 1
180 C = 3e8
180 C = 3e8
181 frequency = 49.92e6
181 frequency = 49.92e6
182 realtime = False
182 realtime = False
183 beacon_heiIndexList = None
183 beacon_heiIndexList = None
184 last_block = None
184 last_block = None
185 blocknow = None
185 blocknow = None
186 azimuth = None
186 azimuth = None
187 zenith = None
187 zenith = None
188 beam = Beam()
188 beam = Beam()
189 profileIndex = None
189 profileIndex = None
190 error = None
190 error = None
191 data = None
191 data = None
192 nmodes = None
192 nmodes = None
193 h0 = 0
193 h0 = 0
194 metadata_list = ['heightList', 'timeZone', 'type']
194 metadata_list = ['heightList', 'timeZone', 'type']
195
195
196 def __str__(self):
196 def __str__(self):
197
197
198 return '{} - {}'.format(self.type, self.datatime())
198 return '{} - {}'.format(self.type, self.datatime())
199
199
200 def getNoise(self):
200 def getNoise(self):
201
201
202 raise NotImplementedError
202 raise NotImplementedError
203
203
204 @property
204 @property
205 def nChannels(self):
205 def nChannels(self):
206
206
207 return len(self.channelList)
207 return len(self.channelList)
208
208
209 @property
209 @property
210 def channelIndexList(self):
210 def channelIndexList(self):
211
211
212 return list(range(self.nChannels))
212 return list(range(self.nChannels))
213
213
214 @property
214 @property
215 def nHeights(self):
215 def nHeights(self):
216
216
217 return len(self.heightList)
217 return len(self.heightList)
218
218
219 def getDeltaH(self):
219 def getDeltaH(self):
220
220
221 return self.heightList[1] - self.heightList[0]
221 return self.heightList[1] - self.heightList[0]
222
222
223 @property
223 @property
224 def ltctime(self):
224 def ltctime(self):
225
225
226 if self.useLocalTime:
226 if self.useLocalTime:
227 return self.utctime - self.timeZone * 60
227 return self.utctime - self.timeZone * 60
228
228
229 return self.utctime
229 return self.utctime
230
230
231 @property
231 @property
232 def datatime(self):
232 def datatime(self):
233
233
234 datatimeValue = datetime.datetime.utcfromtimestamp(self.ltctime)
234 datatimeValue = datetime.datetime.utcfromtimestamp(self.ltctime)
235 return datatimeValue
235 return datatimeValue
236
236
237 def getTimeRange(self):
237 def getTimeRange(self):
238
238
239 datatime = []
239 datatime = []
240
240
241 datatime.append(self.ltctime)
241 datatime.append(self.ltctime)
242 datatime.append(self.ltctime + self.timeInterval + 1)
242 datatime.append(self.ltctime + self.timeInterval + 1)
243
243
244 datatime = numpy.array(datatime)
244 datatime = numpy.array(datatime)
245
245
246 return datatime
246 return datatime
247
247
248 def getFmaxTimeResponse(self):
248 def getFmaxTimeResponse(self):
249
249
250 period = (10**-6) * self.getDeltaH() / (0.15)
250 period = (10**-6) * self.getDeltaH() / (0.15)
251
251
252 PRF = 1. / (period * self.nCohInt)
252 PRF = 1. / (period * self.nCohInt)
253
253
254 fmax = PRF
254 fmax = PRF
255
255
256 return fmax
256 return fmax
257
257
258 def getFmax(self):
258 def getFmax(self):
259 PRF = 1. / (self.ippSeconds * self.nCohInt)
259 PRF = 1. / (self.ippSeconds * self.nCohInt)
260
260
261 fmax = PRF
261 fmax = PRF
262 return fmax
262 return fmax
263
263
264 def getVmax(self):
264 def getVmax(self):
265
265
266 _lambda = self.C / self.frequency
266 _lambda = self.C / self.frequency
267
267
268 vmax = self.getFmax() * _lambda / 2
268 vmax = self.getFmax() * _lambda / 2
269
269
270 return vmax
270 return vmax
271
271
272 @property
272 @property
273 def ippSeconds(self):
273 def ippSeconds(self):
274 '''
274 '''
275 '''
275 '''
276 return self.radarControllerHeaderObj.ippSeconds
276 return self.radarControllerHeaderObj.ippSeconds
277
277
278 @ippSeconds.setter
278 @ippSeconds.setter
279 def ippSeconds(self, ippSeconds):
279 def ippSeconds(self, ippSeconds):
280 '''
280 '''
281 '''
281 '''
282 self.radarControllerHeaderObj.ippSeconds = ippSeconds
282 self.radarControllerHeaderObj.ippSeconds = ippSeconds
283
283
284 @property
284 @property
285 def code(self):
285 def code(self):
286 '''
286 '''
287 '''
287 '''
288 return self.radarControllerHeaderObj.code
288 return self.radarControllerHeaderObj.code
289
289
290 @code.setter
290 @code.setter
291 def code(self, code):
291 def code(self, code):
292 '''
292 '''
293 '''
293 '''
294 self.radarControllerHeaderObj.code = code
294 self.radarControllerHeaderObj.code = code
295
295
296 @property
296 @property
297 def nCode(self):
297 def nCode(self):
298 '''
298 '''
299 '''
299 '''
300 return self.radarControllerHeaderObj.nCode
300 return self.radarControllerHeaderObj.nCode
301
301
302 @nCode.setter
302 @nCode.setter
303 def nCode(self, ncode):
303 def nCode(self, ncode):
304 '''
304 '''
305 '''
305 '''
306 self.radarControllerHeaderObj.nCode = ncode
306 self.radarControllerHeaderObj.nCode = ncode
307
307
308 @property
308 @property
309 def nBaud(self):
309 def nBaud(self):
310 '''
310 '''
311 '''
311 '''
312 return self.radarControllerHeaderObj.nBaud
312 return self.radarControllerHeaderObj.nBaud
313
313
314 @nBaud.setter
314 @nBaud.setter
315 def nBaud(self, nbaud):
315 def nBaud(self, nbaud):
316 '''
316 '''
317 '''
317 '''
318 self.radarControllerHeaderObj.nBaud = nbaud
318 self.radarControllerHeaderObj.nBaud = nbaud
319
319
320 @property
320 @property
321 def ipp(self):
321 def ipp(self):
322 '''
322 '''
323 '''
323 '''
324 return self.radarControllerHeaderObj.ipp
324 return self.radarControllerHeaderObj.ipp
325
325
326 @ipp.setter
326 @ipp.setter
327 def ipp(self, ipp):
327 def ipp(self, ipp):
328 '''
328 '''
329 '''
329 '''
330 self.radarControllerHeaderObj.ipp = ipp
330 self.radarControllerHeaderObj.ipp = ipp
331
331
332 @property
332 @property
333 def metadata(self):
333 def metadata(self):
334 '''
334 '''
335 '''
335 '''
336
336
337 return {attr: getattr(self, attr) for attr in self.metadata_list}
337 return {attr: getattr(self, attr) for attr in self.metadata_list}
338
338
339
339
340 class Voltage(JROData):
340 class Voltage(JROData):
341
341
342 dataPP_POW = None
342 dataPP_POW = None
343 dataPP_DOP = None
343 dataPP_DOP = None
344 dataPP_WIDTH = None
344 dataPP_WIDTH = None
345 dataPP_SNR = None
345 dataPP_SNR = None
346
346
347 def __init__(self):
347 def __init__(self):
348 '''
348 '''
349 Constructor
349 Constructor
350 '''
350 '''
351
351
352 self.useLocalTime = True
352 self.useLocalTime = True
353 self.radarControllerHeaderObj = RadarControllerHeader()
353 self.radarControllerHeaderObj = RadarControllerHeader()
354 self.systemHeaderObj = SystemHeader()
354 self.systemHeaderObj = SystemHeader()
355 self.type = "Voltage"
355 self.type = "Voltage"
356 self.data = None
356 self.data = None
357 self.nProfiles = None
357 self.nProfiles = None
358 self.heightList = None
358 self.heightList = None
359 self.channelList = None
359 self.channelList = None
360 self.flagNoData = True
360 self.flagNoData = True
361 self.flagDiscontinuousBlock = False
361 self.flagDiscontinuousBlock = False
362 self.utctime = None
362 self.utctime = None
363 self.timeZone = 0
363 self.timeZone = 0
364 self.dstFlag = None
364 self.dstFlag = None
365 self.errorCount = None
365 self.errorCount = None
366 self.nCohInt = None
366 self.nCohInt = None
367 self.blocksize = None
367 self.blocksize = None
368 self.flagCohInt = False
368 self.flagCohInt = False
369 self.flagDecodeData = False # asumo q la data no esta decodificada
369 self.flagDecodeData = False # asumo q la data no esta decodificada
370 self.flagDeflipData = False # asumo q la data no esta sin flip
370 self.flagDeflipData = False # asumo q la data no esta sin flip
371 self.flagShiftFFT = False
371 self.flagShiftFFT = False
372 self.flagDataAsBlock = False # Asumo que la data es leida perfil a perfil
372 self.flagDataAsBlock = False # Asumo que la data es leida perfil a perfil
373 self.profileIndex = 0
373 self.profileIndex = 0
374 self.metadata_list = ['type', 'heightList', 'timeZone', 'nProfiles', 'channelList', 'nCohInt',
374 self.metadata_list = ['type', 'heightList', 'timeZone', 'nProfiles', 'channelList', 'nCohInt',
375 'code', 'nCode', 'nBaud', 'ippSeconds', 'ipp']
375 'code', 'nCode', 'nBaud', 'ippSeconds', 'ipp']
376
376
377 def getNoisebyHildebrand(self, channel=None):
377 def getNoisebyHildebrand(self, channel=None):
378 """
378 """
379 Determino el nivel de ruido usando el metodo Hildebrand-Sekhon
379 Determino el nivel de ruido usando el metodo Hildebrand-Sekhon
380
380
381 Return:
381 Return:
382 noiselevel
382 noiselevel
383 """
383 """
384
384
385 if channel != None:
385 if channel != None:
386 data = self.data[channel]
386 data = self.data[channel]
387 nChannels = 1
387 nChannels = 1
388 else:
388 else:
389 data = self.data
389 data = self.data
390 nChannels = self.nChannels
390 nChannels = self.nChannels
391
391
392 noise = numpy.zeros(nChannels)
392 noise = numpy.zeros(nChannels)
393 power = data * numpy.conjugate(data)
393 power = data * numpy.conjugate(data)
394
394
395 for thisChannel in range(nChannels):
395 for thisChannel in range(nChannels):
396 if nChannels == 1:
396 if nChannels == 1:
397 daux = power[:].real
397 daux = power[:].real
398 else:
398 else:
399 daux = power[thisChannel, :].real
399 daux = power[thisChannel, :].real
400 noise[thisChannel] = hildebrand_sekhon(daux, self.nCohInt)
400 noise[thisChannel] = hildebrand_sekhon(daux, self.nCohInt)
401
401
402 return noise
402 return noise
403
403
404 def getNoise(self, type=1, channel=None):
404 def getNoise(self, type=1, channel=None):
405
405
406 if type == 1:
406 if type == 1:
407 noise = self.getNoisebyHildebrand(channel)
407 noise = self.getNoisebyHildebrand(channel)
408
408
409 return noise
409 return noise
410
410
411 def getPower(self, channel=None):
411 def getPower(self, channel=None):
412
412
413 if channel != None:
413 if channel != None:
414 data = self.data[channel]
414 data = self.data[channel]
415 else:
415 else:
416 data = self.data
416 data = self.data
417
417
418 power = data * numpy.conjugate(data)
418 power = data * numpy.conjugate(data)
419 powerdB = 10 * numpy.log10(power.real)
419 powerdB = 10 * numpy.log10(power.real)
420 powerdB = numpy.squeeze(powerdB)
420 powerdB = numpy.squeeze(powerdB)
421
421
422 return powerdB
422 return powerdB
423
423
424 @property
424 @property
425 def timeInterval(self):
425 def timeInterval(self):
426
426
427 return self.ippSeconds * self.nCohInt
427 return self.ippSeconds * self.nCohInt
428
428
429 noise = property(getNoise, "I'm the 'nHeights' property.")
429 noise = property(getNoise, "I'm the 'nHeights' property.")
430
430
431
431
432 class Spectra(JROData):
432 class Spectra(JROData):
433
433
434 def __init__(self):
434 def __init__(self):
435 '''
435 '''
436 Constructor
436 Constructor
437 '''
437 '''
438
438
439 self.data_dc = None
439 self.data_dc = None
440 self.data_spc = None
440 self.data_spc = None
441 self.data_cspc = None
441 self.data_cspc = None
442 self.useLocalTime = True
442 self.useLocalTime = True
443 self.radarControllerHeaderObj = RadarControllerHeader()
443 self.radarControllerHeaderObj = RadarControllerHeader()
444 self.systemHeaderObj = SystemHeader()
444 self.systemHeaderObj = SystemHeader()
445 self.type = "Spectra"
445 self.type = "Spectra"
446 self.timeZone = 0
446 self.timeZone = 0
447 self.nProfiles = None
447 self.nProfiles = None
448 self.heightList = None
448 self.heightList = None
449 self.channelList = None
449 self.channelList = None
450 self.pairsList = None
450 self.pairsList = None
451 self.flagNoData = True
451 self.flagNoData = True
452 self.flagDiscontinuousBlock = False
452 self.flagDiscontinuousBlock = False
453 self.utctime = None
453 self.utctime = None
454 self.nCohInt = None
454 self.nCohInt = None
455 self.nIncohInt = None
455 self.nIncohInt = None
456 self.blocksize = None
456 self.blocksize = None
457 self.nFFTPoints = None
457 self.nFFTPoints = None
458 self.wavelength = None
458 self.wavelength = None
459 self.flagDecodeData = False # asumo q la data no esta decodificada
459 self.flagDecodeData = False # asumo q la data no esta decodificada
460 self.flagDeflipData = False # asumo q la data no esta sin flip
460 self.flagDeflipData = False # asumo q la data no esta sin flip
461 self.flagShiftFFT = False
461 self.flagShiftFFT = False
462 self.ippFactor = 1
462 self.ippFactor = 1
463 self.beacon_heiIndexList = []
463 self.beacon_heiIndexList = []
464 self.noise_estimation = None
464 self.noise_estimation = None
465 self.metadata_list = ['type', 'heightList', 'timeZone', 'pairsList', 'channelList', 'nCohInt',
465 self.metadata_list = ['type', 'heightList', 'timeZone', 'pairsList', 'channelList', 'nCohInt',
466 'code', 'nCode', 'nBaud', 'ippSeconds', 'ipp','nIncohInt', 'nFFTPoints', 'nProfiles']
466 'code', 'nCode', 'nBaud', 'ippSeconds', 'ipp','nIncohInt', 'nFFTPoints', 'nProfiles']
467
467
468 def getNoisebyHildebrand(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None):
468 def getNoisebyHildebrand(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None):
469 """
469 """
470 Determino el nivel de ruido usando el metodo Hildebrand-Sekhon
470 Determino el nivel de ruido usando el metodo Hildebrand-Sekhon
471
471
472 Return:
472 Return:
473 noiselevel
473 noiselevel
474 """
474 """
475
475
476 noise = numpy.zeros(self.nChannels)
476 noise = numpy.zeros(self.nChannels)
477
477
478 for channel in range(self.nChannels):
478 for channel in range(self.nChannels):
479 daux = self.data_spc[channel,
479 daux = self.data_spc[channel,
480 xmin_index:xmax_index, ymin_index:ymax_index]
480 xmin_index:xmax_index, ymin_index:ymax_index]
481 noise[channel] = hildebrand_sekhon(daux, self.nIncohInt)
481 noise[channel] = hildebrand_sekhon(daux, self.nIncohInt)
482
482
483 return noise
483 return noise
484
484
485 def getNoise(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None):
485 def getNoise(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None):
486
486
487 if self.noise_estimation is not None:
487 if self.noise_estimation is not None:
488 # this was estimated by getNoise Operation defined in jroproc_spectra.py
488 # this was estimated by getNoise Operation defined in jroproc_spectra.py
489 return self.noise_estimation
489 return self.noise_estimation
490 else:
490 else:
491 noise = self.getNoisebyHildebrand(
491 noise = self.getNoisebyHildebrand(
492 xmin_index, xmax_index, ymin_index, ymax_index)
492 xmin_index, xmax_index, ymin_index, ymax_index)
493 return noise
493 return noise
494
494
495 def getFreqRangeTimeResponse(self, extrapoints=0):
495 def getFreqRangeTimeResponse(self, extrapoints=0):
496
496
497 deltafreq = self.getFmaxTimeResponse() / (self.nFFTPoints * self.ippFactor)
497 deltafreq = self.getFmaxTimeResponse() / (self.nFFTPoints * self.ippFactor)
498 freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) - self.nFFTPoints / 2.) - deltafreq / 2
498 freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) - self.nFFTPoints / 2.) - deltafreq / 2
499
499
500 return freqrange
500 return freqrange
501
501
502 def getAcfRange(self, extrapoints=0):
502 def getAcfRange(self, extrapoints=0):
503
503
504 deltafreq = 10. / (self.getFmax() / (self.nFFTPoints * self.ippFactor))
504 deltafreq = 10. / (self.getFmax() / (self.nFFTPoints * self.ippFactor))
505 freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) -self.nFFTPoints / 2.) - deltafreq / 2
505 freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) -self.nFFTPoints / 2.) - deltafreq / 2
506
506
507 return freqrange
507 return freqrange
508
508
509 def getFreqRange(self, extrapoints=0):
509 def getFreqRange(self, extrapoints=0):
510
510
511 deltafreq = self.getFmax() / (self.nFFTPoints * self.ippFactor)
511 deltafreq = self.getFmax() / (self.nFFTPoints * self.ippFactor)
512 freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) -self.nFFTPoints / 2.) - deltafreq / 2
512 freqrange = deltafreq * (numpy.arange(self.nFFTPoints + extrapoints) -self.nFFTPoints / 2.) - deltafreq / 2
513
513
514 return freqrange
514 return freqrange
515
515
516 def getVelRange(self, extrapoints=0):
516 def getVelRange(self, extrapoints=0):
517
517
518 deltav = self.getVmax() / (self.nFFTPoints * self.ippFactor)
518 deltav = self.getVmax() / (self.nFFTPoints * self.ippFactor)
519 velrange = deltav * (numpy.arange(self.nFFTPoints + extrapoints) - self.nFFTPoints / 2.)
519 velrange = deltav * (numpy.arange(self.nFFTPoints + extrapoints) - self.nFFTPoints / 2.)
520
520
521 if self.nmodes:
521 if self.nmodes:
522 return velrange/self.nmodes
522 return velrange/self.nmodes
523 else:
523 else:
524 return velrange
524 return velrange
525
525
526 @property
526 @property
527 def nPairs(self):
527 def nPairs(self):
528
528
529 return len(self.pairsList)
529 return len(self.pairsList)
530
530
531 @property
531 @property
532 def pairsIndexList(self):
532 def pairsIndexList(self):
533
533
534 return list(range(self.nPairs))
534 return list(range(self.nPairs))
535
535
536 @property
536 @property
537 def normFactor(self):
537 def normFactor(self):
538
538
539 pwcode = 1
539 pwcode = 1
540
540
541 if self.flagDecodeData:
541 if self.flagDecodeData:
542 pwcode = numpy.sum(self.code[0]**2)
542 pwcode = numpy.sum(self.code[0]**2)
543 #normFactor = min(self.nFFTPoints,self.nProfiles)*self.nIncohInt*self.nCohInt*pwcode*self.windowOfFilter
543 #normFactor = min(self.nFFTPoints,self.nProfiles)*self.nIncohInt*self.nCohInt*pwcode*self.windowOfFilter
544 normFactor = self.nProfiles * self.nIncohInt * self.nCohInt * pwcode * self.windowOfFilter
544 normFactor = self.nProfiles * self.nIncohInt * self.nCohInt * pwcode * self.windowOfFilter
545
545
546 return normFactor
546 return normFactor
547
547
548 @property
548 @property
549 def flag_cspc(self):
549 def flag_cspc(self):
550
550
551 if self.data_cspc is None:
551 if self.data_cspc is None:
552 return True
552 return True
553
553
554 return False
554 return False
555
555
556 @property
556 @property
557 def flag_dc(self):
557 def flag_dc(self):
558
558
559 if self.data_dc is None:
559 if self.data_dc is None:
560 return True
560 return True
561
561
562 return False
562 return False
563
563
564 @property
564 @property
565 def timeInterval(self):
565 def timeInterval(self):
566
566
567 timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt * self.nProfiles * self.ippFactor
567 timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt * self.nProfiles * self.ippFactor
568 if self.nmodes:
568 if self.nmodes:
569 return self.nmodes*timeInterval
569 return self.nmodes*timeInterval
570 else:
570 else:
571 return timeInterval
571 return timeInterval
572
572
573 def getPower(self):
573 def getPower(self):
574
574
575 factor = self.normFactor
575 factor = self.normFactor
576 z = self.data_spc / factor
576 z = self.data_spc / factor
577 z = numpy.where(numpy.isfinite(z), z, numpy.NAN)
577 z = numpy.where(numpy.isfinite(z), z, numpy.NAN)
578 avg = numpy.average(z, axis=1)
578 avg = numpy.average(z, axis=1)
579 return 10 * numpy.log10(avg)
579 return 10 * numpy.log10(avg)
580
580
581 def getCoherence(self, pairsList=None, phase=False):
581 def getCoherence(self, pairsList=None, phase=False):
582
582
583 z = []
583 z = []
584 if pairsList is None:
584 if pairsList is None:
585 pairsIndexList = self.pairsIndexList
585 pairsIndexList = self.pairsIndexList
586 else:
586 else:
587 pairsIndexList = []
587 pairsIndexList = []
588 for pair in pairsList:
588 for pair in pairsList:
589 if pair not in self.pairsList:
589 if pair not in self.pairsList:
590 raise ValueError("Pair %s is not in dataOut.pairsList" % (
590 raise ValueError("Pair %s is not in dataOut.pairsList" % (
591 pair))
591 pair))
592 pairsIndexList.append(self.pairsList.index(pair))
592 pairsIndexList.append(self.pairsList.index(pair))
593 for i in range(len(pairsIndexList)):
593 for i in range(len(pairsIndexList)):
594 pair = self.pairsList[pairsIndexList[i]]
594 pair = self.pairsList[pairsIndexList[i]]
595 ccf = numpy.average(self.data_cspc[pairsIndexList[i], :, :], axis=0)
595 ccf = numpy.average(self.data_cspc[pairsIndexList[i], :, :], axis=0)
596 powa = numpy.average(self.data_spc[pair[0], :, :], axis=0)
596 powa = numpy.average(self.data_spc[pair[0], :, :], axis=0)
597 powb = numpy.average(self.data_spc[pair[1], :, :], axis=0)
597 powb = numpy.average(self.data_spc[pair[1], :, :], axis=0)
598 avgcoherenceComplex = ccf / numpy.sqrt(powa * powb)
598 avgcoherenceComplex = ccf / numpy.sqrt(powa * powb)
599 if phase:
599 if phase:
600 data = numpy.arctan2(avgcoherenceComplex.imag,
600 data = numpy.arctan2(avgcoherenceComplex.imag,
601 avgcoherenceComplex.real) * 180 / numpy.pi
601 avgcoherenceComplex.real) * 180 / numpy.pi
602 else:
602 else:
603 data = numpy.abs(avgcoherenceComplex)
603 data = numpy.abs(avgcoherenceComplex)
604
604
605 z.append(data)
605 z.append(data)
606
606
607 return numpy.array(z)
607 return numpy.array(z)
608
608
609 def setValue(self, value):
609 def setValue(self, value):
610
610
611 print("This property should not be initialized")
611 print("This property should not be initialized")
612
612
613 return
613 return
614
614
615 noise = property(getNoise, setValue, "I'm the 'nHeights' property.")
615 noise = property(getNoise, setValue, "I'm the 'nHeights' property.")
616
616
617
617
618 class SpectraHeis(Spectra):
618 class SpectraHeis(Spectra):
619
619
620 def __init__(self):
620 def __init__(self):
621
621
622 self.radarControllerHeaderObj = RadarControllerHeader()
622 self.radarControllerHeaderObj = RadarControllerHeader()
623 self.systemHeaderObj = SystemHeader()
623 self.systemHeaderObj = SystemHeader()
624 self.type = "SpectraHeis"
624 self.type = "SpectraHeis"
625 self.nProfiles = None
625 self.nProfiles = None
626 self.heightList = None
626 self.heightList = None
627 self.channelList = None
627 self.channelList = None
628 self.flagNoData = True
628 self.flagNoData = True
629 self.flagDiscontinuousBlock = False
629 self.flagDiscontinuousBlock = False
630 self.utctime = None
630 self.utctime = None
631 self.blocksize = None
631 self.blocksize = None
632 self.profileIndex = 0
632 self.profileIndex = 0
633 self.nCohInt = 1
633 self.nCohInt = 1
634 self.nIncohInt = 1
634 self.nIncohInt = 1
635
635
636 @property
636 @property
637 def normFactor(self):
637 def normFactor(self):
638 pwcode = 1
638 pwcode = 1
639 if self.flagDecodeData:
639 if self.flagDecodeData:
640 pwcode = numpy.sum(self.code[0]**2)
640 pwcode = numpy.sum(self.code[0]**2)
641
641
642 normFactor = self.nIncohInt * self.nCohInt * pwcode
642 normFactor = self.nIncohInt * self.nCohInt * pwcode
643
643
644 return normFactor
644 return normFactor
645
645
646 @property
646 @property
647 def timeInterval(self):
647 def timeInterval(self):
648
648
649 return self.ippSeconds * self.nCohInt * self.nIncohInt
649 return self.ippSeconds * self.nCohInt * self.nIncohInt
650
650
651
651
652 class Fits(JROData):
652 class Fits(JROData):
653
653
654 def __init__(self):
654 def __init__(self):
655
655
656 self.type = "Fits"
656 self.type = "Fits"
657 self.nProfiles = None
657 self.nProfiles = None
658 self.heightList = None
658 self.heightList = None
659 self.channelList = None
659 self.channelList = None
660 self.flagNoData = True
660 self.flagNoData = True
661 self.utctime = None
661 self.utctime = None
662 self.nCohInt = 1
662 self.nCohInt = 1
663 self.nIncohInt = 1
663 self.nIncohInt = 1
664 self.useLocalTime = True
664 self.useLocalTime = True
665 self.profileIndex = 0
665 self.profileIndex = 0
666 self.timeZone = 0
666 self.timeZone = 0
667
667
668 def getTimeRange(self):
668 def getTimeRange(self):
669
669
670 datatime = []
670 datatime = []
671
671
672 datatime.append(self.ltctime)
672 datatime.append(self.ltctime)
673 datatime.append(self.ltctime + self.timeInterval)
673 datatime.append(self.ltctime + self.timeInterval)
674
674
675 datatime = numpy.array(datatime)
675 datatime = numpy.array(datatime)
676
676
677 return datatime
677 return datatime
678
678
679 def getChannelIndexList(self):
679 def getChannelIndexList(self):
680
680
681 return list(range(self.nChannels))
681 return list(range(self.nChannels))
682
682
683 def getNoise(self, type=1):
683 def getNoise(self, type=1):
684
684
685
685
686 if type == 1:
686 if type == 1:
687 noise = self.getNoisebyHildebrand()
687 noise = self.getNoisebyHildebrand()
688
688
689 if type == 2:
689 if type == 2:
690 noise = self.getNoisebySort()
690 noise = self.getNoisebySort()
691
691
692 if type == 3:
692 if type == 3:
693 noise = self.getNoisebyWindow()
693 noise = self.getNoisebyWindow()
694
694
695 return noise
695 return noise
696
696
697 @property
697 @property
698 def timeInterval(self):
698 def timeInterval(self):
699
699
700 timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt
700 timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt
701
701
702 return timeInterval
702 return timeInterval
703
703
704 @property
704 @property
705 def ippSeconds(self):
705 def ippSeconds(self):
706 '''
706 '''
707 '''
707 '''
708 return self.ipp_sec
708 return self.ipp_sec
709
709
710 noise = property(getNoise, "I'm the 'nHeights' property.")
710 noise = property(getNoise, "I'm the 'nHeights' property.")
711
711
712
712
713 class Correlation(JROData):
713 class Correlation(JROData):
714
714
715 def __init__(self):
715 def __init__(self):
716 '''
716 '''
717 Constructor
717 Constructor
718 '''
718 '''
719 self.radarControllerHeaderObj = RadarControllerHeader()
719 self.radarControllerHeaderObj = RadarControllerHeader()
720 self.systemHeaderObj = SystemHeader()
720 self.systemHeaderObj = SystemHeader()
721 self.type = "Correlation"
721 self.type = "Correlation"
722 self.data = None
722 self.data = None
723 self.dtype = None
723 self.dtype = None
724 self.nProfiles = None
724 self.nProfiles = None
725 self.heightList = None
725 self.heightList = None
726 self.channelList = None
726 self.channelList = None
727 self.flagNoData = True
727 self.flagNoData = True
728 self.flagDiscontinuousBlock = False
728 self.flagDiscontinuousBlock = False
729 self.utctime = None
729 self.utctime = None
730 self.timeZone = 0
730 self.timeZone = 0
731 self.dstFlag = None
731 self.dstFlag = None
732 self.errorCount = None
732 self.errorCount = None
733 self.blocksize = None
733 self.blocksize = None
734 self.flagDecodeData = False # asumo q la data no esta decodificada
734 self.flagDecodeData = False # asumo q la data no esta decodificada
735 self.flagDeflipData = False # asumo q la data no esta sin flip
735 self.flagDeflipData = False # asumo q la data no esta sin flip
736 self.pairsList = None
736 self.pairsList = None
737 self.nPoints = None
737 self.nPoints = None
738
738
739 def getPairsList(self):
739 def getPairsList(self):
740
740
741 return self.pairsList
741 return self.pairsList
742
742
743 def getNoise(self, mode=2):
743 def getNoise(self, mode=2):
744
744
745 indR = numpy.where(self.lagR == 0)[0][0]
745 indR = numpy.where(self.lagR == 0)[0][0]
746 indT = numpy.where(self.lagT == 0)[0][0]
746 indT = numpy.where(self.lagT == 0)[0][0]
747
747
748 jspectra0 = self.data_corr[:, :, indR, :]
748 jspectra0 = self.data_corr[:, :, indR, :]
749 jspectra = copy.copy(jspectra0)
749 jspectra = copy.copy(jspectra0)
750
750
751 num_chan = jspectra.shape[0]
751 num_chan = jspectra.shape[0]
752 num_hei = jspectra.shape[2]
752 num_hei = jspectra.shape[2]
753
753
754 freq_dc = jspectra.shape[1] / 2
754 freq_dc = jspectra.shape[1] / 2
755 ind_vel = numpy.array([-2, -1, 1, 2]) + freq_dc
755 ind_vel = numpy.array([-2, -1, 1, 2]) + freq_dc
756
756
757 if ind_vel[0] < 0:
757 if ind_vel[0] < 0:
758 ind_vel[list(range(0, 1))] = ind_vel[list(
758 ind_vel[list(range(0, 1))] = ind_vel[list(
759 range(0, 1))] + self.num_prof
759 range(0, 1))] + self.num_prof
760
760
761 if mode == 1:
761 if mode == 1:
762 jspectra[:, freq_dc, :] = (
762 jspectra[:, freq_dc, :] = (
763 jspectra[:, ind_vel[1], :] + jspectra[:, ind_vel[2], :]) / 2 # CORRECCION
763 jspectra[:, ind_vel[1], :] + jspectra[:, ind_vel[2], :]) / 2 # CORRECCION
764
764
765 if mode == 2:
765 if mode == 2:
766
766
767 vel = numpy.array([-2, -1, 1, 2])
767 vel = numpy.array([-2, -1, 1, 2])
768 xx = numpy.zeros([4, 4])
768 xx = numpy.zeros([4, 4])
769
769
770 for fil in range(4):
770 for fil in range(4):
771 xx[fil, :] = vel[fil]**numpy.asarray(list(range(4)))
771 xx[fil, :] = vel[fil]**numpy.asarray(list(range(4)))
772
772
773 xx_inv = numpy.linalg.inv(xx)
773 xx_inv = numpy.linalg.inv(xx)
774 xx_aux = xx_inv[0, :]
774 xx_aux = xx_inv[0, :]
775
775
776 for ich in range(num_chan):
776 for ich in range(num_chan):
777 yy = jspectra[ich, ind_vel, :]
777 yy = jspectra[ich, ind_vel, :]
778 jspectra[ich, freq_dc, :] = numpy.dot(xx_aux, yy)
778 jspectra[ich, freq_dc, :] = numpy.dot(xx_aux, yy)
779
779
780 junkid = jspectra[ich, freq_dc, :] <= 0
780 junkid = jspectra[ich, freq_dc, :] <= 0
781 cjunkid = sum(junkid)
781 cjunkid = sum(junkid)
782
782
783 if cjunkid.any():
783 if cjunkid.any():
784 jspectra[ich, freq_dc, junkid.nonzero()] = (
784 jspectra[ich, freq_dc, junkid.nonzero()] = (
785 jspectra[ich, ind_vel[1], junkid] + jspectra[ich, ind_vel[2], junkid]) / 2
785 jspectra[ich, ind_vel[1], junkid] + jspectra[ich, ind_vel[2], junkid]) / 2
786
786
787 noise = jspectra0[:, freq_dc, :] - jspectra[:, freq_dc, :]
787 noise = jspectra0[:, freq_dc, :] - jspectra[:, freq_dc, :]
788
788
789 return noise
789 return noise
790
790
791 @property
791 @property
792 def timeInterval(self):
792 def timeInterval(self):
793
793
794 return self.ippSeconds * self.nCohInt * self.nProfiles
794 return self.ippSeconds * self.nCohInt * self.nProfiles
795
795
796 def splitFunctions(self):
796 def splitFunctions(self):
797
797
798 pairsList = self.pairsList
798 pairsList = self.pairsList
799 ccf_pairs = []
799 ccf_pairs = []
800 acf_pairs = []
800 acf_pairs = []
801 ccf_ind = []
801 ccf_ind = []
802 acf_ind = []
802 acf_ind = []
803 for l in range(len(pairsList)):
803 for l in range(len(pairsList)):
804 chan0 = pairsList[l][0]
804 chan0 = pairsList[l][0]
805 chan1 = pairsList[l][1]
805 chan1 = pairsList[l][1]
806
806
807 # Obteniendo pares de Autocorrelacion
807 # Obteniendo pares de Autocorrelacion
808 if chan0 == chan1:
808 if chan0 == chan1:
809 acf_pairs.append(chan0)
809 acf_pairs.append(chan0)
810 acf_ind.append(l)
810 acf_ind.append(l)
811 else:
811 else:
812 ccf_pairs.append(pairsList[l])
812 ccf_pairs.append(pairsList[l])
813 ccf_ind.append(l)
813 ccf_ind.append(l)
814
814
815 data_acf = self.data_cf[acf_ind]
815 data_acf = self.data_cf[acf_ind]
816 data_ccf = self.data_cf[ccf_ind]
816 data_ccf = self.data_cf[ccf_ind]
817
817
818 return acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf
818 return acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf
819
819
820 @property
820 @property
821 def normFactor(self):
821 def normFactor(self):
822 acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf = self.splitFunctions()
822 acf_ind, ccf_ind, acf_pairs, ccf_pairs, data_acf, data_ccf = self.splitFunctions()
823 acf_pairs = numpy.array(acf_pairs)
823 acf_pairs = numpy.array(acf_pairs)
824 normFactor = numpy.zeros((self.nPairs, self.nHeights))
824 normFactor = numpy.zeros((self.nPairs, self.nHeights))
825
825
826 for p in range(self.nPairs):
826 for p in range(self.nPairs):
827 pair = self.pairsList[p]
827 pair = self.pairsList[p]
828
828
829 ch0 = pair[0]
829 ch0 = pair[0]
830 ch1 = pair[1]
830 ch1 = pair[1]
831
831
832 ch0_max = numpy.max(data_acf[acf_pairs == ch0, :, :], axis=1)
832 ch0_max = numpy.max(data_acf[acf_pairs == ch0, :, :], axis=1)
833 ch1_max = numpy.max(data_acf[acf_pairs == ch1, :, :], axis=1)
833 ch1_max = numpy.max(data_acf[acf_pairs == ch1, :, :], axis=1)
834 normFactor[p, :] = numpy.sqrt(ch0_max * ch1_max)
834 normFactor[p, :] = numpy.sqrt(ch0_max * ch1_max)
835
835
836 return normFactor
836 return normFactor
837
837
838
838
839 class Parameters(Spectra):
839 class Parameters(Spectra):
840
840
841 groupList = None # List of Pairs, Groups, etc
841 groupList = None # List of Pairs, Groups, etc
842 data_param = None # Parameters obtained
842 data_param = None # Parameters obtained
843 data_pre = None # Data Pre Parametrization
843 data_pre = None # Data Pre Parametrization
844 data_SNR = None # Signal to Noise Ratio
844 data_SNR = None # Signal to Noise Ratio
845 abscissaList = None # Abscissa, can be velocities, lags or time
845 abscissaList = None # Abscissa, can be velocities, lags or time
846 utctimeInit = None # Initial UTC time
846 utctimeInit = None # Initial UTC time
847 paramInterval = None # Time interval to calculate Parameters in seconds
847 paramInterval = None # Time interval to calculate Parameters in seconds
848 useLocalTime = True
848 useLocalTime = True
849 # Fitting
849 # Fitting
850 data_error = None # Error of the estimation
850 data_error = None # Error of the estimation
851 constants = None
851 constants = None
852 library = None
852 library = None
853 # Output signal
853 # Output signal
854 outputInterval = None # Time interval to calculate output signal in seconds
854 outputInterval = None # Time interval to calculate output signal in seconds
855 data_output = None # Out signal
855 data_output = None # Out signal
856 nAvg = None
856 nAvg = None
857 noise_estimation = None
857 noise_estimation = None
858 GauSPC = None # Fit gaussian SPC
858 GauSPC = None # Fit gaussian SPC
859
859
860 def __init__(self):
860 def __init__(self):
861 '''
861 '''
862 Constructor
862 Constructor
863 '''
863 '''
864 self.radarControllerHeaderObj = RadarControllerHeader()
864 self.radarControllerHeaderObj = RadarControllerHeader()
865 self.systemHeaderObj = SystemHeader()
865 self.systemHeaderObj = SystemHeader()
866 self.type = "Parameters"
866 self.type = "Parameters"
867 self.timeZone = 0
867 self.timeZone = 0
868
868
869 def getTimeRange1(self, interval):
869 def getTimeRange1(self, interval):
870
870
871 datatime = []
871 datatime = []
872
872
873 if self.useLocalTime:
873 if self.useLocalTime:
874 time1 = self.utctimeInit - self.timeZone * 60
874 time1 = self.utctimeInit - self.timeZone * 60
875 else:
875 else:
876 time1 = self.utctimeInit
876 time1 = self.utctimeInit
877
877
878 datatime.append(time1)
878 datatime.append(time1)
879 datatime.append(time1 + interval)
879 datatime.append(time1 + interval)
880 datatime = numpy.array(datatime)
880 datatime = numpy.array(datatime)
881
881
882 return datatime
882 return datatime
883
883
884 @property
884 @property
885 def timeInterval(self):
885 def timeInterval(self):
886
886
887 if hasattr(self, 'timeInterval1'):
887 if hasattr(self, 'timeInterval1'):
888 return self.timeInterval1
888 return self.timeInterval1
889 else:
889 else:
890 return self.paramInterval
890 return self.paramInterval
891
891
892 def setValue(self, value):
892 def setValue(self, value):
893
893
894 print("This property should not be initialized")
894 print("This property should not be initialized")
895
895
896 return
896 return
897
897
898 def getNoise(self):
898 def getNoise(self):
899
899
900 return self.spc_noise
900 return self.spc_noise
901
901
902 noise = property(getNoise, setValue, "I'm the 'Noise' property.")
902 noise = property(getNoise, setValue, "I'm the 'Noise' property.")
903
903
904
904
905 class PlotterData(object):
905 class PlotterData(object):
906 '''
906 '''
907 Object to hold data to be plotted
907 Object to hold data to be plotted
908 '''
908 '''
909
909
910 MAXNUMX = 200
910 MAXNUMX = 1000
911 MAXNUMY = 200
911 MAXNUMY = 1000
912
912
913 def __init__(self, code, exp_code, localtime=True):
913 def __init__(self, code, exp_code, localtime=True):
914
914
915 self.key = code
915 self.key = code
916 self.exp_code = exp_code
916 self.exp_code = exp_code
917 self.ready = False
917 self.ready = False
918 self.flagNoData = False
918 self.flagNoData = False
919 self.localtime = localtime
919 self.localtime = localtime
920 self.data = {}
920 self.data = {}
921 self.meta = {}
921 self.meta = {}
922 self.__heights = []
922 self.__heights = []
923
923
924 def __str__(self):
924 def __str__(self):
925 dum = ['{}{}'.format(key, self.shape(key)) for key in self.data]
925 dum = ['{}{}'.format(key, self.shape(key)) for key in self.data]
926 return 'Data[{}][{}]'.format(';'.join(dum), len(self.times))
926 return 'Data[{}][{}]'.format(';'.join(dum), len(self.times))
927
927
928 def __len__(self):
928 def __len__(self):
929 return len(self.data)
929 return len(self.data)
930
930
931 def __getitem__(self, key):
931 def __getitem__(self, key):
932 if isinstance(key, int):
932 if isinstance(key, int):
933 return self.data[self.times[key]]
933 return self.data[self.times[key]]
934 elif isinstance(key, str):
934 elif isinstance(key, str):
935 ret = numpy.array([self.data[x][key] for x in self.times])
935 ret = numpy.array([self.data[x][key] for x in self.times])
936 if ret.ndim > 1:
936 if ret.ndim > 1:
937 ret = numpy.swapaxes(ret, 0, 1)
937 ret = numpy.swapaxes(ret, 0, 1)
938 return ret
938 return ret
939
939
940 def __contains__(self, key):
940 def __contains__(self, key):
941 return key in self.data[self.min_time]
941 return key in self.data[self.min_time]
942
942
943 def setup(self):
943 def setup(self):
944 '''
944 '''
945 Configure object
945 Configure object
946 '''
946 '''
947 self.type = ''
947 self.type = ''
948 self.ready = False
948 self.ready = False
949 del self.data
949 del self.data
950 self.data = {}
950 self.data = {}
951 self.__heights = []
951 self.__heights = []
952 self.__all_heights = set()
952 self.__all_heights = set()
953
953
954 def shape(self, key):
954 def shape(self, key):
955 '''
955 '''
956 Get the shape of the one-element data for the given key
956 Get the shape of the one-element data for the given key
957 '''
957 '''
958
958
959 if len(self.data[self.min_time][key]):
959 if len(self.data[self.min_time][key]):
960 return self.data[self.min_time][key].shape
960 return self.data[self.min_time][key].shape
961 return (0,)
961 return (0,)
962
962
963 def update(self, data, tm, meta={}):
963 def update(self, data, tm, meta={}):
964 '''
964 '''
965 Update data object with new dataOut
965 Update data object with new dataOut
966 '''
966 '''
967
967
968 self.data[tm] = data
968 self.data[tm] = data
969
969
970 for key, value in meta.items():
970 for key, value in meta.items():
971 setattr(self, key, value)
971 setattr(self, key, value)
972
972
973 def normalize_heights(self):
973 def normalize_heights(self):
974 '''
974 '''
975 Ensure same-dimension of the data for different heighList
975 Ensure same-dimension of the data for different heighList
976 '''
976 '''
977
977
978 H = numpy.array(list(self.__all_heights))
978 H = numpy.array(list(self.__all_heights))
979 H.sort()
979 H.sort()
980 for key in self.data:
980 for key in self.data:
981 shape = self.shape(key)[:-1] + H.shape
981 shape = self.shape(key)[:-1] + H.shape
982 for tm, obj in list(self.data[key].items()):
982 for tm, obj in list(self.data[key].items()):
983 h = self.__heights[self.times.tolist().index(tm)]
983 h = self.__heights[self.times.tolist().index(tm)]
984 if H.size == h.size:
984 if H.size == h.size:
985 continue
985 continue
986 index = numpy.where(numpy.in1d(H, h))[0]
986 index = numpy.where(numpy.in1d(H, h))[0]
987 dummy = numpy.zeros(shape) + numpy.nan
987 dummy = numpy.zeros(shape) + numpy.nan
988 if len(shape) == 2:
988 if len(shape) == 2:
989 dummy[:, index] = obj
989 dummy[:, index] = obj
990 else:
990 else:
991 dummy[index] = obj
991 dummy[index] = obj
992 self.data[key][tm] = dummy
992 self.data[key][tm] = dummy
993
993
994 self.__heights = [H for tm in self.times]
994 self.__heights = [H for tm in self.times]
995
995
996 def jsonify(self, tm, plot_name, plot_type, decimate=False):
996 def jsonify(self, tm, plot_name, plot_type, key=None, decimate=False):
997 '''
997 '''
998 Convert data to json
998 Convert data to json
999 '''
999 '''
1000
1000
1001 if key is None:
1002 key = self.key
1003
1001 meta = {}
1004 meta = {}
1002 meta['xrange'] = []
1005 meta['xrange'] = []
1003 dy = int(len(self.yrange)/self.MAXNUMY) + 1
1006 dy = int(len(self.yrange)/self.MAXNUMY) + 1
1004 tmp = self.data[tm][self.key]
1007 tmp = self.data[tm][key]
1005 shape = tmp.shape
1008 shape = tmp.shape
1006 if len(shape) == 2:
1009 if len(shape) == 2:
1007 data = self.roundFloats(self.data[tm][self.key][::, ::dy].tolist())
1010 data = self.roundFloats(self.data[tm][key][::, ::dy].tolist())
1008 elif len(shape) == 3:
1011 elif len(shape) == 3:
1009 dx = int(self.data[tm][self.key].shape[1]/self.MAXNUMX) + 1
1012 dx = int(self.data[tm][key].shape[1]/self.MAXNUMX) + 1
1010 data = self.roundFloats(
1013 data = self.roundFloats(
1011 self.data[tm][self.key][::, ::dx, ::dy].tolist())
1014 self.data[tm][key][::, ::dx, ::dy].tolist())
1012 meta['xrange'] = self.roundFloats(self.xrange[2][::dx].tolist())
1015 meta['xrange'] = self.roundFloats(self.xrange[2][::dx].tolist())
1013 else:
1016 else:
1014 data = self.roundFloats(self.data[tm][self.key].tolist())
1017 data = self.roundFloats(self.data[tm][key].tolist())
1015
1018
1016 ret = {
1019 ret = {
1017 'plot': plot_name,
1020 'plot': plot_name,
1018 'code': self.exp_code,
1021 'code': self.exp_code,
1019 'time': float(tm),
1022 'time': float(tm),
1020 'data': data,
1023 'data': data,
1021 }
1024 }
1022 meta['type'] = plot_type
1025 meta['type'] = plot_type
1023 meta['interval'] = float(self.interval)
1026 meta['interval'] = float(self.interval)
1024 meta['localtime'] = self.localtime
1027 meta['localtime'] = self.localtime
1025 meta['yrange'] = self.roundFloats(self.yrange[::dy].tolist())
1028 #meta['yrange'] = self.roundFloats(self.yrange[::dy].tolist())
1029 meta['yrange'] = self.roundFloats(self.lat[::dy].tolist())
1030 meta['xrange'] = self.roundFloats(self.lon[::dy].tolist())
1026 meta.update(self.meta)
1031 meta.update(self.meta)
1027 ret['metadata'] = meta
1032 ret['metadata'] = meta
1028 return json.dumps(ret)
1033 return json.dumps(ret)
1029
1034
1030 @property
1035 @property
1031 def times(self):
1036 def times(self):
1032 '''
1037 '''
1033 Return the list of times of the current data
1038 Return the list of times of the current data
1034 '''
1039 '''
1035
1040
1036 ret = [t for t in self.data]
1041 ret = [t for t in self.data]
1037 ret.sort()
1042 ret.sort()
1038 return numpy.array(ret)
1043 return numpy.array(ret)
1039
1044
1040 @property
1045 @property
1041 def min_time(self):
1046 def min_time(self):
1042 '''
1047 '''
1043 Return the minimun time value
1048 Return the minimun time value
1044 '''
1049 '''
1045
1050
1046 return self.times[0]
1051 return self.times[0]
1047
1052
1048 @property
1053 @property
1049 def max_time(self):
1054 def max_time(self):
1050 '''
1055 '''
1051 Return the maximun time value
1056 Return the maximun time value
1052 '''
1057 '''
1053
1058
1054 return self.times[-1]
1059 return self.times[-1]
1055
1060
1056 # @property
1061 # @property
1057 # def heights(self):
1062 # def heights(self):
1058 # '''
1063 # '''
1059 # Return the list of heights of the current data
1064 # Return the list of heights of the current data
1060 # '''
1065 # '''
1061
1066
1062 # return numpy.array(self.__heights[-1])
1067 # return numpy.array(self.__heights[-1])
1063
1068
1064 @staticmethod
1069 @staticmethod
1065 def roundFloats(obj):
1070 def roundFloats(obj):
1066 if isinstance(obj, list):
1071 if isinstance(obj, list):
1067 return list(map(PlotterData.roundFloats, obj))
1072 return list(map(PlotterData.roundFloats, obj))
1068 elif isinstance(obj, float):
1073 elif isinstance(obj, float):
1069 return round(obj, 2)
1074 return round(obj, 4)
@@ -1,732 +1,732
1 # Copyright (c) 2012-2020 Jicamarca Radio Observatory
1 # Copyright (c) 2012-2020 Jicamarca Radio Observatory
2 # All rights reserved.
2 # All rights reserved.
3 #
3 #
4 # Distributed under the terms of the BSD 3-clause license.
4 # Distributed under the terms of the BSD 3-clause license.
5 """Base class to create plot operations
5 """Base class to create plot operations
6
6
7 """
7 """
8
8
9 import os
9 import os
10 import sys
10 import sys
11 import zmq
11 import zmq
12 import time
12 import time
13 import numpy
13 import numpy
14 import datetime
14 import datetime
15 from collections import deque
15 from collections import deque
16 from functools import wraps
16 from functools import wraps
17 from threading import Thread
17 from threading import Thread
18 import matplotlib,re
18 import matplotlib,re
19
19
20 if 'BACKEND' in os.environ:
20 if 'BACKEND' in os.environ:
21 matplotlib.use(os.environ['BACKEND'])
21 matplotlib.use(os.environ['BACKEND'])
22 elif 'linux' in sys.platform:
22 elif 'linux' in sys.platform:
23 matplotlib.use("TkAgg")
23 matplotlib.use("TkAgg")
24 elif 'darwin' in sys.platform:
24 elif 'darwin' in sys.platform:
25 matplotlib.use('MacOSX')
25 matplotlib.use('MacOSX')
26 else:
26 else:
27 from schainpy.utils import log
27 from schainpy.utils import log
28 log.warning('Using default Backend="Agg"', 'INFO')
28 log.warning('Using default Backend="Agg"', 'INFO')
29 matplotlib.use('Agg')
29 matplotlib.use('Agg')
30
30
31 import matplotlib.pyplot as plt
31 import matplotlib.pyplot as plt
32 from matplotlib.patches import Polygon
32 from matplotlib.patches import Polygon
33 from mpl_toolkits.axes_grid1 import make_axes_locatable
33 from mpl_toolkits.axes_grid1 import make_axes_locatable
34 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
34 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
35
35
36 from .plotting_codes import *
36 from .plotting_codes import *
37
37
38 from schainpy.model.data.jrodata import PlotterData
38 from schainpy.model.data.jrodata import PlotterData
39 from schainpy.model.proc.jroproc_base import ProcessingUnit, Operation, MPDecorator
39 from schainpy.model.proc.jroproc_base import ProcessingUnit, Operation, MPDecorator
40 from schainpy.utils import log
40 from schainpy.utils import log
41
41
42 for name, cb_table in sophy_cb_tables:
42 for name, cb_table in sophy_cb_tables:
43 ncmap = matplotlib.colors.ListedColormap(cb_table, name=name)
43 ncmap = matplotlib.colors.ListedColormap(cb_table, name=name)
44 matplotlib.pyplot.register_cmap(cmap=ncmap)
44 matplotlib.pyplot.register_cmap(cmap=ncmap)
45
45
46 EARTH_RADIUS = 6.3710e3
46 EARTH_RADIUS = 6.3710e3
47
47
48 def ll2xy(lat1, lon1, lat2, lon2):
48 def ll2xy(lat1, lon1, lat2, lon2):
49
49
50 p = 0.017453292519943295
50 p = 0.017453292519943295
51 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
51 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
52 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
52 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
53 r = 12742 * numpy.arcsin(numpy.sqrt(a))
53 r = 12742 * numpy.arcsin(numpy.sqrt(a))
54 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
54 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
55 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
55 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
56 theta = -theta + numpy.pi/2
56 theta = -theta + numpy.pi/2
57 return r*numpy.cos(theta), r*numpy.sin(theta)
57 return r*numpy.cos(theta), r*numpy.sin(theta)
58
58
59
59
60 def km2deg(km):
60 def km2deg(km):
61 '''
61 '''
62 Convert distance in km to degrees
62 Convert distance in km to degrees
63 '''
63 '''
64
64
65 return numpy.rad2deg(km/EARTH_RADIUS)
65 return numpy.rad2deg(km/EARTH_RADIUS)
66
66
67
67
68 def figpause(interval):
68 def figpause(interval):
69 backend = plt.rcParams['backend']
69 backend = plt.rcParams['backend']
70 if backend in matplotlib.rcsetup.interactive_bk:
70 if backend in matplotlib.rcsetup.interactive_bk:
71 figManager = matplotlib._pylab_helpers.Gcf.get_active()
71 figManager = matplotlib._pylab_helpers.Gcf.get_active()
72 if figManager is not None:
72 if figManager is not None:
73 canvas = figManager.canvas
73 canvas = figManager.canvas
74 if canvas.figure.stale:
74 if canvas.figure.stale:
75 canvas.draw()
75 canvas.draw()
76 try:
76 try:
77 canvas.start_event_loop(interval)
77 canvas.start_event_loop(interval)
78 except:
78 except:
79 pass
79 pass
80 return
80 return
81
81
82 def popup(message):
82 def popup(message):
83 '''
83 '''
84 '''
84 '''
85
85
86 fig = plt.figure(figsize=(12, 8), facecolor='r')
86 fig = plt.figure(figsize=(12, 8), facecolor='r')
87 text = '\n'.join([s.strip() for s in message.split(':')])
87 text = '\n'.join([s.strip() for s in message.split(':')])
88 fig.text(0.01, 0.5, text, ha='left', va='center',
88 fig.text(0.01, 0.5, text, ha='left', va='center',
89 size='20', weight='heavy', color='w')
89 size='20', weight='heavy', color='w')
90 fig.show()
90 fig.show()
91 figpause(1000)
91 figpause(1000)
92
92
93
93
94 class Throttle(object):
94 class Throttle(object):
95 '''
95 '''
96 Decorator that prevents a function from being called more than once every
96 Decorator that prevents a function from being called more than once every
97 time period.
97 time period.
98 To create a function that cannot be called more than once a minute, but
98 To create a function that cannot be called more than once a minute, but
99 will sleep until it can be called:
99 will sleep until it can be called:
100 @Throttle(minutes=1)
100 @Throttle(minutes=1)
101 def foo():
101 def foo():
102 pass
102 pass
103
103
104 for i in range(10):
104 for i in range(10):
105 foo()
105 foo()
106 print "This function has run %s times." % i
106 print "This function has run %s times." % i
107 '''
107 '''
108
108
109 def __init__(self, seconds=0, minutes=0, hours=0):
109 def __init__(self, seconds=0, minutes=0, hours=0):
110 self.throttle_period = datetime.timedelta(
110 self.throttle_period = datetime.timedelta(
111 seconds=seconds, minutes=minutes, hours=hours
111 seconds=seconds, minutes=minutes, hours=hours
112 )
112 )
113
113
114 self.time_of_last_call = datetime.datetime.min
114 self.time_of_last_call = datetime.datetime.min
115
115
116 def __call__(self, fn):
116 def __call__(self, fn):
117 @wraps(fn)
117 @wraps(fn)
118 def wrapper(*args, **kwargs):
118 def wrapper(*args, **kwargs):
119 coerce = kwargs.pop('coerce', None)
119 coerce = kwargs.pop('coerce', None)
120 if coerce:
120 if coerce:
121 self.time_of_last_call = datetime.datetime.now()
121 self.time_of_last_call = datetime.datetime.now()
122 return fn(*args, **kwargs)
122 return fn(*args, **kwargs)
123 else:
123 else:
124 now = datetime.datetime.now()
124 now = datetime.datetime.now()
125 time_since_last_call = now - self.time_of_last_call
125 time_since_last_call = now - self.time_of_last_call
126 time_left = self.throttle_period - time_since_last_call
126 time_left = self.throttle_period - time_since_last_call
127
127
128 if time_left > datetime.timedelta(seconds=0):
128 if time_left > datetime.timedelta(seconds=0):
129 return
129 return
130
130
131 self.time_of_last_call = datetime.datetime.now()
131 self.time_of_last_call = datetime.datetime.now()
132 return fn(*args, **kwargs)
132 return fn(*args, **kwargs)
133
133
134 return wrapper
134 return wrapper
135
135
136 def apply_throttle(value):
136 def apply_throttle(value):
137
137
138 @Throttle(seconds=value)
138 @Throttle(seconds=value)
139 def fnThrottled(fn):
139 def fnThrottled(fn):
140 fn()
140 fn()
141
141
142 return fnThrottled
142 return fnThrottled
143
143
144
144
145 @MPDecorator
145 @MPDecorator
146 class Plot(Operation):
146 class Plot(Operation):
147 """Base class for Schain plotting operations
147 """Base class for Schain plotting operations
148
148
149 This class should never be use directtly you must subclass a new operation,
149 This class should never be use directtly you must subclass a new operation,
150 children classes must be defined as follow:
150 children classes must be defined as follow:
151
151
152 ExamplePlot(Plot):
152 ExamplePlot(Plot):
153
153
154 CODE = 'code'
154 CODE = 'code'
155 colormap = 'jet'
155 colormap = 'jet'
156 plot_type = 'pcolor' # options are ('pcolor', 'pcolorbuffer', 'scatter', 'scatterbuffer')
156 plot_type = 'pcolor' # options are ('pcolor', 'pcolorbuffer', 'scatter', 'scatterbuffer')
157
157
158 def setup(self):
158 def setup(self):
159 pass
159 pass
160
160
161 def plot(self):
161 def plot(self):
162 pass
162 pass
163
163
164 """
164 """
165
165
166 CODE = 'Figure'
166 CODE = 'Figure'
167 colormap = 'jet'
167 colormap = 'jet'
168 bgcolor = 'white'
168 bgcolor = 'white'
169 buffering = True
169 buffering = True
170 __missing = 1E30
170 __missing = 1E30
171
171
172 __attrs__ = ['show', 'save', 'ymin', 'ymax', 'zmin', 'zmax', 'title',
172 __attrs__ = ['show', 'save', 'ymin', 'ymax', 'zmin', 'zmax', 'title',
173 'showprofile']
173 'showprofile']
174
174
175 def __init__(self):
175 def __init__(self):
176
176
177 Operation.__init__(self)
177 Operation.__init__(self)
178 self.isConfig = False
178 self.isConfig = False
179 self.isPlotConfig = False
179 self.isPlotConfig = False
180 self.save_time = 0
180 self.save_time = 0
181 self.sender_time = 0
181 self.sender_time = 0
182 self.data = None
182 self.data = None
183 self.firsttime = True
183 self.firsttime = True
184 self.sender_queue = deque(maxlen=10)
184 self.sender_queue = deque(maxlen=10)
185 self.plots_adjust = {'left': 0.125, 'right': 0.9, 'bottom': 0.15, 'top': 0.9, 'wspace': 0.2, 'hspace': 0.2}
185 self.plots_adjust = {'left': 0.125, 'right': 0.9, 'bottom': 0.15, 'top': 0.9, 'wspace': 0.2, 'hspace': 0.2}
186
186
187 def __fmtTime(self, x, pos):
187 def __fmtTime(self, x, pos):
188 '''
188 '''
189 '''
189 '''
190
190
191 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
191 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
192
192
193 def __setup(self, **kwargs):
193 def __setup(self, **kwargs):
194 '''
194 '''
195 Initialize variables
195 Initialize variables
196 '''
196 '''
197
197
198 self.figures = []
198 self.figures = []
199 self.axes = []
199 self.axes = []
200 self.cb_axes = []
200 self.cb_axes = []
201 self.localtime = kwargs.pop('localtime', True)
201 self.localtime = kwargs.pop('localtime', True)
202 self.show = kwargs.get('show', True)
202 self.show = kwargs.get('show', True)
203 self.save = kwargs.get('save', False)
203 self.save = kwargs.get('save', False)
204 self.save_period = kwargs.get('save_period', 0)
204 self.save_period = kwargs.get('save_period', 0)
205 self.colormap = kwargs.get('colormap', self.colormap)
205 self.colormap = kwargs.get('colormap', self.colormap)
206 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
206 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
207 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
207 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
208 self.colormaps = kwargs.get('colormaps', None)
208 self.colormaps = kwargs.get('colormaps', None)
209 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
209 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
210 self.showprofile = kwargs.get('showprofile', False)
210 self.showprofile = kwargs.get('showprofile', False)
211 self.title = kwargs.get('wintitle', self.CODE.upper())
211 self.title = kwargs.get('wintitle', self.CODE.upper())
212 self.cb_label = kwargs.get('cb_label', None)
212 self.cb_label = kwargs.get('cb_label', None)
213 self.cb_labels = kwargs.get('cb_labels', None)
213 self.cb_labels = kwargs.get('cb_labels', None)
214 self.labels = kwargs.get('labels', None)
214 self.labels = kwargs.get('labels', None)
215 self.xaxis = kwargs.get('xaxis', 'frequency')
215 self.xaxis = kwargs.get('xaxis', 'frequency')
216 self.zmin = kwargs.get('zmin', None)
216 self.zmin = kwargs.get('zmin', None)
217 self.zmax = kwargs.get('zmax', None)
217 self.zmax = kwargs.get('zmax', None)
218 self.zlimits = kwargs.get('zlimits', None)
218 self.zlimits = kwargs.get('zlimits', None)
219 self.xmin = kwargs.get('xmin', None)
219 self.xmin = kwargs.get('xmin', None)
220 self.xmax = kwargs.get('xmax', None)
220 self.xmax = kwargs.get('xmax', None)
221 self.xrange = kwargs.get('xrange', 12)
221 self.xrange = kwargs.get('xrange', 12)
222 self.xscale = kwargs.get('xscale', None)
222 self.xscale = kwargs.get('xscale', None)
223 self.ymin = kwargs.get('ymin', None)
223 self.ymin = kwargs.get('ymin', None)
224 self.ymax = kwargs.get('ymax', None)
224 self.ymax = kwargs.get('ymax', None)
225 self.yscale = kwargs.get('yscale', None)
225 self.yscale = kwargs.get('yscale', None)
226 self.xlabel = kwargs.get('xlabel', None)
226 self.xlabel = kwargs.get('xlabel', None)
227 self.attr_time = kwargs.get('attr_time', 'utctime')
227 self.attr_time = kwargs.get('attr_time', 'utctime')
228 self.attr_data = kwargs.get('attr_data', 'data_param')
228 self.attr_data = kwargs.get('attr_data', 'data_param')
229 self.decimation = kwargs.get('decimation', None)
229 self.decimation = kwargs.get('decimation', None)
230 self.oneFigure = kwargs.get('oneFigure', True)
230 self.oneFigure = kwargs.get('oneFigure', True)
231 self.width = kwargs.get('width', None)
231 self.width = kwargs.get('width', None)
232 self.height = kwargs.get('height', None)
232 self.height = kwargs.get('height', None)
233 self.colorbar = kwargs.get('colorbar', True)
233 self.colorbar = kwargs.get('colorbar', True)
234 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
234 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
235 self.channels = kwargs.get('channels', None)
235 self.channels = kwargs.get('channels', None)
236 self.titles = kwargs.get('titles', [])
236 self.titles = kwargs.get('titles', [])
237 self.polar = False
237 self.polar = False
238 self.type = kwargs.get('type', 'iq')
238 self.type = kwargs.get('type', 'iq')
239 self.grid = kwargs.get('grid', False)
239 self.grid = kwargs.get('grid', False)
240 self.pause = kwargs.get('pause', False)
240 self.pause = kwargs.get('pause', False)
241 self.save_code = kwargs.get('save_code', self.CODE)
241 self.save_code = kwargs.get('save_code', self.CODE)
242 self.throttle = kwargs.get('throttle', 0)
242 self.throttle = kwargs.get('throttle', 0)
243 self.exp_code = kwargs.get('exp_code', None)
243 self.exp_code = kwargs.get('exp_code', None)
244 self.server = kwargs.get('server', False)
244 self.server = kwargs.get('server', False)
245 self.sender_period = kwargs.get('sender_period', 60)
245 self.sender_period = kwargs.get('sender_period', 60)
246 self.tag = kwargs.get('tag', '')
246 self.tag = kwargs.get('tag', '')
247 self.height_index = kwargs.get('height_index', None)
247 self.height_index = kwargs.get('height_index', None)
248 self.__throttle_plot = apply_throttle(self.throttle)
248 self.__throttle_plot = apply_throttle(self.throttle)
249 code = self.attr_data if self.attr_data else self.CODE
249 code = self.attr_data if self.attr_data else self.CODE
250 self.data = PlotterData(self.CODE, self.exp_code, self.localtime)
250 self.data = PlotterData(self.CODE, self.exp_code, self.localtime)
251 self.ang_min = kwargs.get('ang_min', None)
251 self.ang_min = kwargs.get('ang_min', None)
252 self.ang_max = kwargs.get('ang_max', None)
252 self.ang_max = kwargs.get('ang_max', None)
253 self.mode = kwargs.get('mode', None)
253 self.mode = kwargs.get('mode', None)
254 self.snr_threshold = kwargs.get('snr_threshold', 0)
254 self.snr_threshold = kwargs.get('snr_threshold', 0)
255
255
256
256
257 if self.server:
257 if self.server:
258 if not self.server.startswith('tcp://'):
258 if not self.server.startswith('tcp://'):
259 self.server = 'tcp://{}'.format(self.server)
259 self.server = 'tcp://{}'.format(self.server)
260 log.success(
260 log.success(
261 'Sending to server: {}'.format(self.server),
261 'Sending to server: {}'.format(self.server),
262 self.name
262 self.name
263 )
263 )
264
264
265 if isinstance(self.attr_data, str):
265 if isinstance(self.attr_data, str):
266 self.attr_data = [self.attr_data]
266 self.attr_data = [self.attr_data]
267
267
268 def __setup_plot(self):
268 def __setup_plot(self):
269 '''
269 '''
270 Common setup for all figures, here figures and axes are created
270 Common setup for all figures, here figures and axes are created
271 '''
271 '''
272
272
273 self.setup()
273 self.setup()
274
274
275 self.time_label = 'LT' if self.localtime else 'UTC'
275 self.time_label = 'LT' if self.localtime else 'UTC'
276
276
277 if self.width is None:
277 if self.width is None:
278 self.width = 8
278 self.width = 8
279
279
280 self.figures = []
280 self.figures = []
281 self.axes = []
281 self.axes = []
282 self.cb_axes = []
282 self.cb_axes = []
283 self.pf_axes = []
283 self.pf_axes = []
284 self.cmaps = []
284 self.cmaps = []
285
285
286 size = '15%' if self.ncols == 1 else '30%'
286 size = '15%' if self.ncols == 1 else '30%'
287 pad = '4%' if self.ncols == 1 else '8%'
287 pad = '4%' if self.ncols == 1 else '8%'
288
288
289 if self.oneFigure:
289 if self.oneFigure:
290 if self.height is None:
290 if self.height is None:
291 self.height = 1.4 * self.nrows + 1
291 self.height = 1.4 * self.nrows + 1
292 fig = plt.figure(figsize=(self.width, self.height),
292 fig = plt.figure(figsize=(self.width, self.height),
293 edgecolor='k',
293 edgecolor='k',
294 facecolor='w')
294 facecolor='w')
295 self.figures.append(fig)
295 self.figures.append(fig)
296 for n in range(self.nplots):
296 for n in range(self.nplots):
297 ax = fig.add_subplot(self.nrows, self.ncols,
297 ax = fig.add_subplot(self.nrows, self.ncols,
298 n + 1, polar=self.polar)
298 n + 1, polar=self.polar)
299 ax.tick_params(labelsize=8)
299 ax.tick_params(labelsize=8)
300 ax.firsttime = True
300 ax.firsttime = True
301 ax.index = 0
301 ax.index = 0
302 ax.press = None
302 ax.press = None
303 self.axes.append(ax)
303 self.axes.append(ax)
304 if self.showprofile:
304 if self.showprofile:
305 cax = self.__add_axes(ax, size=size, pad=pad)
305 cax = self.__add_axes(ax, size=size, pad=pad)
306 cax.tick_params(labelsize=8)
306 cax.tick_params(labelsize=8)
307 self.pf_axes.append(cax)
307 self.pf_axes.append(cax)
308 else:
308 else:
309 if self.height is None:
309 if self.height is None:
310 self.height = 3
310 self.height = 3
311 for n in range(self.nplots):
311 for n in range(self.nplots):
312 fig = plt.figure(figsize=(self.width, self.height),
312 fig = plt.figure(figsize=(self.width, self.height),
313 edgecolor='k',
313 edgecolor='k',
314 facecolor='w')
314 facecolor='w')
315 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
315 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
316 ax.tick_params(labelsize=8)
316 ax.tick_params(labelsize=8)
317 ax.firsttime = True
317 ax.firsttime = True
318 ax.index = 0
318 ax.index = 0
319 ax.press = None
319 ax.press = None
320 self.figures.append(fig)
320 self.figures.append(fig)
321 self.axes.append(ax)
321 self.axes.append(ax)
322 if self.showprofile:
322 if self.showprofile:
323 cax = self.__add_axes(ax, size=size, pad=pad)
323 cax = self.__add_axes(ax, size=size, pad=pad)
324 cax.tick_params(labelsize=8)
324 cax.tick_params(labelsize=8)
325 self.pf_axes.append(cax)
325 self.pf_axes.append(cax)
326
326
327 for n in range(self.nrows):
327 for n in range(self.nrows):
328 if self.colormaps is not None:
328 if self.colormaps is not None:
329 cmap = plt.get_cmap(self.colormaps[n])
329 cmap = plt.get_cmap(self.colormaps[n])
330 else:
330 else:
331 cmap = plt.get_cmap(self.colormap)
331 cmap = plt.get_cmap(self.colormap)
332 cmap.set_bad(self.bgcolor, 1.)
332 cmap.set_bad(self.bgcolor, 1.)
333 self.cmaps.append(cmap)
333 self.cmaps.append(cmap)
334
334
335 def __add_axes(self, ax, size='30%', pad='8%'):
335 def __add_axes(self, ax, size='30%', pad='8%'):
336 '''
336 '''
337 Add new axes to the given figure
337 Add new axes to the given figure
338 '''
338 '''
339 divider = make_axes_locatable(ax)
339 divider = make_axes_locatable(ax)
340 nax = divider.new_horizontal(size=size, pad=pad)
340 nax = divider.new_horizontal(size=size, pad=pad)
341 ax.figure.add_axes(nax)
341 ax.figure.add_axes(nax)
342 return nax
342 return nax
343
343
344 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
344 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
345 '''
345 '''
346 Create a masked array for missing data
346 Create a masked array for missing data
347 '''
347 '''
348 if x_buffer.shape[0] < 2:
348 if x_buffer.shape[0] < 2:
349 return x_buffer, y_buffer, z_buffer
349 return x_buffer, y_buffer, z_buffer
350
350
351 deltas = x_buffer[1:] - x_buffer[0:-1]
351 deltas = x_buffer[1:] - x_buffer[0:-1]
352 x_median = numpy.median(deltas)
352 x_median = numpy.median(deltas)
353
353
354 index = numpy.where(deltas > 5 * x_median)
354 index = numpy.where(deltas > 5 * x_median)
355
355
356 if len(index[0]) != 0:
356 if len(index[0]) != 0:
357 z_buffer[::, index[0], ::] = self.__missing
357 z_buffer[::, index[0], ::] = self.__missing
358 z_buffer = numpy.ma.masked_inside(z_buffer,
358 z_buffer = numpy.ma.masked_inside(z_buffer,
359 0.99 * self.__missing,
359 0.99 * self.__missing,
360 1.01 * self.__missing)
360 1.01 * self.__missing)
361
361
362 return x_buffer, y_buffer, z_buffer
362 return x_buffer, y_buffer, z_buffer
363
363
364 def decimate(self):
364 def decimate(self):
365
365
366 # dx = int(len(self.x)/self.__MAXNUMX) + 1
366 # dx = int(len(self.x)/self.__MAXNUMX) + 1
367 dy = int(len(self.y) / self.decimation) + 1
367 dy = int(len(self.y) / self.decimation) + 1
368
368
369 # x = self.x[::dx]
369 # x = self.x[::dx]
370 x = self.x
370 x = self.x
371 y = self.y[::dy]
371 y = self.y[::dy]
372 z = self.z[::, ::, ::dy]
372 z = self.z[::, ::, ::dy]
373
373
374 return x, y, z
374 return x, y, z
375
375
376 def format(self):
376 def format(self):
377 '''
377 '''
378 Set min and max values, labels, ticks and titles
378 Set min and max values, labels, ticks and titles
379 '''
379 '''
380
380
381 for n, ax in enumerate(self.axes):
381 for n, ax in enumerate(self.axes):
382 if ax.firsttime:
382 if ax.firsttime:
383 if self.xaxis != 'time':
383 if self.xaxis != 'time':
384 xmin = self.xmin
384 xmin = self.xmin
385 xmax = self.xmax
385 xmax = self.xmax
386 else:
386 else:
387 xmin = self.tmin
387 xmin = self.tmin
388 xmax = self.tmin + self.xrange*60*60
388 xmax = self.tmin + self.xrange*60*60
389 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
389 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
390 ax.xaxis.set_major_locator(LinearLocator(9))
390 ax.xaxis.set_major_locator(LinearLocator(9))
391 ymin = self.ymin if self.ymin is not None else numpy.nanmin(self.y[numpy.isfinite(self.y)])
391 ymin = self.ymin if self.ymin is not None else numpy.nanmin(self.y[numpy.isfinite(self.y)])
392 ymax = self.ymax if self.ymax is not None else numpy.nanmax(self.y[numpy.isfinite(self.y)])
392 ymax = self.ymax if self.ymax is not None else numpy.nanmax(self.y[numpy.isfinite(self.y)])
393 ax.set_facecolor(self.bgcolor)
393 ax.set_facecolor(self.bgcolor)
394 if self.xscale:
394 if self.xscale:
395 ax.xaxis.set_major_formatter(FuncFormatter(
395 ax.xaxis.set_major_formatter(FuncFormatter(
396 lambda x, pos: '{0:g}'.format(x*self.xscale)))
396 lambda x, pos: '{0:g}'.format(x*self.xscale)))
397 if self.yscale:
397 if self.yscale:
398 ax.yaxis.set_major_formatter(FuncFormatter(
398 ax.yaxis.set_major_formatter(FuncFormatter(
399 lambda x, pos: '{0:g}'.format(x*self.yscale)))
399 lambda x, pos: '{0:g}'.format(x*self.yscale)))
400 if self.xlabel is not None:
400 if self.xlabel is not None:
401 ax.set_xlabel(self.xlabel)
401 ax.set_xlabel(self.xlabel)
402 if self.ylabel is not None:
402 if self.ylabel is not None:
403 ax.set_ylabel(self.ylabel)
403 ax.set_ylabel(self.ylabel)
404 if self.showprofile:
404 if self.showprofile:
405 self.pf_axes[n].set_ylim(ymin, ymax)
405 self.pf_axes[n].set_ylim(ymin, ymax)
406 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
406 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
407 self.pf_axes[n].set_xlabel('dB')
407 self.pf_axes[n].set_xlabel('dB')
408 self.pf_axes[n].grid(b=True, axis='x')
408 self.pf_axes[n].grid(b=True, axis='x')
409 [tick.set_visible(False)
409 [tick.set_visible(False)
410 for tick in self.pf_axes[n].get_yticklabels()]
410 for tick in self.pf_axes[n].get_yticklabels()]
411 if self.colorbar:
411 if self.colorbar:
412 ax.cbar = plt.colorbar(
412 ax.cbar = plt.colorbar(
413 ax.plt, ax=ax, fraction=0.05, pad=0.06, aspect=10)
413 ax.plt, ax=ax, fraction=0.05, pad=0.06, aspect=10)
414 ax.cbar.ax.tick_params(labelsize=8)
414 ax.cbar.ax.tick_params(labelsize=8)
415 ax.cbar.ax.press = None
415 ax.cbar.ax.press = None
416 if self.cb_label:
416 if self.cb_label:
417 ax.cbar.set_label(self.cb_label, size=8)
417 ax.cbar.set_label(self.cb_label, size=8)
418 elif self.cb_labels:
418 elif self.cb_labels:
419 ax.cbar.set_label(self.cb_labels[n], size=8)
419 ax.cbar.set_label(self.cb_labels[n], size=8)
420 else:
420 else:
421 ax.cbar = None
421 ax.cbar = None
422 ax.set_xlim(xmin, xmax)
422 ax.set_xlim(xmin, xmax)
423 ax.set_ylim(ymin, ymax)
423 ax.set_ylim(ymin, ymax)
424 ax.firsttime = False
424 ax.firsttime = False
425 if self.grid:
425 if self.grid:
426 ax.grid(True)
426 ax.grid(True)
427 if not self.polar:
427 if not self.polar:
428 ax.set_title('{} {} {}'.format(
428 ax.set_title('{} {} {}'.format(
429 self.titles[n],
429 self.titles[n],
430 self.getDateTime(self.data.max_time).strftime(
430 self.getDateTime(self.data.max_time).strftime(
431 '%Y-%m-%d %H:%M:%S'),
431 '%Y-%m-%d %H:%M:%S'),
432 self.time_label),
432 self.time_label),
433 size=8)
433 size=8)
434 else:
434 else:
435 #ax.set_title('{}'.format(self.titles[n]), size=8)
435 #ax.set_title('{}'.format(self.titles[n]), size=8)
436 ax.set_title('{} {} {}'.format(
436 ax.set_title('{} {} {}'.format(
437 self.titles[n],
437 self.titles[n],
438 self.getDateTime(self.data.max_time).strftime(
438 self.getDateTime(self.data.max_time).strftime(
439 '%Y-%m-%d %H:%M:%S'),
439 '%Y-%m-%d %H:%M:%S'),
440 self.time_label),
440 self.time_label),
441 size=8)
441 size=8)
442 ax.set_ylim(0, self.ymax)
442 ax.set_ylim(0, self.ymax)
443 ax.set_yticks(ax.get_yticks(), labels=ax.get_yticks(), color='white')
443 ax.set_yticks(ax.get_yticks(), labels=ax.get_yticks(), color='white')
444 ax.yaxis.labelpad = 28
444 ax.yaxis.labelpad = 28
445
445
446 if self.firsttime:
446 if self.firsttime:
447 for n, fig in enumerate(self.figures):
447 for n, fig in enumerate(self.figures):
448 fig.subplots_adjust(**self.plots_adjust)
448 fig.subplots_adjust(**self.plots_adjust)
449 self.firsttime = False
449 self.firsttime = False
450
450
451 def clear_figures(self):
451 def clear_figures(self):
452 '''
452 '''
453 Reset axes for redraw plots
453 Reset axes for redraw plots
454 '''
454 '''
455
455
456 for ax in self.axes+self.pf_axes+self.cb_axes:
456 for ax in self.axes+self.pf_axes+self.cb_axes:
457 ax.clear()
457 ax.clear()
458 ax.firsttime = True
458 ax.firsttime = True
459 if hasattr(ax, 'cbar') and ax.cbar:
459 if hasattr(ax, 'cbar') and ax.cbar:
460 ax.cbar.remove()
460 ax.cbar.remove()
461
461
462 def __plot(self):
462 def __plot(self):
463 '''
463 '''
464 Main function to plot, format and save figures
464 Main function to plot, format and save figures
465 '''
465 '''
466
466
467 self.plot()
467 self.plot()
468 self.format()
468 self.format()
469
469
470 for n, fig in enumerate(self.figures):
470 for n, fig in enumerate(self.figures):
471 if self.nrows == 0 or self.nplots == 0:
471 if self.nrows == 0 or self.nplots == 0:
472 log.warning('No data', self.name)
472 log.warning('No data', self.name)
473 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
473 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
474 fig.canvas.manager.set_window_title(self.CODE)
474 fig.canvas.manager.set_window_title(self.CODE)
475 continue
475 continue
476
476
477 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
477 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
478 self.getDateTime(self.data.max_time).strftime('%Y/%m/%d')))
478 self.getDateTime(self.data.max_time).strftime('%Y/%m/%d')))
479 fig.canvas.draw()
479 fig.canvas.draw()
480 if self.show:
480 if self.show:
481 fig.show()
481 fig.show()
482 figpause(0.01)
482 figpause(0.01)
483
483
484 if self.save:
484 if self.save:
485 if self.CODE=="PPI" or self.CODE=="RHI":
485 if self.CODE=="PPI" or self.CODE=="RHI":
486 self.save_figure(n,stitle =self.titles)
486 self.save_figure(n,stitle =self.titles)
487 else:
487 else:
488 self.save_figure(n)
488 self.save_figure(n)
489
489
490 if self.server:
490 if self.server:
491 self.send_to_server()
491 self.send_to_server()
492
492
493 def __update(self, dataOut, timestamp):
493 def __update(self, dataOut, timestamp):
494 '''
494 '''
495 '''
495 '''
496
496
497 metadata = {
497 metadata = {
498 'yrange': dataOut.heightList,
498 'yrange': dataOut.heightList,
499 'interval': dataOut.timeInterval,
499 'interval': dataOut.timeInterval,
500 'channels': dataOut.channelList
500 'channels': dataOut.channelList
501 }
501 }
502
502
503 data, meta = self.update(dataOut)
503 data, meta = self.update(dataOut)
504 metadata.update(meta)
504 metadata.update(meta)
505 self.data.update(data, timestamp, metadata)
505 self.data.update(data, timestamp, metadata)
506
506
507 def save_figure(self, n,stitle=None):
507 def save_figure(self, n,stitle=None):
508 '''
508 '''
509 '''
509 '''
510 if stitle is not None:
510 if stitle is not None:
511 s_string = re.sub(r"[^A-Z0-9.]","",str(stitle))
511 s_string = re.sub(r"[^A-Z0-9.]","",str(stitle))
512 new_string=s_string[:3]+"_"+s_string[4:6]+"_"+s_string[6:]
512 new_string=s_string[:3]+"_"+s_string[4:6]+"_"+s_string[6:]
513
513
514 if self.oneFigure:
514 if self.oneFigure:
515 if (self.data.max_time - self.save_time) <= self.save_period:
515 if (self.data.max_time - self.save_time) <= self.save_period:
516 return
516 return
517
517
518 self.save_time = self.data.max_time
518 self.save_time = self.data.max_time
519
519
520 fig = self.figures[n]
520 fig = self.figures[n]
521
521
522 if self.throttle == 0:
522 if self.throttle == 0:
523 if self.oneFigure:
523 if self.oneFigure:
524 if stitle is not None:
524 if stitle is not None:
525 figname = os.path.join(
525 figname = os.path.join(
526 self.save,
526 self.save,
527 self.save_code + '_' + new_string,
527 self.save_code + '_' + new_string,
528 '{}_{}_{}.png'.format(
528 '{}_{}_{}.png'.format(
529 self.save_code,
529 self.save_code,
530 new_string,
530 new_string,
531 self.getDateTime(self.data.max_time).strftime(
531 self.getDateTime(self.data.max_time).strftime(
532 '%Y%m%d_%H%M%S',
532 '%Y%m%d_%H%M%S',
533 ),
533 ),
534 )
534 )
535 )
535 )
536 else:
536 else:
537 figname = os.path.join(
537 figname = os.path.join(
538 self.save,
538 self.save,
539 self.save_code,
539 self.save_code,
540 '{}_{}.png'.format(
540 '{}_{}.png'.format(
541 self.save_code,
541 self.save_code,
542 self.getDateTime(self.data.max_time).strftime(
542 self.getDateTime(self.data.max_time).strftime(
543 '%Y%m%d_%H%M%S'
543 '%Y%m%d_%H%M%S'
544 ),
544 ),
545 )
545 )
546 )
546 )
547 else:
547 else:
548 figname = os.path.join(
548 figname = os.path.join(
549 self.save,
549 self.save,
550 self.save_code,
550 self.save_code,
551 '{}_ch{}_{}.png'.format(
551 '{}_ch{}_{}.png'.format(
552 self.save_code,n,
552 self.save_code,n,
553 self.getDateTime(self.data.max_time).strftime(
553 self.getDateTime(self.data.max_time).strftime(
554 '%Y%m%d_%H%M%S'
554 '%Y%m%d_%H%M%S'
555 ),
555 ),
556 )
556 )
557 )
557 )
558 log.log('Saving figure: {}'.format(figname), self.name)
558 log.log('Saving figure: {}'.format(figname), self.name)
559 if not os.path.isdir(os.path.dirname(figname)):
559 if not os.path.isdir(os.path.dirname(figname)):
560 os.makedirs(os.path.dirname(figname))
560 os.makedirs(os.path.dirname(figname))
561 fig.savefig(figname)
561 fig.savefig(figname)
562
562
563 figname = os.path.join(
563 figname = os.path.join(
564 self.save,
564 self.save,
565 '{}_{}.png'.format(
565 '{}_{}.png'.format(
566 self.save_code,
566 self.save_code,
567 self.getDateTime(self.data.min_time).strftime(
567 self.getDateTime(self.data.min_time).strftime(
568 '%Y%m%d'
568 '%Y%m%d'
569 ),
569 ),
570 )
570 )
571 )
571 )
572
572
573 log.log('Saving figure: {}'.format(figname), self.name)
573 log.log('Saving figure: {}'.format(figname), self.name)
574 if not os.path.isdir(os.path.dirname(figname)):
574 if not os.path.isdir(os.path.dirname(figname)):
575 os.makedirs(os.path.dirname(figname))
575 os.makedirs(os.path.dirname(figname))
576 fig.savefig(figname)
576 fig.savefig(figname)
577
577
578 def send_to_server(self):
578 def send_to_server(self):
579 '''
579 '''
580 '''
580 '''
581
581
582 if self.exp_code == None:
582 if self.exp_code == None:
583 log.warning('Missing `exp_code` skipping sending to server...')
583 log.warning('Missing `exp_code` skipping sending to server...')
584
584
585 last_time = self.data.max_time
585 last_time = self.data.max_time
586 interval = last_time - self.sender_time
586 interval = last_time - self.sender_time
587 if interval < self.sender_period:
587 if interval < self.sender_period:
588 return
588 return
589
589
590 self.sender_time = last_time
590 self.sender_time = last_time
591
591
592 attrs = ['titles', 'zmin', 'zmax', 'tag', 'ymin', 'ymax']
592 attrs = ['titles', 'zmin', 'zmax', 'tag', 'ymin', 'ymax']
593 for attr in attrs:
593 for attr in attrs:
594 value = getattr(self, attr)
594 value = getattr(self, attr)
595 if value:
595 if value:
596 if isinstance(value, (numpy.float32, numpy.float64)):
596 if isinstance(value, (numpy.float32, numpy.float64)):
597 value = round(float(value), 2)
597 value = round(float(value), 2)
598 self.data.meta[attr] = value
598 self.data.meta[attr] = value
599 if self.colormap == 'jet':
599 if self.colormap == 'jet' or self.colormap == 'sophy_w':
600 self.data.meta['colormap'] = 'Jet'
600 self.data.meta['colormap'] = 'Jet'
601 elif 'RdBu' in self.colormap:
601 elif 'sophy_v' in self.colormap:
602 self.data.meta['colormap'] = 'RdBu'
602 self.data.meta['colormap'] = 'RdBu'
603 else:
603 else:
604 self.data.meta['colormap'] = 'Viridis'
604 self.data.meta['colormap'] = 'Viridis'
605 self.data.meta['interval'] = int(interval)
605 self.data.meta['interval'] = int(interval)
606
606
607 self.sender_queue.append(last_time)
607 self.sender_queue.append(last_time)
608
608
609 while True:
609 while True:
610 try:
610 try:
611 tm = self.sender_queue.popleft()
611 tm = self.sender_queue.popleft()
612 except IndexError:
612 except IndexError:
613 break
613 break
614 msg = self.data.jsonify(tm, self.save_code, self.plot_type)
614 msg = self.data.jsonify(tm, self.save_code, self.plot_type, key='var')
615 self.socket.send_string(msg)
615 self.socket.send_string(msg)
616 socks = dict(self.poll.poll(2000))
616 socks = dict(self.poll.poll(2000))
617 if socks.get(self.socket) == zmq.POLLIN:
617 if socks.get(self.socket) == zmq.POLLIN:
618 reply = self.socket.recv_string()
618 reply = self.socket.recv_string()
619 if reply == 'ok':
619 if reply == 'ok':
620 log.log("Response from server ok", self.name)
620 log.log("Response from server ok", self.name)
621 time.sleep(0.1)
621 time.sleep(0.1)
622 continue
622 continue
623 else:
623 else:
624 log.warning(
624 log.warning(
625 "Malformed reply from server: {}".format(reply), self.name)
625 "Malformed reply from server: {}".format(reply), self.name)
626 else:
626 else:
627 log.warning(
627 log.warning(
628 "No response from server, retrying...", self.name)
628 "No response from server, retrying...", self.name)
629 self.sender_queue.appendleft(tm)
629 self.sender_queue.appendleft(tm)
630 self.socket.setsockopt(zmq.LINGER, 0)
630 self.socket.setsockopt(zmq.LINGER, 0)
631 self.socket.close()
631 self.socket.close()
632 self.poll.unregister(self.socket)
632 self.poll.unregister(self.socket)
633 self.socket = self.context.socket(zmq.REQ)
633 self.socket = self.context.socket(zmq.REQ)
634 self.socket.connect(self.server)
634 self.socket.connect(self.server)
635 self.poll.register(self.socket, zmq.POLLIN)
635 self.poll.register(self.socket, zmq.POLLIN)
636 break
636 break
637
637
638 def setup(self):
638 def setup(self):
639 '''
639 '''
640 This method should be implemented in the child class, the following
640 This method should be implemented in the child class, the following
641 attributes should be set:
641 attributes should be set:
642
642
643 self.nrows: number of rows
643 self.nrows: number of rows
644 self.ncols: number of cols
644 self.ncols: number of cols
645 self.nplots: number of plots (channels or pairs)
645 self.nplots: number of plots (channels or pairs)
646 self.ylabel: label for Y axes
646 self.ylabel: label for Y axes
647 self.titles: list of axes title
647 self.titles: list of axes title
648
648
649 '''
649 '''
650 raise NotImplementedError
650 raise NotImplementedError
651
651
652 def plot(self):
652 def plot(self):
653 '''
653 '''
654 Must be defined in the child class, the actual plotting method
654 Must be defined in the child class, the actual plotting method
655 '''
655 '''
656 raise NotImplementedError
656 raise NotImplementedError
657
657
658 def update(self, dataOut):
658 def update(self, dataOut):
659 '''
659 '''
660 Must be defined in the child class, update self.data with new data
660 Must be defined in the child class, update self.data with new data
661 '''
661 '''
662
662
663 data = {
663 data = {
664 self.CODE: getattr(dataOut, 'data_{}'.format(self.CODE))
664 self.CODE: getattr(dataOut, 'data_{}'.format(self.CODE))
665 }
665 }
666 meta = {}
666 meta = {}
667
667
668 return data, meta
668 return data, meta
669
669
670 def run(self, dataOut, **kwargs):
670 def run(self, dataOut, **kwargs):
671 '''
671 '''
672 Main plotting routine
672 Main plotting routine
673 '''
673 '''
674
674
675 if self.isConfig is False:
675 if self.isConfig is False:
676 self.__setup(**kwargs)
676 self.__setup(**kwargs)
677
677
678 if self.localtime:
678 if self.localtime:
679 self.getDateTime = datetime.datetime.fromtimestamp
679 self.getDateTime = datetime.datetime.fromtimestamp
680 else:
680 else:
681 self.getDateTime = datetime.datetime.utcfromtimestamp
681 self.getDateTime = datetime.datetime.utcfromtimestamp
682
682
683 self.data.setup()
683 self.data.setup()
684 self.isConfig = True
684 self.isConfig = True
685 if self.server:
685 if self.server:
686 self.context = zmq.Context()
686 self.context = zmq.Context()
687 self.socket = self.context.socket(zmq.REQ)
687 self.socket = self.context.socket(zmq.REQ)
688 self.socket.connect(self.server)
688 self.socket.connect(self.server)
689 self.poll = zmq.Poller()
689 self.poll = zmq.Poller()
690 self.poll.register(self.socket, zmq.POLLIN)
690 self.poll.register(self.socket, zmq.POLLIN)
691
691
692 tm = getattr(dataOut, self.attr_time)
692 tm = getattr(dataOut, self.attr_time)
693
693
694 if self.data and 'time' in self.xaxis and (tm - self.tmin) >= self.xrange*60*60:
694 if self.data and 'time' in self.xaxis and (tm - self.tmin) >= self.xrange*60*60:
695 self.save_time = tm
695 self.save_time = tm
696 self.__plot()
696 self.__plot()
697 self.tmin += self.xrange*60*60
697 self.tmin += self.xrange*60*60
698 self.data.setup()
698 self.data.setup()
699 self.clear_figures()
699 self.clear_figures()
700
700
701 self.__update(dataOut, tm)
701 self.__update(dataOut, tm)
702
702
703 if self.isPlotConfig is False:
703 if self.isPlotConfig is False:
704 self.__setup_plot()
704 self.__setup_plot()
705 self.isPlotConfig = True
705 self.isPlotConfig = True
706 if self.xaxis == 'time':
706 if self.xaxis == 'time':
707 dt = self.getDateTime(tm)
707 dt = self.getDateTime(tm)
708 if self.xmin is None:
708 if self.xmin is None:
709 self.tmin = tm
709 self.tmin = tm
710 self.xmin = dt.hour
710 self.xmin = dt.hour
711 minutes = (self.xmin-int(self.xmin)) * 60
711 minutes = (self.xmin-int(self.xmin)) * 60
712 seconds = (minutes - int(minutes)) * 60
712 seconds = (minutes - int(minutes)) * 60
713 self.tmin = (dt.replace(hour=int(self.xmin), minute=int(minutes), second=int(seconds)) -
713 self.tmin = (dt.replace(hour=int(self.xmin), minute=int(minutes), second=int(seconds)) -
714 datetime.datetime(1970, 1, 1)).total_seconds()
714 datetime.datetime(1970, 1, 1)).total_seconds()
715 if self.localtime:
715 if self.localtime:
716 self.tmin += time.timezone
716 self.tmin += time.timezone
717
717
718 if self.xmin is not None and self.xmax is not None:
718 if self.xmin is not None and self.xmax is not None:
719 self.xrange = self.xmax - self.xmin
719 self.xrange = self.xmax - self.xmin
720
720
721 if self.throttle == 0:
721 if self.throttle == 0:
722 self.__plot()
722 self.__plot()
723 else:
723 else:
724 self.__throttle_plot(self.__plot)#, coerce=coerce)
724 self.__throttle_plot(self.__plot)#, coerce=coerce)
725
725
726 def close(self):
726 def close(self):
727
727
728 if self.data and not self.data.flagNoData:
728 if self.data and not self.data.flagNoData:
729 self.save_time = 0
729 self.save_time = 0
730 self.__plot()
730 self.__plot()
731 if self.data and not self.data.flagNoData and self.pause:
731 if self.data and not self.data.flagNoData and self.pause:
732 figpause(10)
732 figpause(10)
@@ -1,512 +1,519
1 import os
1 import os
2 import datetime
2 import datetime
3 import numpy
3 import numpy
4 from mpl_toolkits.axisartist.grid_finder import FixedLocator, DictFormatter
4 from mpl_toolkits.axisartist.grid_finder import FixedLocator, DictFormatter
5
5
6 from schainpy.model.graphics.jroplot_base import Plot, plt
6 from schainpy.model.graphics.jroplot_base import Plot, plt
7 from schainpy.model.graphics.jroplot_spectra import SpectraPlot, RTIPlot, CoherencePlot, SpectraCutPlot
7 from schainpy.model.graphics.jroplot_spectra import SpectraPlot, RTIPlot, CoherencePlot, SpectraCutPlot
8 from schainpy.utils import log
8 from schainpy.utils import log
9 # libreria wradlib
9
10 #import wradlib as wrl
10 import wradlib.georef as georef
11
11
12 EARTH_RADIUS = 6.3710e3
12 EARTH_RADIUS = 6.3710e3
13
13
14
14
15 def ll2xy(lat1, lon1, lat2, lon2):
15 def ll2xy(lat1, lon1, lat2, lon2):
16
16
17 p = 0.017453292519943295
17 p = 0.017453292519943295
18 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
18 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
19 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
19 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
20 r = 12742 * numpy.arcsin(numpy.sqrt(a))
20 r = 12742 * numpy.arcsin(numpy.sqrt(a))
21 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
21 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
22 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
22 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
23 theta = -theta + numpy.pi/2
23 theta = -theta + numpy.pi/2
24 return r*numpy.cos(theta), r*numpy.sin(theta)
24 return r*numpy.cos(theta), r*numpy.sin(theta)
25
25
26
26
27 def km2deg(km):
27 def km2deg(km):
28 '''
28 '''
29 Convert distance in km to degrees
29 Convert distance in km to degrees
30 '''
30 '''
31
31
32 return numpy.rad2deg(km/EARTH_RADIUS)
32 return numpy.rad2deg(km/EARTH_RADIUS)
33
33
34
34
35
35
36 class SpectralMomentsPlot(SpectraPlot):
36 class SpectralMomentsPlot(SpectraPlot):
37 '''
37 '''
38 Plot for Spectral Moments
38 Plot for Spectral Moments
39 '''
39 '''
40 CODE = 'spc_moments'
40 CODE = 'spc_moments'
41 # colormap = 'jet'
41 # colormap = 'jet'
42 # plot_type = 'pcolor'
42 # plot_type = 'pcolor'
43
43
44 class DobleGaussianPlot(SpectraPlot):
44 class DobleGaussianPlot(SpectraPlot):
45 '''
45 '''
46 Plot for Double Gaussian Plot
46 Plot for Double Gaussian Plot
47 '''
47 '''
48 CODE = 'gaussian_fit'
48 CODE = 'gaussian_fit'
49 # colormap = 'jet'
49 # colormap = 'jet'
50 # plot_type = 'pcolor'
50 # plot_type = 'pcolor'
51
51
52 class DoubleGaussianSpectraCutPlot(SpectraCutPlot):
52 class DoubleGaussianSpectraCutPlot(SpectraCutPlot):
53 '''
53 '''
54 Plot SpectraCut with Double Gaussian Fit
54 Plot SpectraCut with Double Gaussian Fit
55 '''
55 '''
56 CODE = 'cut_gaussian_fit'
56 CODE = 'cut_gaussian_fit'
57
57
58 class SnrPlot(RTIPlot):
58 class SnrPlot(RTIPlot):
59 '''
59 '''
60 Plot for SNR Data
60 Plot for SNR Data
61 '''
61 '''
62
62
63 CODE = 'snr'
63 CODE = 'snr'
64 colormap = 'jet'
64 colormap = 'jet'
65
65
66 def update(self, dataOut):
66 def update(self, dataOut):
67
67
68 data = {
68 data = {
69 'snr': 10*numpy.log10(dataOut.data_snr)
69 'snr': 10*numpy.log10(dataOut.data_snr)
70 }
70 }
71
71
72 return data, {}
72 return data, {}
73
73
74 class DopplerPlot(RTIPlot):
74 class DopplerPlot(RTIPlot):
75 '''
75 '''
76 Plot for DOPPLER Data (1st moment)
76 Plot for DOPPLER Data (1st moment)
77 '''
77 '''
78
78
79 CODE = 'dop'
79 CODE = 'dop'
80 colormap = 'jet'
80 colormap = 'jet'
81
81
82 def update(self, dataOut):
82 def update(self, dataOut):
83
83
84 data = {
84 data = {
85 'dop': 10*numpy.log10(dataOut.data_dop)
85 'dop': 10*numpy.log10(dataOut.data_dop)
86 }
86 }
87
87
88 return data, {}
88 return data, {}
89
89
90 class PowerPlot(RTIPlot):
90 class PowerPlot(RTIPlot):
91 '''
91 '''
92 Plot for Power Data (0 moment)
92 Plot for Power Data (0 moment)
93 '''
93 '''
94
94
95 CODE = 'pow'
95 CODE = 'pow'
96 colormap = 'jet'
96 colormap = 'jet'
97
97
98 def update(self, dataOut):
98 def update(self, dataOut):
99 data = {
99 data = {
100 'pow': 10*numpy.log10(dataOut.data_pow/dataOut.normFactor)
100 'pow': 10*numpy.log10(dataOut.data_pow/dataOut.normFactor)
101 }
101 }
102 return data, {}
102 return data, {}
103
103
104 class SpectralWidthPlot(RTIPlot):
104 class SpectralWidthPlot(RTIPlot):
105 '''
105 '''
106 Plot for Spectral Width Data (2nd moment)
106 Plot for Spectral Width Data (2nd moment)
107 '''
107 '''
108
108
109 CODE = 'width'
109 CODE = 'width'
110 colormap = 'jet'
110 colormap = 'jet'
111
111
112 def update(self, dataOut):
112 def update(self, dataOut):
113
113
114 data = {
114 data = {
115 'width': dataOut.data_width
115 'width': dataOut.data_width
116 }
116 }
117
117
118 return data, {}
118 return data, {}
119
119
120 class SkyMapPlot(Plot):
120 class SkyMapPlot(Plot):
121 '''
121 '''
122 Plot for meteors detection data
122 Plot for meteors detection data
123 '''
123 '''
124
124
125 CODE = 'param'
125 CODE = 'param'
126
126
127 def setup(self):
127 def setup(self):
128
128
129 self.ncols = 1
129 self.ncols = 1
130 self.nrows = 1
130 self.nrows = 1
131 self.width = 7.2
131 self.width = 7.2
132 self.height = 7.2
132 self.height = 7.2
133 self.nplots = 1
133 self.nplots = 1
134 self.xlabel = 'Zonal Zenith Angle (deg)'
134 self.xlabel = 'Zonal Zenith Angle (deg)'
135 self.ylabel = 'Meridional Zenith Angle (deg)'
135 self.ylabel = 'Meridional Zenith Angle (deg)'
136 self.polar = True
136 self.polar = True
137 self.ymin = -180
137 self.ymin = -180
138 self.ymax = 180
138 self.ymax = 180
139 self.colorbar = False
139 self.colorbar = False
140
140
141 def plot(self):
141 def plot(self):
142
142
143 arrayParameters = numpy.concatenate(self.data['param'])
143 arrayParameters = numpy.concatenate(self.data['param'])
144 error = arrayParameters[:, -1]
144 error = arrayParameters[:, -1]
145 indValid = numpy.where(error == 0)[0]
145 indValid = numpy.where(error == 0)[0]
146 finalMeteor = arrayParameters[indValid, :]
146 finalMeteor = arrayParameters[indValid, :]
147 finalAzimuth = finalMeteor[:, 3]
147 finalAzimuth = finalMeteor[:, 3]
148 finalZenith = finalMeteor[:, 4]
148 finalZenith = finalMeteor[:, 4]
149
149
150 x = finalAzimuth * numpy.pi / 180
150 x = finalAzimuth * numpy.pi / 180
151 y = finalZenith
151 y = finalZenith
152
152
153 ax = self.axes[0]
153 ax = self.axes[0]
154
154
155 if ax.firsttime:
155 if ax.firsttime:
156 ax.plot = ax.plot(x, y, 'bo', markersize=5)[0]
156 ax.plot = ax.plot(x, y, 'bo', markersize=5)[0]
157 else:
157 else:
158 ax.plot.set_data(x, y)
158 ax.plot.set_data(x, y)
159
159
160 dt1 = self.getDateTime(self.data.min_time).strftime('%y/%m/%d %H:%M:%S')
160 dt1 = self.getDateTime(self.data.min_time).strftime('%y/%m/%d %H:%M:%S')
161 dt2 = self.getDateTime(self.data.max_time).strftime('%y/%m/%d %H:%M:%S')
161 dt2 = self.getDateTime(self.data.max_time).strftime('%y/%m/%d %H:%M:%S')
162 title = 'Meteor Detection Sky Map\n %s - %s \n Number of events: %5.0f\n' % (dt1,
162 title = 'Meteor Detection Sky Map\n %s - %s \n Number of events: %5.0f\n' % (dt1,
163 dt2,
163 dt2,
164 len(x))
164 len(x))
165 self.titles[0] = title
165 self.titles[0] = title
166
166
167
167
168 class GenericRTIPlot(Plot):
168 class GenericRTIPlot(Plot):
169 '''
169 '''
170 Plot for data_xxxx object
170 Plot for data_xxxx object
171 '''
171 '''
172
172
173 CODE = 'param'
173 CODE = 'param'
174 colormap = 'viridis'
174 colormap = 'viridis'
175 plot_type = 'pcolorbuffer'
175 plot_type = 'pcolorbuffer'
176
176
177 def setup(self):
177 def setup(self):
178 self.xaxis = 'time'
178 self.xaxis = 'time'
179 self.ncols = 1
179 self.ncols = 1
180 self.nrows = self.data.shape('param')[0]
180 self.nrows = self.data.shape('param')[0]
181 self.nplots = self.nrows
181 self.nplots = self.nrows
182 self.plots_adjust.update({'hspace':0.8, 'left': 0.1, 'bottom': 0.08, 'right':0.95, 'top': 0.95})
182 self.plots_adjust.update({'hspace':0.8, 'left': 0.1, 'bottom': 0.08, 'right':0.95, 'top': 0.95})
183
183
184 if not self.xlabel:
184 if not self.xlabel:
185 self.xlabel = 'Time'
185 self.xlabel = 'Time'
186
186
187 self.ylabel = 'Range [km]'
187 self.ylabel = 'Range [km]'
188 if not self.titles:
188 if not self.titles:
189 self.titles = ['Param {}'.format(x) for x in range(self.nrows)]
189 self.titles = ['Param {}'.format(x) for x in range(self.nrows)]
190
190
191 def update(self, dataOut):
191 def update(self, dataOut):
192
192
193 data = {
193 data = {
194 'param' : numpy.concatenate([getattr(dataOut, attr) for attr in self.attr_data], axis=0)
194 'param' : numpy.concatenate([getattr(dataOut, attr) for attr in self.attr_data], axis=0)
195 }
195 }
196
196
197 meta = {}
197 meta = {}
198
198
199 return data, meta
199 return data, meta
200
200
201 def plot(self):
201 def plot(self):
202 # self.data.normalize_heights()
202 # self.data.normalize_heights()
203 self.x = self.data.times
203 self.x = self.data.times
204 self.y = self.data.yrange
204 self.y = self.data.yrange
205 self.z = self.data['param']
205 self.z = self.data['param']
206 self.z = 10*numpy.log10(self.z)
206 self.z = 10*numpy.log10(self.z)
207 self.z = numpy.ma.masked_invalid(self.z)
207 self.z = numpy.ma.masked_invalid(self.z)
208
208
209 if self.decimation is None:
209 if self.decimation is None:
210 x, y, z = self.fill_gaps(self.x, self.y, self.z)
210 x, y, z = self.fill_gaps(self.x, self.y, self.z)
211 else:
211 else:
212 x, y, z = self.fill_gaps(*self.decimate())
212 x, y, z = self.fill_gaps(*self.decimate())
213
213
214 for n, ax in enumerate(self.axes):
214 for n, ax in enumerate(self.axes):
215
215
216 self.zmax = self.zmax if self.zmax is not None else numpy.max(
216 self.zmax = self.zmax if self.zmax is not None else numpy.max(
217 self.z[n])
217 self.z[n])
218 self.zmin = self.zmin if self.zmin is not None else numpy.min(
218 self.zmin = self.zmin if self.zmin is not None else numpy.min(
219 self.z[n])
219 self.z[n])
220
220
221 if ax.firsttime:
221 if ax.firsttime:
222 if self.zlimits is not None:
222 if self.zlimits is not None:
223 self.zmin, self.zmax = self.zlimits[n]
223 self.zmin, self.zmax = self.zlimits[n]
224
224
225 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
225 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
226 vmin=self.zmin,
226 vmin=self.zmin,
227 vmax=self.zmax,
227 vmax=self.zmax,
228 cmap=self.cmaps[n]
228 cmap=self.cmaps[n]
229 )
229 )
230 else:
230 else:
231 if self.zlimits is not None:
231 if self.zlimits is not None:
232 self.zmin, self.zmax = self.zlimits[n]
232 self.zmin, self.zmax = self.zlimits[n]
233 ax.collections.remove(ax.collections[0])
233 ax.collections.remove(ax.collections[0])
234 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
234 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
235 vmin=self.zmin,
235 vmin=self.zmin,
236 vmax=self.zmax,
236 vmax=self.zmax,
237 cmap=self.cmaps[n]
237 cmap=self.cmaps[n]
238 )
238 )
239
239
240
240
241 class PolarMapPlot(Plot):
241 class PolarMapPlot(Plot):
242 '''
242 '''
243 Plot for weather radar
243 Plot for weather radar
244 '''
244 '''
245
245
246 CODE = 'param'
246 CODE = 'param'
247 colormap = 'seismic'
247 colormap = 'seismic'
248
248
249 def setup(self):
249 def setup(self):
250 self.ncols = 1
250 self.ncols = 1
251 self.nrows = 1
251 self.nrows = 1
252 self.width = 9
252 self.width = 9
253 self.height = 8
253 self.height = 8
254 self.mode = self.data.meta['mode']
254 self.mode = self.data.meta['mode']
255 if self.channels is not None:
255 if self.channels is not None:
256 self.nplots = len(self.channels)
256 self.nplots = len(self.channels)
257 self.nrows = len(self.channels)
257 self.nrows = len(self.channels)
258 else:
258 else:
259 self.nplots = self.data.shape(self.CODE)[0]
259 self.nplots = self.data.shape(self.CODE)[0]
260 self.nrows = self.nplots
260 self.nrows = self.nplots
261 self.channels = list(range(self.nplots))
261 self.channels = list(range(self.nplots))
262 if self.mode == 'E':
262 if self.mode == 'E':
263 self.xlabel = 'Longitude'
263 self.xlabel = 'Longitude'
264 self.ylabel = 'Latitude'
264 self.ylabel = 'Latitude'
265 else:
265 else:
266 self.xlabel = 'Range (km)'
266 self.xlabel = 'Range (km)'
267 self.ylabel = 'Height (km)'
267 self.ylabel = 'Height (km)'
268 self.bgcolor = 'white'
268 self.bgcolor = 'white'
269 self.cb_labels = self.data.meta['units']
269 self.cb_labels = self.data.meta['units']
270 self.lat = self.data.meta['latitude']
270 self.lat = self.data.meta['latitude']
271 self.lon = self.data.meta['longitude']
271 self.lon = self.data.meta['longitude']
272 self.xmin, self.xmax = float(
272 self.xmin, self.xmax = float(
273 km2deg(self.xmin) + self.lon), float(km2deg(self.xmax) + self.lon)
273 km2deg(self.xmin) + self.lon), float(km2deg(self.xmax) + self.lon)
274 self.ymin, self.ymax = float(
274 self.ymin, self.ymax = float(
275 km2deg(self.ymin) + self.lat), float(km2deg(self.ymax) + self.lat)
275 km2deg(self.ymin) + self.lat), float(km2deg(self.ymax) + self.lat)
276 # self.polar = True
276 # self.polar = True
277
277
278 def plot(self):
278 def plot(self):
279
279
280 for n, ax in enumerate(self.axes):
280 for n, ax in enumerate(self.axes):
281 data = self.data['param'][self.channels[n]]
281 data = self.data['param'][self.channels[n]]
282
282
283 zeniths = numpy.linspace(
283 zeniths = numpy.linspace(
284 0, self.data.meta['max_range'], data.shape[1])
284 0, self.data.meta['max_range'], data.shape[1])
285 if self.mode == 'E':
285 if self.mode == 'E':
286 azimuths = -numpy.radians(self.data.yrange)+numpy.pi/2
286 azimuths = -numpy.radians(self.data.yrange)+numpy.pi/2
287 r, theta = numpy.meshgrid(zeniths, azimuths)
287 r, theta = numpy.meshgrid(zeniths, azimuths)
288 x, y = r*numpy.cos(theta)*numpy.cos(numpy.radians(self.data.meta['elevation'])), r*numpy.sin(
288 x, y = r*numpy.cos(theta)*numpy.cos(numpy.radians(self.data.meta['elevation'])), r*numpy.sin(
289 theta)*numpy.cos(numpy.radians(self.data.meta['elevation']))
289 theta)*numpy.cos(numpy.radians(self.data.meta['elevation']))
290 x = km2deg(x) + self.lon
290 x = km2deg(x) + self.lon
291 y = km2deg(y) + self.lat
291 y = km2deg(y) + self.lat
292 else:
292 else:
293 azimuths = numpy.radians(self.data.yrange)
293 azimuths = numpy.radians(self.data.yrange)
294 r, theta = numpy.meshgrid(zeniths, azimuths)
294 r, theta = numpy.meshgrid(zeniths, azimuths)
295 x, y = r*numpy.cos(theta), r*numpy.sin(theta)
295 x, y = r*numpy.cos(theta), r*numpy.sin(theta)
296 self.y = zeniths
296 self.y = zeniths
297
297
298 if ax.firsttime:
298 if ax.firsttime:
299 if self.zlimits is not None:
299 if self.zlimits is not None:
300 self.zmin, self.zmax = self.zlimits[n]
300 self.zmin, self.zmax = self.zlimits[n]
301 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
301 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
302 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
302 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
303 vmin=self.zmin,
303 vmin=self.zmin,
304 vmax=self.zmax,
304 vmax=self.zmax,
305 cmap=self.cmaps[n])
305 cmap=self.cmaps[n])
306 else:
306 else:
307 if self.zlimits is not None:
307 if self.zlimits is not None:
308 self.zmin, self.zmax = self.zlimits[n]
308 self.zmin, self.zmax = self.zlimits[n]
309 ax.collections.remove(ax.collections[0])
309 ax.collections.remove(ax.collections[0])
310 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
310 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
311 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
311 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
312 vmin=self.zmin,
312 vmin=self.zmin,
313 vmax=self.zmax,
313 vmax=self.zmax,
314 cmap=self.cmaps[n])
314 cmap=self.cmaps[n])
315
315
316 if self.mode == 'A':
316 if self.mode == 'A':
317 continue
317 continue
318
318
319 # plot district names
319 # plot district names
320 f = open('/data/workspace/schain_scripts/distrito.csv')
320 f = open('/data/workspace/schain_scripts/distrito.csv')
321 for line in f:
321 for line in f:
322 label, lon, lat = [s.strip() for s in line.split(',') if s]
322 label, lon, lat = [s.strip() for s in line.split(',') if s]
323 lat = float(lat)
323 lat = float(lat)
324 lon = float(lon)
324 lon = float(lon)
325 # ax.plot(lon, lat, '.b', ms=2)
325 # ax.plot(lon, lat, '.b', ms=2)
326 ax.text(lon, lat, label.decode('utf8'), ha='center',
326 ax.text(lon, lat, label.decode('utf8'), ha='center',
327 va='bottom', size='8', color='black')
327 va='bottom', size='8', color='black')
328
328
329 # plot limites
329 # plot limites
330 limites = []
330 limites = []
331 tmp = []
331 tmp = []
332 for line in open('/data/workspace/schain_scripts/lima.csv'):
332 for line in open('/data/workspace/schain_scripts/lima.csv'):
333 if '#' in line:
333 if '#' in line:
334 if tmp:
334 if tmp:
335 limites.append(tmp)
335 limites.append(tmp)
336 tmp = []
336 tmp = []
337 continue
337 continue
338 values = line.strip().split(',')
338 values = line.strip().split(',')
339 tmp.append((float(values[0]), float(values[1])))
339 tmp.append((float(values[0]), float(values[1])))
340 for points in limites:
340 for points in limites:
341 ax.add_patch(
341 ax.add_patch(
342 Polygon(points, ec='k', fc='none', ls='--', lw=0.5))
342 Polygon(points, ec='k', fc='none', ls='--', lw=0.5))
343
343
344 # plot Cuencas
344 # plot Cuencas
345 for cuenca in ('rimac', 'lurin', 'mala', 'chillon', 'chilca', 'chancay-huaral'):
345 for cuenca in ('rimac', 'lurin', 'mala', 'chillon', 'chilca', 'chancay-huaral'):
346 f = open('/data/workspace/schain_scripts/{}.csv'.format(cuenca))
346 f = open('/data/workspace/schain_scripts/{}.csv'.format(cuenca))
347 values = [line.strip().split(',') for line in f]
347 values = [line.strip().split(',') for line in f]
348 points = [(float(s[0]), float(s[1])) for s in values]
348 points = [(float(s[0]), float(s[1])) for s in values]
349 ax.add_patch(Polygon(points, ec='b', fc='none'))
349 ax.add_patch(Polygon(points, ec='b', fc='none'))
350
350
351 # plot grid
351 # plot grid
352 for r in (15, 30, 45, 60):
352 for r in (15, 30, 45, 60):
353 ax.add_artist(plt.Circle((self.lon, self.lat),
353 ax.add_artist(plt.Circle((self.lon, self.lat),
354 km2deg(r), color='0.6', fill=False, lw=0.2))
354 km2deg(r), color='0.6', fill=False, lw=0.2))
355 ax.text(
355 ax.text(
356 self.lon + (km2deg(r))*numpy.cos(60*numpy.pi/180),
356 self.lon + (km2deg(r))*numpy.cos(60*numpy.pi/180),
357 self.lat + (km2deg(r))*numpy.sin(60*numpy.pi/180),
357 self.lat + (km2deg(r))*numpy.sin(60*numpy.pi/180),
358 '{}km'.format(r),
358 '{}km'.format(r),
359 ha='center', va='bottom', size='8', color='0.6', weight='heavy')
359 ha='center', va='bottom', size='8', color='0.6', weight='heavy')
360
360
361 if self.mode == 'E':
361 if self.mode == 'E':
362 title = 'El={}$^\circ$'.format(self.data.meta['elevation'])
362 title = 'El={}$^\circ$'.format(self.data.meta['elevation'])
363 label = 'E{:02d}'.format(int(self.data.meta['elevation']))
363 label = 'E{:02d}'.format(int(self.data.meta['elevation']))
364 else:
364 else:
365 title = 'Az={}$^\circ$'.format(self.data.meta['azimuth'])
365 title = 'Az={}$^\circ$'.format(self.data.meta['azimuth'])
366 label = 'A{:02d}'.format(int(self.data.meta['azimuth']))
366 label = 'A{:02d}'.format(int(self.data.meta['azimuth']))
367
367
368 self.save_labels = ['{}-{}'.format(lbl, label) for lbl in self.labels]
368 self.save_labels = ['{}-{}'.format(lbl, label) for lbl in self.labels]
369 self.titles = ['{} {}'.format(
369 self.titles = ['{} {}'.format(
370 self.data.parameters[x], title) for x in self.channels]
370 self.data.parameters[x], title) for x in self.channels]
371
371
372 class WeatherParamsPlot(Plot):
372 class WeatherParamsPlot(Plot):
373 #CODE = 'RHI'
373 #CODE = 'RHI'
374 #plot_name = 'RHI'
374 #plot_name = 'RHI'
375 #plot_type = 'rhistyle'
375 plot_type = 'scattermap'
376 buffering = False
376 buffering = False
377
377
378 def setup(self):
378 def setup(self):
379
379
380 self.ncols = 1
380 self.ncols = 1
381 self.nrows = 1
381 self.nrows = 1
382 self.nplots= 1
382 self.nplots= 1
383 self.ylabel= 'Range [km]'
383 self.ylabel= 'Range [km]'
384 self.xlabel= 'Range [km]'
384 self.xlabel= 'Range [km]'
385 self.polar = True
385 self.polar = True
386 self.grid = True
386 self.grid = True
387 if self.channels is not None:
387 if self.channels is not None:
388 self.nplots = len(self.channels)
388 self.nplots = len(self.channels)
389 self.nrows = len(self.channels)
389 self.nrows = len(self.channels)
390 else:
390 else:
391 self.nplots = self.data.shape(self.CODE)[0]
391 self.nplots = self.data.shape(self.CODE)[0]
392 self.nrows = self.nplots
392 self.nrows = self.nplots
393 self.channels = list(range(self.nplots))
393 self.channels = list(range(self.nplots))
394
394
395 self.colorbar=True
395 self.colorbar=True
396 self.width =8
396 self.width =8
397 self.height =8
397 self.height =8
398 self.ini =0
398 self.ini =0
399 self.len_azi =0
399 self.len_azi =0
400 self.buffer_ini = None
400 self.buffer_ini = None
401 self.buffer_ele = None
401 self.buffer_ele = None
402 self.plots_adjust.update({'wspace': 0.4, 'hspace':0.4, 'left': 0.1, 'right': 0.9, 'bottom': 0.08})
402 self.plots_adjust.update({'wspace': 0.4, 'hspace':0.4, 'left': 0.1, 'right': 0.9, 'bottom': 0.08})
403 self.flag =0
403 self.flag =0
404 self.indicador= 0
404 self.indicador= 0
405 self.last_data_ele = None
405 self.last_data_ele = None
406 self.val_mean = None
406 self.val_mean = None
407
407
408 def update(self, dataOut):
408 def update(self, dataOut):
409
409
410 data = {}
410 data = {}
411 meta = {}
411 meta = {}
412 if hasattr(dataOut, 'dataPP_POWER'):
412 if hasattr(dataOut, 'dataPP_POWER'):
413 factor = 1
413 factor = 1
414 if hasattr(dataOut, 'nFFTPoints'):
414 if hasattr(dataOut, 'nFFTPoints'):
415 factor = dataOut.normFactor
415 factor = dataOut.normFactor
416
416
417 mask = dataOut.data_snr<self.snr_threshold
417 mask = dataOut.data_snr<self.snr_threshold
418
418
419 if 'pow' in self.attr_data[0].lower():
419 if 'pow' in self.attr_data[0].lower():
420 # data['data'] = 10*numpy.log10(getattr(dataOut, self.attr_data[0])/(factor))
420 # data['data'] = 10*numpy.log10(getattr(dataOut, self.attr_data[0])/(factor))
421 data['data'] = numpy.ma.masked_array(10*numpy.log10(getattr(dataOut, self.attr_data[0])/(factor)), mask=mask)
421 tmp = numpy.ma.masked_array(10*numpy.log10(getattr(dataOut, self.attr_data[0])/(factor)), mask=mask)
422 else:
422 else:
423 data['data'] = numpy.ma.masked_array(getattr(dataOut, self.attr_data[0]), mask=mask)
423 tmp = numpy.ma.masked_array(getattr(dataOut, self.attr_data[0]), mask=mask)
424 # data['data'] = getattr(dataOut, self.attr_data[0])
424 # tmp = getattr(dataOut, self.attr_data[0])
425
426 r = dataOut.heightList
427 delta_height = r[1]-r[0]
428 valid = numpy.where(r>=0)[0]
429 data['r'] = numpy.arange(len(valid))*delta_height
430
431 try:
432 data['data'] = tmp[self.channels[0]][:,valid]
433 except:
434 data['data'] = tmp[0][:,valid]
425
435
426 if dataOut.mode_op == 'PPI':
436 if dataOut.mode_op == 'PPI':
427 self.CODE = 'PPI'
437 self.CODE = 'PPI'
428 self.title = self.CODE
438 self.title = self.CODE
429 elif dataOut.mode_op == 'RHI':
439 elif dataOut.mode_op == 'RHI':
430 self.CODE = 'RHI'
440 self.CODE = 'RHI'
431 self.title = self.CODE
441 self.title = self.CODE
432
442
433 data['azi'] = dataOut.data_azi
443 data['azi'] = dataOut.data_azi
434 data['ele'] = dataOut.data_ele
444 data['ele'] = dataOut.data_ele
435 data['mode_op'] = dataOut.mode_op
445 data['mode_op'] = dataOut.mode_op
446 var = data['data'].flatten()
447 r = numpy.tile(data['r'], data['data'].shape[0]).reshape(data['data'].shape)*1000
448 lla = georef.spherical_to_proj(r, data['azi'], data['ele'], (-75.295893, -12.040436, 3379.2147))
449 meta['lat'] = lla[:,:,1].flatten()[var.mask==False]
450 meta['lon'] = lla[:,:,0].flatten()[var.mask==False]
451 data['var'] = numpy.array([var[var.mask==False]])
436
452
437 return data, meta
453 return data, meta
438
454
439 def plot(self):
455 def plot(self):
440 data = self.data[-1]
456 data = self.data[-1]
441 r = self.data.yrange
457 z = data['data']
442 delta_height = r[1]-r[0]
458 r = data['r']
443 r_mask = numpy.where(r>=0)[0]
444 r = numpy.arange(len(r_mask))*delta_height
445 self.y = 2*r
446
447 try:
448 z = data['data'][self.channels[0]][:,r_mask]
449 except:
450 z = data['data'][0][:,r_mask]
451
452 self.titles = []
459 self.titles = []
453
460
454 self.ymax = self.ymax if self.ymax else numpy.nanmax(r)
461 self.ymax = self.ymax if self.ymax else numpy.nanmax(r)
455 self.ymin = self.ymin if self.ymin else numpy.nanmin(r)
462 self.ymin = self.ymin if self.ymin else numpy.nanmin(r)
456 self.zmax = self.zmax if self.zmax else numpy.nanmax(z)
463 self.zmax = self.zmax if self.zmax else numpy.nanmax(z)
457 self.zmin = self.zmin if self.zmin is not None else numpy.nanmin(z)
464 self.zmin = self.zmin if self.zmin is not None else numpy.nanmin(z)
458
465
459 if data['mode_op'] == 'RHI':
466 if data['mode_op'] == 'RHI':
460 try:
467 try:
461 if self.data['mode_op'][-2] == 'PPI':
468 if self.data['mode_op'][-2] == 'PPI':
462 self.ang_min = None
469 self.ang_min = None
463 self.ang_max = None
470 self.ang_max = None
464 except:
471 except:
465 pass
472 pass
466 self.ang_min = self.ang_min if self.ang_min else 0
473 self.ang_min = self.ang_min if self.ang_min else 0
467 self.ang_max = self.ang_max if self.ang_max else 90
474 self.ang_max = self.ang_max if self.ang_max else 90
468 r, theta = numpy.meshgrid(r, numpy.radians(data['ele']) )
475 r, theta = numpy.meshgrid(r, numpy.radians(data['ele']) )
469 elif data['mode_op'] == 'PPI':
476 elif data['mode_op'] == 'PPI':
470 try:
477 try:
471 if self.data['mode_op'][-2] == 'RHI':
478 if self.data['mode_op'][-2] == 'RHI':
472 self.ang_min = None
479 self.ang_min = None
473 self.ang_max = None
480 self.ang_max = None
474 except:
481 except:
475 pass
482 pass
476 self.ang_min = self.ang_min if self.ang_min else 0
483 self.ang_min = self.ang_min if self.ang_min else 0
477 self.ang_max = self.ang_max if self.ang_max else 360
484 self.ang_max = self.ang_max if self.ang_max else 360
478 r, theta = numpy.meshgrid(r, numpy.radians(data['azi']) )
485 r, theta = numpy.meshgrid(r, numpy.radians(data['azi']) )
479
486
480 self.clear_figures()
487 self.clear_figures()
481
488
482 for i,ax in enumerate(self.axes):
489 for i,ax in enumerate(self.axes):
483
490
484 if ax.firsttime:
491 if ax.firsttime:
485 ax.set_xlim(numpy.radians(self.ang_min),numpy.radians(self.ang_max))
492 ax.set_xlim(numpy.radians(self.ang_min),numpy.radians(self.ang_max))
486 ax.plt = ax.pcolormesh(theta, r, z, cmap=self.colormap, vmin=self.zmin, vmax=self.zmax)
493 ax.plt = ax.pcolormesh(theta, r, z, cmap=self.colormap, vmin=self.zmin, vmax=self.zmax)
487 if data['mode_op'] == 'PPI':
494 if data['mode_op'] == 'PPI':
488 ax.set_theta_direction(-1)
495 ax.set_theta_direction(-1)
489 ax.set_theta_offset(numpy.pi/2)
496 ax.set_theta_offset(numpy.pi/2)
490
497
491 else:
498 else:
492 ax.set_xlim(numpy.radians(self.ang_min),numpy.radians(self.ang_max))
499 ax.set_xlim(numpy.radians(self.ang_min),numpy.radians(self.ang_max))
493 ax.plt = ax.pcolormesh(theta, r, z, cmap=self.colormap, vmin=self.zmin, vmax=self.zmax)
500 ax.plt = ax.pcolormesh(theta, r, z, cmap=self.colormap, vmin=self.zmin, vmax=self.zmax)
494 if data['mode_op'] == 'PPI':
501 if data['mode_op'] == 'PPI':
495 ax.set_theta_direction(-1)
502 ax.set_theta_direction(-1)
496 ax.set_theta_offset(numpy.pi/2)
503 ax.set_theta_offset(numpy.pi/2)
497
504
498 ax.grid(True)
505 ax.grid(True)
499 if data['mode_op'] == 'RHI':
506 if data['mode_op'] == 'RHI':
500 len_aux = int(data['azi'].shape[0]/4)
507 len_aux = int(data['azi'].shape[0]/4)
501 mean = numpy.mean(data['azi'][len_aux:-len_aux])
508 mean = numpy.mean(data['azi'][len_aux:-len_aux])
502 if len(self.channels) !=1:
509 if len(self.channels) !=1:
503 self.titles = ['RHI {} at AZ: {} CH {}'.format(self.labels[x], str(round(mean,1)), x) for x in range(self.nrows)]
510 self.titles = ['RHI {} at AZ: {} CH {}'.format(self.labels[x], str(round(mean,1)), x) for x in range(self.nrows)]
504 else:
511 else:
505 self.titles = ['RHI {} at AZ: {} CH {}'.format(self.labels[0], str(round(mean,1)), self.channels[0])]
512 self.titles = ['RHI {} at AZ: {} CH {}'.format(self.labels[0], str(round(mean,1)), self.channels[0])]
506 elif data['mode_op'] == 'PPI':
513 elif data['mode_op'] == 'PPI':
507 len_aux = int(data['ele'].shape[0]/4)
514 len_aux = int(data['ele'].shape[0]/4)
508 mean = numpy.mean(data['ele'][len_aux:-len_aux])
515 mean = numpy.mean(data['ele'][len_aux:-len_aux])
509 if len(self.channels) !=1:
516 if len(self.channels) !=1:
510 self.titles = ['PPI {} at EL: {} CH {}'.format(self.self.labels[x], str(round(mean,1)), x) for x in range(self.nrows)]
517 self.titles = ['PPI {} at EL: {} CH {}'.format(self.self.labels[x], str(round(mean,1)), x) for x in range(self.nrows)]
511 else:
518 else:
512 self.titles = ['PPI {} at EL: {} CH {}'.format(self.labels[0], str(round(mean,1)), self.channels[0])]
519 self.titles = ['PPI {} at EL: {} CH {}'.format(self.labels[0], str(round(mean,1)), self.channels[0])]
General Comments 0
You need to be logged in to leave comments. Login now