##// END OF EJS Templates
test for localtime plotting issues
Juan C. Espinoza -
r1218:afa054b492c7
parent child
Show More
@@ -1,802 +1,799
1 1
2 2 import os
3 3 import sys
4 4 import zmq
5 5 import time
6 6 import numpy
7 7 import datetime
8 8 from functools import wraps
9 9 from threading import Thread
10 10 import matplotlib
11 11
12 12 if 'BACKEND' in os.environ:
13 13 matplotlib.use(os.environ['BACKEND'])
14 14 elif 'linux' in sys.platform:
15 15 matplotlib.use("TkAgg")
16 16 elif 'darwin' in sys.platform:
17 17 matplotlib.use('WxAgg')
18 18 else:
19 19 from schainpy.utils import log
20 20 log.warning('Using default Backend="Agg"', 'INFO')
21 21 matplotlib.use('Agg')
22 22
23 23 import matplotlib.pyplot as plt
24 24 from matplotlib.patches import Polygon
25 25 from mpl_toolkits.axes_grid1 import make_axes_locatable
26 26 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
27 27
28 28 from schainpy.model.data.jrodata import PlotterData
29 29 from schainpy.model.proc.jroproc_base import ProcessingUnit, Operation, MPDecorator
30 30 from schainpy.utils import log
31 31
32 32 jet_values = matplotlib.pyplot.get_cmap('jet', 100)(numpy.arange(100))[10:90]
33 33 blu_values = matplotlib.pyplot.get_cmap(
34 34 'seismic_r', 20)(numpy.arange(20))[10:15]
35 35 ncmap = matplotlib.colors.LinearSegmentedColormap.from_list(
36 36 'jro', numpy.vstack((blu_values, jet_values)))
37 37 matplotlib.pyplot.register_cmap(cmap=ncmap)
38 38
39 39 CMAPS = [plt.get_cmap(s) for s in ('jro', 'jet', 'viridis',
40 40 'plasma', 'inferno', 'Greys', 'seismic', 'bwr', 'coolwarm')]
41 41
42 42 EARTH_RADIUS = 6.3710e3
43 43
44 44 def ll2xy(lat1, lon1, lat2, lon2):
45 45
46 46 p = 0.017453292519943295
47 47 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
48 48 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
49 49 r = 12742 * numpy.arcsin(numpy.sqrt(a))
50 50 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
51 51 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
52 52 theta = -theta + numpy.pi/2
53 53 return r*numpy.cos(theta), r*numpy.sin(theta)
54 54
55 55
56 56 def km2deg(km):
57 57 '''
58 58 Convert distance in km to degrees
59 59 '''
60 60
61 61 return numpy.rad2deg(km/EARTH_RADIUS)
62 62
63 63
64 64 def figpause(interval):
65 65 backend = plt.rcParams['backend']
66 66 if backend in matplotlib.rcsetup.interactive_bk:
67 67 figManager = matplotlib._pylab_helpers.Gcf.get_active()
68 68 if figManager is not None:
69 69 canvas = figManager.canvas
70 70 if canvas.figure.stale:
71 71 canvas.draw()
72 72 try:
73 73 canvas.start_event_loop(interval)
74 74 except:
75 75 pass
76 76 return
77 77
78 78
79 79 def popup(message):
80 80 '''
81 81 '''
82 82
83 83 fig = plt.figure(figsize=(12, 8), facecolor='r')
84 84 text = '\n'.join([s.strip() for s in message.split(':')])
85 85 fig.text(0.01, 0.5, text, ha='left', va='center',
86 86 size='20', weight='heavy', color='w')
87 87 fig.show()
88 88 figpause(1000)
89 89
90 90
91 91 class Throttle(object):
92 92 '''
93 93 Decorator that prevents a function from being called more than once every
94 94 time period.
95 95 To create a function that cannot be called more than once a minute, but
96 96 will sleep until it can be called:
97 97 @Throttle(minutes=1)
98 98 def foo():
99 99 pass
100 100
101 101 for i in range(10):
102 102 foo()
103 103 print "This function has run %s times." % i
104 104 '''
105 105
106 106 def __init__(self, seconds=0, minutes=0, hours=0):
107 107 self.throttle_period = datetime.timedelta(
108 108 seconds=seconds, minutes=minutes, hours=hours
109 109 )
110 110
111 111 self.time_of_last_call = datetime.datetime.min
112 112
113 113 def __call__(self, fn):
114 114 @wraps(fn)
115 115 def wrapper(*args, **kwargs):
116 116 coerce = kwargs.pop('coerce', None)
117 117 if coerce:
118 118 self.time_of_last_call = datetime.datetime.now()
119 119 return fn(*args, **kwargs)
120 120 else:
121 121 now = datetime.datetime.now()
122 122 time_since_last_call = now - self.time_of_last_call
123 123 time_left = self.throttle_period - time_since_last_call
124 124
125 125 if time_left > datetime.timedelta(seconds=0):
126 126 return
127 127
128 128 self.time_of_last_call = datetime.datetime.now()
129 129 return fn(*args, **kwargs)
130 130
131 131 return wrapper
132 132
133 133 def apply_throttle(value):
134 134
135 135 @Throttle(seconds=value)
136 136 def fnThrottled(fn):
137 137 fn()
138 138
139 139 return fnThrottled
140 140
141 141
142 142 @MPDecorator
143 143 class Plot(Operation):
144 144 '''
145 145 Base class for Schain plotting operations
146 146 '''
147 147
148 148 CODE = 'Figure'
149 149 colormap = 'jet'
150 150 bgcolor = 'white'
151 151 __missing = 1E30
152 152
153 153 __attrs__ = ['show', 'save', 'xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax',
154 154 'zlimits', 'xlabel', 'ylabel', 'xaxis', 'cb_label', 'title',
155 155 'colorbar', 'bgcolor', 'width', 'height', 'localtime', 'oneFigure',
156 156 'showprofile', 'decimation', 'pause']
157 157
158 158 def __init__(self):
159 159
160 160 Operation.__init__(self)
161 161 self.isConfig = False
162 162 self.isPlotConfig = False
163 163 self.save_counter = 1
164 164 self.sender_counter = 1
165 self.data = None
165 166
166 167 def __fmtTime(self, x, pos):
167 168 '''
168 169 '''
169 170
170 171 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
171 172
172 173 def __setup(self, **kwargs):
173 174 '''
174 175 Initialize variables
175 176 '''
176 177
177 178 self.figures = []
178 179 self.axes = []
179 180 self.cb_axes = []
180 181 self.localtime = kwargs.pop('localtime', True)
181 182 self.show = kwargs.get('show', True)
182 183 self.save = kwargs.get('save', False)
183 184 self.save_period = kwargs.get('save_period', 2)
184 185 self.ftp = kwargs.get('ftp', False)
185 186 self.colormap = kwargs.get('colormap', self.colormap)
186 187 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
187 188 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
188 189 self.colormaps = kwargs.get('colormaps', None)
189 190 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
190 191 self.showprofile = kwargs.get('showprofile', False)
191 192 self.title = kwargs.get('wintitle', self.CODE.upper())
192 193 self.cb_label = kwargs.get('cb_label', None)
193 194 self.cb_labels = kwargs.get('cb_labels', None)
194 195 self.labels = kwargs.get('labels', None)
195 196 self.xaxis = kwargs.get('xaxis', 'frequency')
196 197 self.zmin = kwargs.get('zmin', None)
197 198 self.zmax = kwargs.get('zmax', None)
198 199 self.zlimits = kwargs.get('zlimits', None)
199 200 self.xmin = kwargs.get('xmin', None)
200 201 self.xmax = kwargs.get('xmax', None)
201 202 self.xrange = kwargs.get('xrange', 24)
202 203 self.xscale = kwargs.get('xscale', None)
203 204 self.ymin = kwargs.get('ymin', None)
204 205 self.ymax = kwargs.get('ymax', None)
205 206 self.yscale = kwargs.get('yscale', None)
206 207 self.xlabel = kwargs.get('xlabel', None)
207 208 self.decimation = kwargs.get('decimation', None)
208 209 self.showSNR = kwargs.get('showSNR', False)
209 210 self.oneFigure = kwargs.get('oneFigure', True)
210 211 self.width = kwargs.get('width', None)
211 212 self.height = kwargs.get('height', None)
212 213 self.colorbar = kwargs.get('colorbar', True)
213 214 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
214 215 self.channels = kwargs.get('channels', None)
215 216 self.titles = kwargs.get('titles', [])
216 217 self.polar = False
217 218 self.type = kwargs.get('type', 'iq')
218 219 self.grid = kwargs.get('grid', False)
219 220 self.pause = kwargs.get('pause', False)
220 221 self.save_labels = kwargs.get('save_labels', None)
221 222 self.realtime = kwargs.get('realtime', True)
222 223 self.buffering = kwargs.get('buffering', True)
223 224 self.throttle = kwargs.get('throttle', 2)
224 225 self.exp_code = kwargs.get('exp_code', None)
225 226 self.plot_server = kwargs.get('plot_server', False)
226 227 self.sender_period = kwargs.get('sender_period', 2)
227 228 self.__throttle_plot = apply_throttle(self.throttle)
228 229 self.data = PlotterData(
229 230 self.CODE, self.throttle, self.exp_code, self.buffering)
230 231
231 232 if self.plot_server:
232 233 if not self.plot_server.startswith('tcp://'):
233 234 self.plot_server = 'tcp://{}'.format(self.plot_server)
234 235 log.success(
235 236 'Sending to server: {}'.format(self.plot_server),
236 237 self.name
237 238 )
238 239
239 240 def __setup_plot(self):
240 241 '''
241 242 Common setup for all figures, here figures and axes are created
242 243 '''
243 244
244 245 self.setup()
245 246
246 247 self.time_label = 'LT' if self.localtime else 'UTC'
247 if self.data.localtime:
248 self.getDateTime = datetime.datetime.fromtimestamp
249 else:
250 self.getDateTime = datetime.datetime.utcfromtimestamp
251 248
252 249 if self.width is None:
253 250 self.width = 8
254 251
255 252 self.figures = []
256 253 self.axes = []
257 254 self.cb_axes = []
258 255 self.pf_axes = []
259 256 self.cmaps = []
260 257
261 258 size = '15%' if self.ncols == 1 else '30%'
262 259 pad = '4%' if self.ncols == 1 else '8%'
263 260
264 261 if self.oneFigure:
265 262 if self.height is None:
266 263 self.height = 1.4 * self.nrows + 1
267 264 fig = plt.figure(figsize=(self.width, self.height),
268 265 edgecolor='k',
269 266 facecolor='w')
270 267 self.figures.append(fig)
271 268 for n in range(self.nplots):
272 269 ax = fig.add_subplot(self.nrows, self.ncols,
273 270 n + 1, polar=self.polar)
274 271 ax.tick_params(labelsize=8)
275 272 ax.firsttime = True
276 273 ax.index = 0
277 274 ax.press = None
278 275 self.axes.append(ax)
279 276 if self.showprofile:
280 277 cax = self.__add_axes(ax, size=size, pad=pad)
281 278 cax.tick_params(labelsize=8)
282 279 self.pf_axes.append(cax)
283 280 else:
284 281 if self.height is None:
285 282 self.height = 3
286 283 for n in range(self.nplots):
287 284 fig = plt.figure(figsize=(self.width, self.height),
288 285 edgecolor='k',
289 286 facecolor='w')
290 287 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
291 288 ax.tick_params(labelsize=8)
292 289 ax.firsttime = True
293 290 ax.index = 0
294 291 ax.press = None
295 292 self.figures.append(fig)
296 293 self.axes.append(ax)
297 294 if self.showprofile:
298 295 cax = self.__add_axes(ax, size=size, pad=pad)
299 296 cax.tick_params(labelsize=8)
300 297 self.pf_axes.append(cax)
301 298
302 299 for n in range(self.nrows):
303 300 if self.colormaps is not None:
304 301 cmap = plt.get_cmap(self.colormaps[n])
305 302 else:
306 303 cmap = plt.get_cmap(self.colormap)
307 304 cmap.set_bad(self.bgcolor, 1.)
308 305 self.cmaps.append(cmap)
309 306
310 307 for fig in self.figures:
311 308 fig.canvas.mpl_connect('key_press_event', self.OnKeyPress)
312 309 fig.canvas.mpl_connect('scroll_event', self.OnBtnScroll)
313 310 fig.canvas.mpl_connect('button_press_event', self.onBtnPress)
314 311 fig.canvas.mpl_connect('motion_notify_event', self.onMotion)
315 312 fig.canvas.mpl_connect('button_release_event', self.onBtnRelease)
316 313 if self.show:
317 314 fig.show()
318 315
319 316 def OnKeyPress(self, event):
320 317 '''
321 318 Event for pressing keys (up, down) change colormap
322 319 '''
323 320 ax = event.inaxes
324 321 if ax in self.axes:
325 322 if event.key == 'down':
326 323 ax.index += 1
327 324 elif event.key == 'up':
328 325 ax.index -= 1
329 326 if ax.index < 0:
330 327 ax.index = len(CMAPS) - 1
331 328 elif ax.index == len(CMAPS):
332 329 ax.index = 0
333 330 cmap = CMAPS[ax.index]
334 331 ax.cbar.set_cmap(cmap)
335 332 ax.cbar.draw_all()
336 333 ax.plt.set_cmap(cmap)
337 334 ax.cbar.patch.figure.canvas.draw()
338 335 self.colormap = cmap.name
339 336
340 337 def OnBtnScroll(self, event):
341 338 '''
342 339 Event for scrolling, scale figure
343 340 '''
344 341 cb_ax = event.inaxes
345 342 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
346 343 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
347 344 pt = ax.cbar.ax.bbox.get_points()[:, 1]
348 345 nrm = ax.cbar.norm
349 346 vmin, vmax, p0, p1, pS = (
350 347 nrm.vmin, nrm.vmax, pt[0], pt[1], event.y)
351 348 scale = 2 if event.step == 1 else 0.5
352 349 point = vmin + (vmax - vmin) / (p1 - p0) * (pS - p0)
353 350 ax.cbar.norm.vmin = point - scale * (point - vmin)
354 351 ax.cbar.norm.vmax = point - scale * (point - vmax)
355 352 ax.plt.set_norm(ax.cbar.norm)
356 353 ax.cbar.draw_all()
357 354 ax.cbar.patch.figure.canvas.draw()
358 355
359 356 def onBtnPress(self, event):
360 357 '''
361 358 Event for mouse button press
362 359 '''
363 360 cb_ax = event.inaxes
364 361 if cb_ax is None:
365 362 return
366 363
367 364 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
368 365 cb_ax.press = event.x, event.y
369 366 else:
370 367 cb_ax.press = None
371 368
372 369 def onMotion(self, event):
373 370 '''
374 371 Event for move inside colorbar
375 372 '''
376 373 cb_ax = event.inaxes
377 374 if cb_ax is None:
378 375 return
379 376 if cb_ax not in [ax.cbar.ax for ax in self.axes if ax.cbar]:
380 377 return
381 378 if cb_ax.press is None:
382 379 return
383 380
384 381 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
385 382 xprev, yprev = cb_ax.press
386 383 dx = event.x - xprev
387 384 dy = event.y - yprev
388 385 cb_ax.press = event.x, event.y
389 386 scale = ax.cbar.norm.vmax - ax.cbar.norm.vmin
390 387 perc = 0.03
391 388
392 389 if event.button == 1:
393 390 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
394 391 ax.cbar.norm.vmax -= (perc * scale) * numpy.sign(dy)
395 392 elif event.button == 3:
396 393 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
397 394 ax.cbar.norm.vmax += (perc * scale) * numpy.sign(dy)
398 395
399 396 ax.cbar.draw_all()
400 397 ax.plt.set_norm(ax.cbar.norm)
401 398 ax.cbar.patch.figure.canvas.draw()
402 399
403 400 def onBtnRelease(self, event):
404 401 '''
405 402 Event for mouse button release
406 403 '''
407 404 cb_ax = event.inaxes
408 405 if cb_ax is not None:
409 406 cb_ax.press = None
410 407
411 408 def __add_axes(self, ax, size='30%', pad='8%'):
412 409 '''
413 410 Add new axes to the given figure
414 411 '''
415 412 divider = make_axes_locatable(ax)
416 413 nax = divider.new_horizontal(size=size, pad=pad)
417 414 ax.figure.add_axes(nax)
418 415 return nax
419 416
420 417 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
421 418 '''
422 419 Create a masked array for missing data
423 420 '''
424 421 if x_buffer.shape[0] < 2:
425 422 return x_buffer, y_buffer, z_buffer
426 423
427 424 deltas = x_buffer[1:] - x_buffer[0:-1]
428 425 x_median = numpy.median(deltas)
429 426
430 427 index = numpy.where(deltas > 5 * x_median)
431 428
432 429 if len(index[0]) != 0:
433 430 z_buffer[::, index[0], ::] = self.__missing
434 431 z_buffer = numpy.ma.masked_inside(z_buffer,
435 432 0.99 * self.__missing,
436 433 1.01 * self.__missing)
437 434
438 435 return x_buffer, y_buffer, z_buffer
439 436
440 437 def decimate(self):
441 438
442 439 # dx = int(len(self.x)/self.__MAXNUMX) + 1
443 440 dy = int(len(self.y) / self.decimation) + 1
444 441
445 442 # x = self.x[::dx]
446 443 x = self.x
447 444 y = self.y[::dy]
448 445 z = self.z[::, ::, ::dy]
449 446
450 447 return x, y, z
451 448
452 449 def format(self):
453 450 '''
454 451 Set min and max values, labels, ticks and titles
455 452 '''
456 453
457 454 if self.xmin is None:
458 455 xmin = self.data.min_time
459 456 else:
460 457 if self.xaxis is 'time':
461 458 dt = self.getDateTime(self.data.min_time)
462 459 xmin = (dt.replace(hour=int(self.xmin), minute=0, second=0) -
463 460 datetime.datetime(1970, 1, 1)).total_seconds()
464 461 if self.data.localtime:
465 462 xmin += time.timezone
466 463 else:
467 464 xmin = self.xmin
468 465
469 466 if self.xmax is None:
470 467 xmax = xmin + self.xrange * 60 * 60
471 468 else:
472 469 if self.xaxis is 'time':
473 470 dt = self.getDateTime(self.data.max_time)
474 471 xmax = (dt.replace(hour=int(self.xmax), minute=59, second=59) -
475 472 datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=1)).total_seconds()
476 473 if self.data.localtime:
477 474 xmax += time.timezone
478 475 else:
479 476 xmax = self.xmax
480 477
481 478 ymin = self.ymin if self.ymin else numpy.nanmin(self.y)
482 479 ymax = self.ymax if self.ymax else numpy.nanmax(self.y)
483 480 #Y = numpy.array([1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000])
484 481
485 482 #i = 1 if numpy.where(
486 483 # abs(ymax-ymin) <= Y)[0][0] < 0 else numpy.where(abs(ymax-ymin) <= Y)[0][0]
487 484 #ystep = Y[i] / 10.
488 485 dig = int(numpy.log10(ymax))
489 486 if dig == 0:
490 487 digD = len(str(ymax)) - 2
491 488 ydec = ymax*(10**digD)
492 489
493 490 dig = int(numpy.log10(ydec))
494 491 ystep = ((ydec + (10**(dig)))//10**(dig))*(10**(dig))
495 492 ystep = ystep/5
496 493 ystep = ystep/(10**digD)
497 494
498 495 else:
499 496 ystep = ((ymax + (10**(dig)))//10**(dig))*(10**(dig))
500 497 ystep = ystep/5
501 498
502 499 if self.xaxis is not 'time':
503 500
504 501 dig = int(numpy.log10(xmax))
505 502
506 503 if dig <= 0:
507 504 digD = len(str(xmax)) - 2
508 505 xdec = xmax*(10**digD)
509 506
510 507 dig = int(numpy.log10(xdec))
511 508 xstep = ((xdec + (10**(dig)))//10**(dig))*(10**(dig))
512 509 xstep = xstep*0.5
513 510 xstep = xstep/(10**digD)
514 511
515 512 else:
516 513 xstep = ((xmax + (10**(dig)))//10**(dig))*(10**(dig))
517 514 xstep = xstep/5
518 515
519 516 for n, ax in enumerate(self.axes):
520 517 if ax.firsttime:
521 518 ax.set_facecolor(self.bgcolor)
522 519 ax.yaxis.set_major_locator(MultipleLocator(ystep))
523 520 if self.xscale:
524 521 ax.xaxis.set_major_formatter(FuncFormatter(
525 522 lambda x, pos: '{0:g}'.format(x*self.xscale)))
526 523 if self.xscale:
527 524 ax.yaxis.set_major_formatter(FuncFormatter(
528 525 lambda x, pos: '{0:g}'.format(x*self.yscale)))
529 526 if self.xaxis is 'time':
530 527 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
531 528 ax.xaxis.set_major_locator(LinearLocator(9))
532 529 else:
533 530 ax.xaxis.set_major_locator(MultipleLocator(xstep))
534 531 if self.xlabel is not None:
535 532 ax.set_xlabel(self.xlabel)
536 533 ax.set_ylabel(self.ylabel)
537 534 ax.firsttime = False
538 535 if self.showprofile:
539 536 self.pf_axes[n].set_ylim(ymin, ymax)
540 537 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
541 538 self.pf_axes[n].set_xlabel('dB')
542 539 self.pf_axes[n].grid(b=True, axis='x')
543 540 [tick.set_visible(False)
544 541 for tick in self.pf_axes[n].get_yticklabels()]
545 542 if self.colorbar:
546 543 ax.cbar = plt.colorbar(
547 544 ax.plt, ax=ax, fraction=0.05, pad=0.02, aspect=10)
548 545 ax.cbar.ax.tick_params(labelsize=8)
549 546 ax.cbar.ax.press = None
550 547 if self.cb_label:
551 548 ax.cbar.set_label(self.cb_label, size=8)
552 549 elif self.cb_labels:
553 550 ax.cbar.set_label(self.cb_labels[n], size=8)
554 551 else:
555 552 ax.cbar = None
556 553 if self.grid:
557 554 ax.grid(True)
558 555
559 556 if not self.polar:
560 557 ax.set_xlim(xmin, xmax)
561 558 ax.set_ylim(ymin, ymax)
562 559 ax.set_title('{} {} {}'.format(
563 560 self.titles[n],
564 561 self.getDateTime(self.data.max_time).strftime(
565 562 '%Y-%m-%d %H:%M:%S'),
566 563 self.time_label),
567 564 size=8)
568 565 else:
569 566 ax.set_title('{}'.format(self.titles[n]), size=8)
570 567 ax.set_ylim(0, 90)
571 568 ax.set_yticks(numpy.arange(0, 90, 20))
572 569 ax.yaxis.labelpad = 40
573 570
574 571 def clear_figures(self):
575 572 '''
576 573 Reset axes for redraw plots
577 574 '''
578 575
579 576 for ax in self.axes:
580 577 ax.clear()
581 578 ax.firsttime = True
582 579 if ax.cbar:
583 580 ax.cbar.remove()
584 581
585 582 def __plot(self):
586 583 '''
587 584 Main function to plot, format and save figures
588 585 '''
589 586
590 587 try:
591 588 self.plot()
592 589 self.format()
593 590 except Exception as e:
594 591 log.warning('{} Plot could not be updated... check data'.format(
595 592 self.CODE), self.name)
596 593 log.error(str(e), '')
597 594 return
598 595
599 596 for n, fig in enumerate(self.figures):
600 597 if self.nrows == 0 or self.nplots == 0:
601 598 log.warning('No data', self.name)
602 599 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
603 600 fig.canvas.manager.set_window_title(self.CODE)
604 601 continue
605 602
606 603 fig.tight_layout()
607 604 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
608 605 self.getDateTime(self.data.max_time).strftime('%Y/%m/%d')))
609 606 fig.canvas.draw()
610 607
611 608 if self.save:
612 609 self.save_figure(n)
613 610
614 611 if self.plot_server:
615 612 self.send_to_server()
616 613 # t = Thread(target=self.send_to_server)
617 614 # t.start()
618 615
619 616 def save_figure(self, n):
620 617 '''
621 618 '''
622 619
623 620 if self.save_counter < self.save_period:
624 621 self.save_counter += 1
625 622 return
626 623
627 624 self.save_counter = 1
628 625
629 626 fig = self.figures[n]
630 627
631 628 if self.save_labels:
632 629 labels = self.save_labels
633 630 else:
634 631 labels = list(range(self.nrows))
635 632
636 633 if self.oneFigure:
637 634 label = ''
638 635 else:
639 636 label = '-{}'.format(labels[n])
640 637 figname = os.path.join(
641 638 self.save,
642 639 self.CODE,
643 640 '{}{}_{}.png'.format(
644 641 self.CODE,
645 642 label,
646 643 self.getDateTime(self.data.max_time).strftime(
647 644 '%Y%m%d_%H%M%S'
648 645 ),
649 646 )
650 647 )
651 648 log.log('Saving figure: {}'.format(figname), self.name)
652 649 if not os.path.isdir(os.path.dirname(figname)):
653 650 os.makedirs(os.path.dirname(figname))
654 651 fig.savefig(figname)
655 652
656 653 if self.realtime:
657 654 figname = os.path.join(
658 655 self.save,
659 656 '{}{}_{}.png'.format(
660 657 self.CODE,
661 658 label,
662 659 self.getDateTime(self.data.min_time).strftime(
663 660 '%Y%m%d'
664 661 ),
665 662 )
666 663 )
667 664 fig.savefig(figname)
668 665
669 666 def send_to_server(self):
670 667 '''
671 668 '''
672 669
673 670 if self.sender_counter < self.sender_period:
674 671 self.sender_counter += 1
675 672
676 673 self.sender_counter = 1
677 674
678 675 retries = 2
679 676 while True:
680 677 self.socket.send_string(self.data.jsonify())
681 678 socks = dict(self.poll.poll(5000))
682 679 if socks.get(self.socket) == zmq.POLLIN:
683 680 reply = self.socket.recv_string()
684 681 if reply == 'ok':
685 682 log.log("Response from server ok", self.name)
686 683 break
687 684 else:
688 685 log.warning(
689 686 "Malformed reply from server: {}".format(reply), self.name)
690 687
691 688 else:
692 689 log.warning(
693 690 "No response from server, retrying...", self.name)
694 691 self.socket.setsockopt(zmq.LINGER, 0)
695 692 self.socket.close()
696 693 self.poll.unregister(self.socket)
697 694 retries -= 1
698 695 if retries == 0:
699 696 log.error(
700 697 "Server seems to be offline, abandoning", self.name)
701 698 self.socket = self.context.socket(zmq.REQ)
702 699 self.socket.connect(self.plot_server)
703 700 self.poll.register(self.socket, zmq.POLLIN)
704 701 time.sleep(1)
705 702 break
706 703 self.socket = self.context.socket(zmq.REQ)
707 704 self.socket.connect(self.plot_server)
708 705 self.poll.register(self.socket, zmq.POLLIN)
709 706 time.sleep(0.5)
710 707
711 708 def setup(self):
712 709 '''
713 710 This method should be implemented in the child class, the following
714 711 attributes should be set:
715 712
716 713 self.nrows: number of rows
717 714 self.ncols: number of cols
718 715 self.nplots: number of plots (channels or pairs)
719 716 self.ylabel: label for Y axes
720 717 self.titles: list of axes title
721 718
722 719 '''
723 720 raise NotImplementedError
724 721
725 722 def plot(self):
726 723 '''
727 724 Must be defined in the child class
728 725 '''
729 726 raise NotImplementedError
730 727
731 728 def run(self, dataOut, **kwargs):
732
733 if dataOut.error:
734 coerce = True
735 else:
736 coerce = False
729 '''
730 Main plotting routine
731 '''
737 732
738 733 if self.isConfig is False:
739 734 self.__setup(**kwargs)
740 735 if dataOut.type == 'Parameters':
741 self.tmin = dataOut.utctimeInit
736 t = dataOut.utctimeInit
742 737 else:
743 self.tmin = dataOut.utctime
738 t = dataOut.utctime
744 739
745 740 if dataOut.useLocalTime:
746 if not self.localtime:
747 self.tmin += time.timezone
741 self.getDateTime = datetime.datetime.fromtimestamp
748 742 else:
749 if self.localtime:
750 self.tmin -= time.timezone
743 self.getDateTime = datetime.datetime.utcfromtimestamp
744
745 if self.xmin is None:
746 self.tmin = t
747 else:
748 self.tmin = (self.getDateTime(t).replace(hour=self.xmin, minute=0, second=0) - datetime.datetime(1970, 1, 1)).total_seconds()
749
751 750 self.data.setup()
752 751 self.isConfig = True
753 752 if self.plot_server:
754 753 self.context = zmq.Context()
755 754 self.socket = self.context.socket(zmq.REQ)
756 755 self.socket.connect(self.plot_server)
757 756 self.poll = zmq.Poller()
758 757 self.poll.register(self.socket, zmq.POLLIN)
759 758
760 759 if dataOut.type == 'Parameters':
761 760 tm = dataOut.utctimeInit
762 761 else:
763 762 tm = dataOut.utctime
764 763
765 if dataOut.useLocalTime:
766 if not self.localtime:
767 tm += time.timezone
768 else:
769 if self.localtime:
764 if not dataOut.useLocalTime and self.localtime:
770 765 tm -= time.timezone
766 if dataOut.useLocalTime and not self.localtime:
767 tm += time.timezone
771 768
772 769 if self.xaxis is 'time' and self.data and (tm - self.tmin) >= self.xrange*60*60:
773 770 self.save_counter = self.save_period
774 771 self.__plot()
775 772 self.xmin += self.xrange
776 773 if self.xmin >= 24:
777 774 self.xmin -= 24
778 775 self.tmin += self.xrange*60*60
779 776 self.data.setup()
780 777 self.clear_figures()
781 778
782 779 self.data.update(dataOut, tm)
783 780
784 781 if self.isPlotConfig is False:
785 782 self.__setup_plot()
786 783 self.isPlotConfig = True
787 784
788 785 if self.realtime:
789 786 self.__plot()
790 787 else:
791 self.__throttle_plot(self.__plot, coerce=coerce)
788 self.__throttle_plot(self.__plot)#, coerce=coerce)
792 789
793 790 figpause(0.01)
794 791
795 792 def close(self):
796 793
797 794 if self.data:
798 795 self.save_counter = self.save_period
799 796 self.__plot()
800 797 if self.data and self.pause:
801 798 figpause(10)
802 799
General Comments 0
You need to be logged in to leave comments. Login now