##// END OF EJS Templates
New plotting architecture with buffering/throttle capabilities
Juan C. Espinoza -
r1187:66a3db7e736d
parent child
Show More
This diff has been collapsed as it changes many lines, (803 lines changed) Show them Hide them
@@ -0,0 +1,803
1
2 import os
3 import sys
4 import zmq
5 import time
6 import datetime
7 from functools import wraps
8 import numpy
9 import matplotlib
10
11 if 'BACKEND' in os.environ:
12 matplotlib.use(os.environ['BACKEND'])
13 elif 'linux' in sys.platform:
14 matplotlib.use("TkAgg")
15 elif 'darwin' in sys.platform:
16 matplotlib.use('TkAgg')
17 else:
18 from schainpy.utils import log
19 log.warning('Using default Backend="Agg"', 'INFO')
20 matplotlib.use('Agg')
21
22 import matplotlib.pyplot as plt
23 from matplotlib.patches import Polygon
24 from mpl_toolkits.axes_grid1 import make_axes_locatable
25 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
26
27 from schainpy.model.data.jrodata import PlotterData
28 from schainpy.model.proc.jroproc_base import ProcessingUnit, Operation, MPDecorator
29 from schainpy.utils import log
30
31 jet_values = matplotlib.pyplot.get_cmap('jet', 100)(numpy.arange(100))[10:90]
32 blu_values = matplotlib.pyplot.get_cmap(
33 'seismic_r', 20)(numpy.arange(20))[10:15]
34 ncmap = matplotlib.colors.LinearSegmentedColormap.from_list(
35 'jro', numpy.vstack((blu_values, jet_values)))
36 matplotlib.pyplot.register_cmap(cmap=ncmap)
37
38 CMAPS = [plt.get_cmap(s) for s in ('jro', 'jet', 'viridis',
39 'plasma', 'inferno', 'Greys', 'seismic', 'bwr', 'coolwarm')]
40
41 EARTH_RADIUS = 6.3710e3
42
43
44 def ll2xy(lat1, lon1, lat2, lon2):
45
46 p = 0.017453292519943295
47 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
48 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
49 r = 12742 * numpy.arcsin(numpy.sqrt(a))
50 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
51 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
52 theta = -theta + numpy.pi/2
53 return r*numpy.cos(theta), r*numpy.sin(theta)
54
55
56 def km2deg(km):
57 '''
58 Convert distance in km to degrees
59 '''
60
61 return numpy.rad2deg(km/EARTH_RADIUS)
62
63
64 def figpause(interval):
65 backend = plt.rcParams['backend']
66 if backend in matplotlib.rcsetup.interactive_bk:
67 figManager = matplotlib._pylab_helpers.Gcf.get_active()
68 if figManager is not None:
69 canvas = figManager.canvas
70 if canvas.figure.stale:
71 canvas.draw()
72 try:
73 canvas.start_event_loop(interval)
74 except:
75 pass
76 return
77
78
79 def popup(message):
80 '''
81 '''
82
83 fig = plt.figure(figsize=(12, 8), facecolor='r')
84 text = '\n'.join([s.strip() for s in message.split(':')])
85 fig.text(0.01, 0.5, text, ha='left', va='center',
86 size='20', weight='heavy', color='w')
87 fig.show()
88 figpause(1000)
89
90
91 class Throttle(object):
92 '''
93 Decorator that prevents a function from being called more than once every
94 time period.
95 To create a function that cannot be called more than once a minute, but
96 will sleep until it can be called:
97 @Throttle(minutes=1)
98 def foo():
99 pass
100
101 for i in range(10):
102 foo()
103 print "This function has run %s times." % i
104 '''
105
106 def __init__(self, seconds=0, minutes=0, hours=0):
107 self.throttle_period = datetime.timedelta(
108 seconds=seconds, minutes=minutes, hours=hours
109 )
110
111 self.time_of_last_call = datetime.datetime.min
112
113 def __call__(self, fn):
114 @wraps(fn)
115 def wrapper(*args, **kwargs):
116 coerce = kwargs.pop('coerce', None)
117 if coerce:
118 self.time_of_last_call = datetime.datetime.now()
119 return fn(*args, **kwargs)
120 else:
121 now = datetime.datetime.now()
122 time_since_last_call = now - self.time_of_last_call
123 time_left = self.throttle_period - time_since_last_call
124
125 if time_left > datetime.timedelta(seconds=0):
126 return
127
128 self.time_of_last_call = datetime.datetime.now()
129 return fn(*args, **kwargs)
130
131 return wrapper
132
133 def apply_throttle(value):
134
135 @Throttle(seconds=value)
136 def fnThrottled(fn):
137 fn()
138
139 return fnThrottled
140
141 @MPDecorator
142 class Plotter(ProcessingUnit):
143 '''
144 Proccessing unit to handle plot operations
145 '''
146
147 def __init__(self):
148
149 ProcessingUnit.__init__(self)
150
151 def setup(self, **kwargs):
152
153 self.connections = 0
154 self.web_address = kwargs.get('web_server', False)
155 self.realtime = kwargs.get('realtime', False)
156 self.localtime = kwargs.get('localtime', True)
157 self.buffering = kwargs.get('buffering', True)
158 self.throttle = kwargs.get('throttle', 2)
159 self.exp_code = kwargs.get('exp_code', None)
160 self.set_ready = apply_throttle(self.throttle)
161 self.dates = []
162 self.data = PlotterData(
163 self.plots, self.throttle, self.exp_code, self.buffering)
164 self.isConfig = True
165
166 def ready(self):
167 '''
168 Set dataOut ready
169 '''
170
171 self.data.ready = True
172 self.dataOut.data_plt = self.data
173
174 def run(self, realtime=True, localtime=True, buffering=True,
175 throttle=2, exp_code=None, web_server=None):
176
177 if not self.isConfig:
178 self.setup(realtime=realtime, localtime=localtime,
179 buffering=buffering, throttle=throttle, exp_code=exp_code,
180 web_server=web_server)
181
182 if self.web_address:
183 log.success(
184 'Sending to web: {}'.format(self.web_address),
185 self.name
186 )
187 self.context = zmq.Context()
188 self.sender_web = self.context.socket(zmq.REQ)
189 self.sender_web.connect(self.web_address)
190 self.poll = zmq.Poller()
191 self.poll.register(self.sender_web, zmq.POLLIN)
192 time.sleep(1)
193
194 # t = Thread(target=self.event_monitor, args=(monitor,))
195 # t.start()
196
197 self.dataOut = self.dataIn
198 self.data.ready = False
199
200 if self.dataOut.flagNoData:
201 coerce = True
202 else:
203 coerce = False
204
205 if self.dataOut.type == 'Parameters':
206 tm = self.dataOut.utctimeInit
207 else:
208 tm = self.dataOut.utctime
209 if self.dataOut.useLocalTime:
210 if not self.localtime:
211 tm += time.timezone
212 dt = datetime.datetime.fromtimestamp(tm).date()
213 else:
214 if self.localtime:
215 tm -= time.timezone
216 dt = datetime.datetime.utcfromtimestamp(tm).date()
217 if dt not in self.dates:
218 if self.data:
219 self.ready()
220 self.data.setup()
221 self.dates.append(dt)
222
223 self.data.update(self.dataOut, tm)
224
225 if False: # TODO check when publishers ends
226 self.connections -= 1
227 if self.connections == 0 and dt in self.dates:
228 self.data.ended = True
229 self.ready()
230 time.sleep(1)
231 else:
232 if self.realtime:
233 self.ready()
234 if self.web_address:
235 retries = 5
236 while True:
237 self.sender_web.send(self.data.jsonify())
238 socks = dict(self.poll.poll(5000))
239 if socks.get(self.sender_web) == zmq.POLLIN:
240 reply = self.sender_web.recv_string()
241 if reply == 'ok':
242 log.log("Response from server ok", self.name)
243 break
244 else:
245 log.warning(
246 "Malformed reply from server: {}".format(reply), self.name)
247
248 else:
249 log.warning(
250 "No response from server, retrying...", self.name)
251 self.sender_web.setsockopt(zmq.LINGER, 0)
252 self.sender_web.close()
253 self.poll.unregister(self.sender_web)
254 retries -= 1
255 if retries == 0:
256 log.error(
257 "Server seems to be offline, abandoning", self.name)
258 self.sender_web = self.context.socket(zmq.REQ)
259 self.sender_web.connect(self.web_address)
260 self.poll.register(self.sender_web, zmq.POLLIN)
261 time.sleep(1)
262 break
263 self.sender_web = self.context.socket(zmq.REQ)
264 self.sender_web.connect(self.web_address)
265 self.poll.register(self.sender_web, zmq.POLLIN)
266 time.sleep(1)
267 else:
268 self.set_ready(self.ready, coerce=coerce)
269
270 return
271
272 def close(self):
273 pass
274
275
276 @MPDecorator
277 class Plot(Operation):
278 '''
279 Base class for Schain plotting operations
280 '''
281
282 CODE = 'Figure'
283 colormap = 'jro'
284 bgcolor = 'white'
285 __missing = 1E30
286
287 __attrs__ = ['show', 'save', 'xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax',
288 'zlimits', 'xlabel', 'ylabel', 'xaxis', 'cb_label', 'title',
289 'colorbar', 'bgcolor', 'width', 'height', 'localtime', 'oneFigure',
290 'showprofile', 'decimation', 'pause']
291
292 def __init__(self):
293
294 Operation.__init__(self)
295 self.isConfig = False
296 self.isPlotConfig = False
297
298 def __fmtTime(self, x, pos):
299 '''
300 '''
301
302 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
303
304 def __setup(self, **kwargs):
305 '''
306 Initialize variables
307 '''
308
309 self.figures = []
310 self.axes = []
311 self.cb_axes = []
312 self.localtime = kwargs.pop('localtime', True)
313 self.show = kwargs.get('show', True)
314 self.save = kwargs.get('save', False)
315 self.ftp = kwargs.get('ftp', False)
316 self.colormap = kwargs.get('colormap', self.colormap)
317 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
318 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
319 self.colormaps = kwargs.get('colormaps', None)
320 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
321 self.showprofile = kwargs.get('showprofile', False)
322 self.title = kwargs.get('wintitle', self.CODE.upper())
323 self.cb_label = kwargs.get('cb_label', None)
324 self.cb_labels = kwargs.get('cb_labels', None)
325 self.labels = kwargs.get('labels', None)
326 self.xaxis = kwargs.get('xaxis', 'frequency')
327 self.zmin = kwargs.get('zmin', None)
328 self.zmax = kwargs.get('zmax', None)
329 self.zlimits = kwargs.get('zlimits', None)
330 self.xmin = kwargs.get('xmin', None)
331 self.xmax = kwargs.get('xmax', None)
332 self.xrange = kwargs.get('xrange', 12)
333 self.xscale = kwargs.get('xscale', None)
334 self.ymin = kwargs.get('ymin', None)
335 self.ymax = kwargs.get('ymax', None)
336 self.yscale = kwargs.get('yscale', None)
337 self.xlabel = kwargs.get('xlabel', None)
338 self.decimation = kwargs.get('decimation', None)
339 self.showSNR = kwargs.get('showSNR', False)
340 self.oneFigure = kwargs.get('oneFigure', True)
341 self.width = kwargs.get('width', None)
342 self.height = kwargs.get('height', None)
343 self.colorbar = kwargs.get('colorbar', True)
344 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
345 self.channels = kwargs.get('channels', None)
346 self.titles = kwargs.get('titles', [])
347 self.polar = False
348 self.grid = kwargs.get('grid', False)
349 self.pause = kwargs.get('pause', False)
350 self.save_labels = kwargs.get('save_labels', None)
351 self.realtime = kwargs.get('realtime', True)
352 self.buffering = kwargs.get('buffering', True)
353 self.throttle = kwargs.get('throttle', 2)
354 self.exp_code = kwargs.get('exp_code', None)
355 self.__throttle_plot = apply_throttle(self.throttle)
356 self.data = PlotterData(
357 self.CODE, self.throttle, self.exp_code, self.buffering)
358
359 def __setup_plot(self):
360 '''
361 Common setup for all figures, here figures and axes are created
362 '''
363
364 self.setup()
365
366 self.time_label = 'LT' if self.localtime else 'UTC'
367 if self.data.localtime:
368 self.getDateTime = datetime.datetime.fromtimestamp
369 else:
370 self.getDateTime = datetime.datetime.utcfromtimestamp
371
372 if self.width is None:
373 self.width = 8
374
375 self.figures = []
376 self.axes = []
377 self.cb_axes = []
378 self.pf_axes = []
379 self.cmaps = []
380
381 size = '15%' if self.ncols == 1 else '30%'
382 pad = '4%' if self.ncols == 1 else '8%'
383
384 if self.oneFigure:
385 if self.height is None:
386 self.height = 1.4 * self.nrows + 1
387 fig = plt.figure(figsize=(self.width, self.height),
388 edgecolor='k',
389 facecolor='w')
390 self.figures.append(fig)
391 for n in range(self.nplots):
392 ax = fig.add_subplot(self.nrows, self.ncols,
393 n + 1, polar=self.polar)
394 ax.tick_params(labelsize=8)
395 ax.firsttime = True
396 ax.index = 0
397 ax.press = None
398 self.axes.append(ax)
399 if self.showprofile:
400 cax = self.__add_axes(ax, size=size, pad=pad)
401 cax.tick_params(labelsize=8)
402 self.pf_axes.append(cax)
403 else:
404 if self.height is None:
405 self.height = 3
406 for n in range(self.nplots):
407 fig = plt.figure(figsize=(self.width, self.height),
408 edgecolor='k',
409 facecolor='w')
410 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
411 ax.tick_params(labelsize=8)
412 ax.firsttime = True
413 ax.index = 0
414 ax.press = None
415 self.figures.append(fig)
416 self.axes.append(ax)
417 if self.showprofile:
418 cax = self.__add_axes(ax, size=size, pad=pad)
419 cax.tick_params(labelsize=8)
420 self.pf_axes.append(cax)
421
422 for n in range(self.nrows):
423 if self.colormaps is not None:
424 cmap = plt.get_cmap(self.colormaps[n])
425 else:
426 cmap = plt.get_cmap(self.colormap)
427 cmap.set_bad(self.bgcolor, 1.)
428 self.cmaps.append(cmap)
429
430 for fig in self.figures:
431 fig.canvas.mpl_connect('key_press_event', self.OnKeyPress)
432 fig.canvas.mpl_connect('scroll_event', self.OnBtnScroll)
433 fig.canvas.mpl_connect('button_press_event', self.onBtnPress)
434 fig.canvas.mpl_connect('motion_notify_event', self.onMotion)
435 fig.canvas.mpl_connect('button_release_event', self.onBtnRelease)
436 if self.show:
437 fig.show()
438
439 def OnKeyPress(self, event):
440 '''
441 Event for pressing keys (up, down) change colormap
442 '''
443 ax = event.inaxes
444 if ax in self.axes:
445 if event.key == 'down':
446 ax.index += 1
447 elif event.key == 'up':
448 ax.index -= 1
449 if ax.index < 0:
450 ax.index = len(CMAPS) - 1
451 elif ax.index == len(CMAPS):
452 ax.index = 0
453 cmap = CMAPS[ax.index]
454 ax.cbar.set_cmap(cmap)
455 ax.cbar.draw_all()
456 ax.plt.set_cmap(cmap)
457 ax.cbar.patch.figure.canvas.draw()
458 self.colormap = cmap.name
459
460 def OnBtnScroll(self, event):
461 '''
462 Event for scrolling, scale figure
463 '''
464 cb_ax = event.inaxes
465 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
466 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
467 pt = ax.cbar.ax.bbox.get_points()[:, 1]
468 nrm = ax.cbar.norm
469 vmin, vmax, p0, p1, pS = (
470 nrm.vmin, nrm.vmax, pt[0], pt[1], event.y)
471 scale = 2 if event.step == 1 else 0.5
472 point = vmin + (vmax - vmin) / (p1 - p0) * (pS - p0)
473 ax.cbar.norm.vmin = point - scale * (point - vmin)
474 ax.cbar.norm.vmax = point - scale * (point - vmax)
475 ax.plt.set_norm(ax.cbar.norm)
476 ax.cbar.draw_all()
477 ax.cbar.patch.figure.canvas.draw()
478
479 def onBtnPress(self, event):
480 '''
481 Event for mouse button press
482 '''
483 cb_ax = event.inaxes
484 if cb_ax is None:
485 return
486
487 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
488 cb_ax.press = event.x, event.y
489 else:
490 cb_ax.press = None
491
492 def onMotion(self, event):
493 '''
494 Event for move inside colorbar
495 '''
496 cb_ax = event.inaxes
497 if cb_ax is None:
498 return
499 if cb_ax not in [ax.cbar.ax for ax in self.axes if ax.cbar]:
500 return
501 if cb_ax.press is None:
502 return
503
504 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
505 xprev, yprev = cb_ax.press
506 dx = event.x - xprev
507 dy = event.y - yprev
508 cb_ax.press = event.x, event.y
509 scale = ax.cbar.norm.vmax - ax.cbar.norm.vmin
510 perc = 0.03
511
512 if event.button == 1:
513 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
514 ax.cbar.norm.vmax -= (perc * scale) * numpy.sign(dy)
515 elif event.button == 3:
516 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
517 ax.cbar.norm.vmax += (perc * scale) * numpy.sign(dy)
518
519 ax.cbar.draw_all()
520 ax.plt.set_norm(ax.cbar.norm)
521 ax.cbar.patch.figure.canvas.draw()
522
523 def onBtnRelease(self, event):
524 '''
525 Event for mouse button release
526 '''
527 cb_ax = event.inaxes
528 if cb_ax is not None:
529 cb_ax.press = None
530
531 def __add_axes(self, ax, size='30%', pad='8%'):
532 '''
533 Add new axes to the given figure
534 '''
535 divider = make_axes_locatable(ax)
536 nax = divider.new_horizontal(size=size, pad=pad)
537 ax.figure.add_axes(nax)
538 return nax
539
540 def setup(self):
541 '''
542 This method should be implemented in the child class, the following
543 attributes should be set:
544
545 self.nrows: number of rows
546 self.ncols: number of cols
547 self.nplots: number of plots (channels or pairs)
548 self.ylabel: label for Y axes
549 self.titles: list of axes title
550
551 '''
552 raise NotImplementedError
553
554 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
555 '''
556 Create a masked array for missing data
557 '''
558 if x_buffer.shape[0] < 2:
559 return x_buffer, y_buffer, z_buffer
560
561 deltas = x_buffer[1:] - x_buffer[0:-1]
562 x_median = numpy.median(deltas)
563
564 index = numpy.where(deltas > 5 * x_median)
565
566 if len(index[0]) != 0:
567 z_buffer[::, index[0], ::] = self.__missing
568 z_buffer = numpy.ma.masked_inside(z_buffer,
569 0.99 * self.__missing,
570 1.01 * self.__missing)
571
572 return x_buffer, y_buffer, z_buffer
573
574 def decimate(self):
575
576 # dx = int(len(self.x)/self.__MAXNUMX) + 1
577 dy = int(len(self.y) / self.decimation) + 1
578
579 # x = self.x[::dx]
580 x = self.x
581 y = self.y[::dy]
582 z = self.z[::, ::, ::dy]
583
584 return x, y, z
585
586 def format(self):
587 '''
588 Set min and max values, labels, ticks and titles
589 '''
590
591 if self.xmin is None:
592 xmin = self.data.min_time
593 else:
594 if self.xaxis is 'time':
595 dt = self.getDateTime(self.data.min_time)
596 xmin = (dt.replace(hour=int(self.xmin), minute=0, second=0) -
597 datetime.datetime(1970, 1, 1)).total_seconds()
598 if self.data.localtime:
599 xmin += time.timezone
600 else:
601 xmin = self.xmin
602
603 if self.xmax is None:
604 xmax = xmin + self.xrange * 60 * 60
605 else:
606 if self.xaxis is 'time':
607 dt = self.getDateTime(self.data.max_time)
608 xmax = (dt.replace(hour=int(self.xmax), minute=59, second=59) -
609 datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=1)).total_seconds()
610 if self.data.localtime:
611 xmax += time.timezone
612 else:
613 xmax = self.xmax
614
615 ymin = self.ymin if self.ymin else numpy.nanmin(self.y)
616 ymax = self.ymax if self.ymax else numpy.nanmax(self.y)
617
618 Y = numpy.array([1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000])
619 i = 1 if numpy.where(
620 abs(ymax-ymin) <= Y)[0][0] < 0 else numpy.where(abs(ymax-ymin) <= Y)[0][0]
621 ystep = Y[i] / 10.
622
623 if self.xaxis is not 'time':
624 X = numpy.array([1, 2, 5, 10, 20, 50, 100,
625 200, 500, 1000, 2000, 5000])/2.
626 i = 1 if numpy.where(
627 abs(xmax-xmin) <= X)[0][0] < 0 else numpy.where(abs(xmax-xmin) <= X)[0][0]
628 xstep = X[i] / 10.
629
630 for n, ax in enumerate(self.axes):
631 if ax.firsttime:
632 ax.set_facecolor(self.bgcolor)
633 ax.yaxis.set_major_locator(MultipleLocator(ystep))
634 if self.xscale:
635 ax.xaxis.set_major_formatter(FuncFormatter(
636 lambda x, pos: '{0:g}'.format(x*self.xscale)))
637 if self.xscale:
638 ax.yaxis.set_major_formatter(FuncFormatter(
639 lambda x, pos: '{0:g}'.format(x*self.yscale)))
640 if self.xaxis is 'time':
641 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
642 ax.xaxis.set_major_locator(LinearLocator(9))
643 else:
644 ax.xaxis.set_major_locator(MultipleLocator(xstep))
645 if self.xlabel is not None:
646 ax.set_xlabel(self.xlabel)
647 ax.set_ylabel(self.ylabel)
648 ax.firsttime = False
649 if self.showprofile:
650 self.pf_axes[n].set_ylim(ymin, ymax)
651 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
652 self.pf_axes[n].set_xlabel('dB')
653 self.pf_axes[n].grid(b=True, axis='x')
654 [tick.set_visible(False)
655 for tick in self.pf_axes[n].get_yticklabels()]
656 if self.colorbar:
657 ax.cbar = plt.colorbar(
658 ax.plt, ax=ax, fraction=0.05, pad=0.02, aspect=10)
659 ax.cbar.ax.tick_params(labelsize=8)
660 ax.cbar.ax.press = None
661 if self.cb_label:
662 ax.cbar.set_label(self.cb_label, size=8)
663 elif self.cb_labels:
664 ax.cbar.set_label(self.cb_labels[n], size=8)
665 else:
666 ax.cbar = None
667 if self.grid:
668 ax.grid(True)
669
670 if not self.polar:
671 ax.set_xlim(xmin, xmax)
672 ax.set_ylim(ymin, ymax)
673 ax.set_title('{} {} {}'.format(
674 self.titles[n],
675 self.getDateTime(self.data.max_time).strftime(
676 '%Y-%m-%dT%H:%M:%S'),
677 self.time_label),
678 size=8)
679 else:
680 ax.set_title('{}'.format(self.titles[n]), size=8)
681 ax.set_ylim(0, 90)
682 ax.set_yticks(numpy.arange(0, 90, 20))
683 ax.yaxis.labelpad = 40
684
685 def clear_figures(self):
686 '''
687 Reset axes for redraw plots
688 '''
689
690 for ax in self.axes:
691 ax.clear()
692 ax.firsttime = True
693 if ax.cbar:
694 ax.cbar.remove()
695
696 def __plot(self):
697 '''
698 Main function to plot, format and save figures
699 '''
700
701 #try:
702 self.plot()
703 self.format()
704 #except Exception as e:
705 # log.warning('{} Plot could not be updated... check data'.format(
706 # self.CODE), self.name)
707 # log.error(str(e), '')
708 # return
709
710 for n, fig in enumerate(self.figures):
711 if self.nrows == 0 or self.nplots == 0:
712 log.warning('No data', self.name)
713 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
714 fig.canvas.manager.set_window_title(self.CODE)
715 continue
716
717 fig.tight_layout()
718 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
719 self.getDateTime(self.data.max_time).strftime('%Y/%m/%d')))
720 fig.canvas.draw()
721
722 if self.save:
723
724 if self.save_labels:
725 labels = self.save_labels
726 else:
727 labels = list(range(self.nrows))
728
729 if self.oneFigure:
730 label = ''
731 else:
732 label = '-{}'.format(labels[n])
733 figname = os.path.join(
734 self.save,
735 self.CODE,
736 '{}{}_{}.png'.format(
737 self.CODE,
738 label,
739 self.getDateTime(self.data.max_time).strftime(
740 '%Y%m%d_%H%M%S'),
741 )
742 )
743 log.log('Saving figure: {}'.format(figname), self.name)
744 if not os.path.isdir(os.path.dirname(figname)):
745 os.makedirs(os.path.dirname(figname))
746 fig.savefig(figname)
747
748 def plot(self):
749 '''
750 Must be defined in the child class
751 '''
752 raise NotImplementedError
753
754 def run(self, dataOut, **kwargs):
755
756 if dataOut.flagNoData and not dataOut.error:
757 return dataOut
758
759 if dataOut.error:
760 coerce = True
761 else:
762 coerce = False
763
764 if self.isConfig is False:
765 self.__setup(**kwargs)
766 self.data.setup()
767 self.isConfig = True
768
769 if dataOut.type == 'Parameters':
770 tm = dataOut.utctimeInit
771 else:
772 tm = dataOut.utctime
773
774 if dataOut.useLocalTime:
775 if not self.localtime:
776 tm += time.timezone
777 else:
778 if self.localtime:
779 tm -= time.timezone
780
781 if self.data and (tm - self.data.min_time) >= self.xrange*60*60:
782 self.__plot()
783 self.data.setup()
784 self.clear_figures()
785
786 self.data.update(dataOut, tm)
787
788 if self.isPlotConfig is False:
789 self.__setup_plot()
790 self.isPlotConfig = True
791
792 if self.realtime:
793 self.__plot()
794 else:
795 self.__throttle_plot(self.__plot, coerce=coerce)
796
797 figpause(0.001)
798
799 def close(self):
800
801 if self.data and self.pause:
802 figpause(10)
803
@@ -1,5 +1,12
1 ## CHANGELOG:
1 ## CHANGELOG:
2
2
3 ### 3.0
4 * Python 3.x compatible
5 * New architecture with multiprocessing and IPC communication
6 * Add @MPDecorator for multiprocessing Units and Operations
7 * Added new type of operation `external` for non-locking operations
8 * New plotting architecture with buffering/throttle capabilities to speed up plots
9
3 ### 2.3
10 ### 2.3
4 * Added support for Madrigal formats (reading/writing).
11 * Added support for Madrigal formats (reading/writing).
5 * Added support for reading BLTR parameters (*.sswma).
12 * Added support for reading BLTR parameters (*.sswma).
@@ -24,7 +24,7 from email.mime.multipart import MIMEMultipart
24
24
25 import schainpy
25 import schainpy
26 from schainpy.utils import log
26 from schainpy.utils import log
27 from schainpy.model.graphics.jroplot_data import popup
27 from schainpy.model.graphics.jroplot_base import popup
28
28
29 def get_path():
29 def get_path():
30 '''
30 '''
@@ -97,9 +97,7 def wait(context):
97 receiver = c.socket(zmq.SUB)
97 receiver = c.socket(zmq.SUB)
98 receiver.connect('ipc:///tmp/schain_{}_pub'.format(self.id))
98 receiver.connect('ipc:///tmp/schain_{}_pub'.format(self.id))
99 receiver.setsockopt(zmq.SUBSCRIBE, self.id.encode())
99 receiver.setsockopt(zmq.SUBSCRIBE, self.id.encode())
100 log.error('startinggg')
101 msg = receiver.recv_multipart()[1]
100 msg = receiver.recv_multipart()[1]
102 #log.error(msg)
103 context.terminate()
101 context.terminate()
104
102
105 class ParameterConf():
103 class ParameterConf():
@@ -1245,7 +1243,7 class Project(Process):
1245
1243
1246 try:
1244 try:
1247 zmq.proxy(xpub, xsub)
1245 zmq.proxy(xpub, xsub)
1248 except zmq.ContextTerminated:
1246 except: # zmq.ContextTerminated:
1249 xpub.close()
1247 xpub.close()
1250 xsub.close()
1248 xsub.close()
1251
1249
@@ -1260,6 +1258,6 class Project(Process):
1260
1258
1261 # Iniciar todos los procesos .start(), monitoreo de procesos. ELiminar lo de abajo
1259 # Iniciar todos los procesos .start(), monitoreo de procesos. ELiminar lo de abajo
1262
1260
1263 log.success('{} finished (time: {}s)'.format(
1261 log.success('{} Done (time: {}s)'.format(
1264 self.name,
1262 self.name,
1265 time.time()-self.start_time))
1263 time.time()-self.start_time))
@@ -7,7 +7,9 $Id: JROData.py 173 2012-11-20 15:06:21Z murco $
7 import copy
7 import copy
8 import numpy
8 import numpy
9 import datetime
9 import datetime
10 import json
10
11
12 from schainpy.utils import log
11 from .jroheaderIO import SystemHeader, RadarControllerHeader
13 from .jroheaderIO import SystemHeader, RadarControllerHeader
12
14
13
15
@@ -79,7 +81,7 def hildebrand_sekhon(data, navg):
79 j = 0
81 j = 0
80 cont = 1
82 cont = 1
81
83
82 while((cont==1)and(j<lenOfData)):
84 while((cont == 1)and(j < lenOfData)):
83
85
84 sump += sortdata[j]
86 sump += sortdata[j]
85 sumq += sortdata[j]**2
87 sumq += sortdata[j]**2
@@ -88,13 +90,13 def hildebrand_sekhon(data, navg):
88 rtest = float(j)/(j-1) + 1.0/navg
90 rtest = float(j)/(j-1) + 1.0/navg
89 if ((sumq*j) > (rtest*sump**2)):
91 if ((sumq*j) > (rtest*sump**2)):
90 j = j - 1
92 j = j - 1
91 sump = sump - sortdata[j]
93 sump = sump - sortdata[j]
92 sumq = sumq - sortdata[j]**2
94 sumq = sumq - sortdata[j]**2
93 cont = 0
95 cont = 0
94
96
95 j += 1
97 j += 1
96
98
97 lnoise = sump /j
99 lnoise = sump / j
98
100
99 return lnoise
101 return lnoise
100
102
@@ -147,85 +149,49 class JROData(GenericData):
147 # m_ProcessingHeader = ProcessingHeader()
149 # m_ProcessingHeader = ProcessingHeader()
148
150
149 systemHeaderObj = SystemHeader()
151 systemHeaderObj = SystemHeader()
150
151 radarControllerHeaderObj = RadarControllerHeader()
152 radarControllerHeaderObj = RadarControllerHeader()
152
153 # data = None
153 # data = None
154
155 type = None
154 type = None
156
157 datatype = None # dtype but in string
155 datatype = None # dtype but in string
158
159 # dtype = None
156 # dtype = None
160
161 # nChannels = None
157 # nChannels = None
162
163 # nHeights = None
158 # nHeights = None
164
165 nProfiles = None
159 nProfiles = None
166
167 heightList = None
160 heightList = None
168
169 channelList = None
161 channelList = None
170
171 flagDiscontinuousBlock = False
162 flagDiscontinuousBlock = False
172
173 useLocalTime = False
163 useLocalTime = False
174
175 utctime = None
164 utctime = None
176
177 timeZone = None
165 timeZone = None
178
179 dstFlag = None
166 dstFlag = None
180
181 errorCount = None
167 errorCount = None
182
183 blocksize = None
168 blocksize = None
184
185 # nCode = None
169 # nCode = None
186 #
187 # nBaud = None
170 # nBaud = None
188 #
189 # code = None
171 # code = None
190
191 flagDecodeData = False # asumo q la data no esta decodificada
172 flagDecodeData = False # asumo q la data no esta decodificada
192
193 flagDeflipData = False # asumo q la data no esta sin flip
173 flagDeflipData = False # asumo q la data no esta sin flip
194
195 flagShiftFFT = False
174 flagShiftFFT = False
196
197 # ippSeconds = None
175 # ippSeconds = None
198
199 # timeInterval = None
176 # timeInterval = None
200
201 nCohInt = None
177 nCohInt = None
202
203 # noise = None
178 # noise = None
204
205 windowOfFilter = 1
179 windowOfFilter = 1
206
207 # Speed of ligth
180 # Speed of ligth
208 C = 3e8
181 C = 3e8
209
210 frequency = 49.92e6
182 frequency = 49.92e6
211
212 realtime = False
183 realtime = False
213
214 beacon_heiIndexList = None
184 beacon_heiIndexList = None
215
216 last_block = None
185 last_block = None
217
218 blocknow = None
186 blocknow = None
219
220 azimuth = None
187 azimuth = None
221
222 zenith = None
188 zenith = None
223
224 beam = Beam()
189 beam = Beam()
225
226 profileIndex = None
190 profileIndex = None
191 error = None
192 data = None
193 data_plt = None
227
194
228 error = (0, '')
229
195
230 def __str__(self):
196 def __str__(self):
231
197
@@ -395,53 +361,29 class Voltage(JROData):
395 '''
361 '''
396
362
397 self.useLocalTime = True
363 self.useLocalTime = True
398
399 self.radarControllerHeaderObj = RadarControllerHeader()
364 self.radarControllerHeaderObj = RadarControllerHeader()
400
401 self.systemHeaderObj = SystemHeader()
365 self.systemHeaderObj = SystemHeader()
402
403 self.type = "Voltage"
366 self.type = "Voltage"
404
405 self.data = None
367 self.data = None
406
407 # self.dtype = None
368 # self.dtype = None
408
409 # self.nChannels = 0
369 # self.nChannels = 0
410
411 # self.nHeights = 0
370 # self.nHeights = 0
412
413 self.nProfiles = None
371 self.nProfiles = None
414
372 self.heightList = Non
415 self.heightList = None
416
417 self.channelList = None
373 self.channelList = None
418
419 # self.channelIndexList = None
374 # self.channelIndexList = None
420
421 self.flagNoData = True
375 self.flagNoData = True
422
423 self.flagDiscontinuousBlock = False
376 self.flagDiscontinuousBlock = False
424
425 self.utctime = None
377 self.utctime = None
426
427 self.timeZone = None
378 self.timeZone = None
428
429 self.dstFlag = None
379 self.dstFlag = None
430
431 self.errorCount = None
380 self.errorCount = None
432
433 self.nCohInt = None
381 self.nCohInt = None
434
435 self.blocksize = None
382 self.blocksize = None
436
437 self.flagDecodeData = False # asumo q la data no esta decodificada
383 self.flagDecodeData = False # asumo q la data no esta decodificada
438
439 self.flagDeflipData = False # asumo q la data no esta sin flip
384 self.flagDeflipData = False # asumo q la data no esta sin flip
440
441 self.flagShiftFFT = False
385 self.flagShiftFFT = False
442
443 self.flagDataAsBlock = False # Asumo que la data es leida perfil a perfil
386 self.flagDataAsBlock = False # Asumo que la data es leida perfil a perfil
444
445 self.profileIndex = 0
387 self.profileIndex = 0
446
388
447 def getNoisebyHildebrand(self, channel=None):
389 def getNoisebyHildebrand(self, channel=None):
@@ -505,93 +447,53 class Spectra(JROData):
505
447
506 # data spc es un numpy array de 2 dmensiones (canales, perfiles, alturas)
448 # data spc es un numpy array de 2 dmensiones (canales, perfiles, alturas)
507 data_spc = None
449 data_spc = None
508
509 # data cspc es un numpy array de 2 dmensiones (canales, pares, alturas)
450 # data cspc es un numpy array de 2 dmensiones (canales, pares, alturas)
510 data_cspc = None
451 data_cspc = None
511
512 # data dc es un numpy array de 2 dmensiones (canales, alturas)
452 # data dc es un numpy array de 2 dmensiones (canales, alturas)
513 data_dc = None
453 data_dc = None
514
515 # data power
454 # data power
516 data_pwr = None
455 data_pwr = None
517
518 nFFTPoints = None
456 nFFTPoints = None
519
520 # nPairs = None
457 # nPairs = None
521
522 pairsList = None
458 pairsList = None
523
524 nIncohInt = None
459 nIncohInt = None
525
526 wavelength = None # Necesario para cacular el rango de velocidad desde la frecuencia
460 wavelength = None # Necesario para cacular el rango de velocidad desde la frecuencia
527
528 nCohInt = None # se requiere para determinar el valor de timeInterval
461 nCohInt = None # se requiere para determinar el valor de timeInterval
529
530 ippFactor = None
462 ippFactor = None
531
532 profileIndex = 0
463 profileIndex = 0
533
534 plotting = "spectra"
464 plotting = "spectra"
535
536 def __init__(self):
465 def __init__(self):
537 '''
466 '''
538 Constructor
467 Constructor
539 '''
468 '''
540
469
541 self.useLocalTime = True
470 self.useLocalTime = True
542
543 self.radarControllerHeaderObj = RadarControllerHeader()
471 self.radarControllerHeaderObj = RadarControllerHeader()
544
545 self.systemHeaderObj = SystemHeader()
472 self.systemHeaderObj = SystemHeader()
546
547 self.type = "Spectra"
473 self.type = "Spectra"
548
549 # self.data = None
474 # self.data = None
550
551 # self.dtype = None
475 # self.dtype = None
552
553 # self.nChannels = 0
476 # self.nChannels = 0
554
555 # self.nHeights = 0
477 # self.nHeights = 0
556
557 self.nProfiles = None
478 self.nProfiles = None
558
559 self.heightList = None
479 self.heightList = None
560
561 self.channelList = None
480 self.channelList = None
562
563 # self.channelIndexList = None
481 # self.channelIndexList = None
564
565 self.pairsList = None
482 self.pairsList = None
566
567 self.flagNoData = True
483 self.flagNoData = True
568
569 self.flagDiscontinuousBlock = False
484 self.flagDiscontinuousBlock = False
570
571 self.utctime = None
485 self.utctime = None
572
573 self.nCohInt = None
486 self.nCohInt = None
574
575 self.nIncohInt = None
487 self.nIncohInt = None
576
577 self.blocksize = None
488 self.blocksize = None
578
579 self.nFFTPoints = None
489 self.nFFTPoints = None
580
581 self.wavelength = None
490 self.wavelength = None
582
583 self.flagDecodeData = False # asumo q la data no esta decodificada
491 self.flagDecodeData = False # asumo q la data no esta decodificada
584
585 self.flagDeflipData = False # asumo q la data no esta sin flip
492 self.flagDeflipData = False # asumo q la data no esta sin flip
586
587 self.flagShiftFFT = False
493 self.flagShiftFFT = False
588
589 self.ippFactor = 1
494 self.ippFactor = 1
590
591 #self.noise = None
495 #self.noise = None
592
593 self.beacon_heiIndexList = []
496 self.beacon_heiIndexList = []
594
595 self.noise_estimation = None
497 self.noise_estimation = None
596
498
597 def getNoisebyHildebrand(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None):
499 def getNoisebyHildebrand(self, xmin_index=None, xmax_index=None, ymin_index=None, ymax_index=None):
@@ -692,7 +594,8 class Spectra(JROData):
692
594
693 def getTimeInterval(self):
595 def getTimeInterval(self):
694
596
695 timeInterval = self.ippSeconds * self.nCohInt * self.nIncohInt * self.nProfiles * self.ippFactor
597 timeInterval = self.ippSeconds * self.nCohInt * \
598 self.nIncohInt * self.nProfiles * self.ippFactor
696
599
697 return timeInterval
600 return timeInterval
698
601
@@ -755,19 +658,12 class Spectra(JROData):
755 class SpectraHeis(Spectra):
658 class SpectraHeis(Spectra):
756
659
757 data_spc = None
660 data_spc = None
758
759 data_cspc = None
661 data_cspc = None
760
761 data_dc = None
662 data_dc = None
762
763 nFFTPoints = None
663 nFFTPoints = None
764
765 # nPairs = None
664 # nPairs = None
766
767 pairsList = None
665 pairsList = None
768
769 nCohInt = None
666 nCohInt = None
770
771 nIncohInt = None
667 nIncohInt = None
772
668
773 def __init__(self):
669 def __init__(self):
@@ -830,36 +726,21 class SpectraHeis(Spectra):
830 class Fits(JROData):
726 class Fits(JROData):
831
727
832 heightList = None
728 heightList = None
833
834 channelList = None
729 channelList = None
835
836 flagNoData = True
730 flagNoData = True
837
838 flagDiscontinuousBlock = False
731 flagDiscontinuousBlock = False
839
840 useLocalTime = False
732 useLocalTime = False
841
842 utctime = None
733 utctime = None
843
844 timeZone = None
734 timeZone = None
845
846 # ippSeconds = None
735 # ippSeconds = None
847
848 # timeInterval = None
736 # timeInterval = None
849
850 nCohInt = None
737 nCohInt = None
851
852 nIncohInt = None
738 nIncohInt = None
853
854 noise = None
739 noise = None
855
856 windowOfFilter = 1
740 windowOfFilter = 1
857
858 # Speed of ligth
741 # Speed of ligth
859 C = 3e8
742 C = 3e8
860
861 frequency = 49.92e6
743 frequency = 49.92e6
862
863 realtime = False
744 realtime = False
864
745
865 def __init__(self):
746 def __init__(self):
@@ -978,33 +859,19 class Fits(JROData):
978 class Correlation(JROData):
859 class Correlation(JROData):
979
860
980 noise = None
861 noise = None
981
982 SNR = None
862 SNR = None
983
984 #--------------------------------------------------
863 #--------------------------------------------------
985
986 mode = None
864 mode = None
987
988 split = False
865 split = False
989
990 data_cf = None
866 data_cf = None
991
992 lags = None
867 lags = None
993
994 lagRange = None
868 lagRange = None
995
996 pairsList = None
869 pairsList = None
997
998 normFactor = None
870 normFactor = None
999
1000 #--------------------------------------------------
871 #--------------------------------------------------
1001
1002 # calculateVelocity = None
872 # calculateVelocity = None
1003
1004 nLags = None
873 nLags = None
1005
1006 nPairs = None
874 nPairs = None
1007
1008 nAvg = None
875 nAvg = None
1009
876
1010 def __init__(self):
877 def __init__(self):
@@ -1068,7 +935,8 class Correlation(JROData):
1068 ind_vel = numpy.array([-2, -1, 1, 2]) + freq_dc
935 ind_vel = numpy.array([-2, -1, 1, 2]) + freq_dc
1069
936
1070 if ind_vel[0] < 0:
937 if ind_vel[0] < 0:
1071 ind_vel[list(range(0, 1))] = ind_vel[list(range(0, 1))] + self.num_prof
938 ind_vel[list(range(0, 1))] = ind_vel[list(
939 range(0, 1))] + self.num_prof
1072
940
1073 if mode == 1:
941 if mode == 1:
1074 jspectra[:, freq_dc, :] = (
942 jspectra[:, freq_dc, :] = (
@@ -1154,55 +1022,30 class Correlation(JROData):
1154 class Parameters(Spectra):
1022 class Parameters(Spectra):
1155
1023
1156 experimentInfo = None # Information about the experiment
1024 experimentInfo = None # Information about the experiment
1157
1158 # Information from previous data
1025 # Information from previous data
1159
1160 inputUnit = None # Type of data to be processed
1026 inputUnit = None # Type of data to be processed
1161
1162 operation = None # Type of operation to parametrize
1027 operation = None # Type of operation to parametrize
1163
1164 # normFactor = None #Normalization Factor
1028 # normFactor = None #Normalization Factor
1165
1166 groupList = None # List of Pairs, Groups, etc
1029 groupList = None # List of Pairs, Groups, etc
1167
1168 # Parameters
1030 # Parameters
1169
1170 data_param = None # Parameters obtained
1031 data_param = None # Parameters obtained
1171
1172 data_pre = None # Data Pre Parametrization
1032 data_pre = None # Data Pre Parametrization
1173
1174 data_SNR = None # Signal to Noise Ratio
1033 data_SNR = None # Signal to Noise Ratio
1175
1176 # heightRange = None #Heights
1034 # heightRange = None #Heights
1177
1178 abscissaList = None # Abscissa, can be velocities, lags or time
1035 abscissaList = None # Abscissa, can be velocities, lags or time
1179
1180 # noise = None #Noise Potency
1036 # noise = None #Noise Potency
1181
1182 utctimeInit = None # Initial UTC time
1037 utctimeInit = None # Initial UTC time
1183
1184 paramInterval = None # Time interval to calculate Parameters in seconds
1038 paramInterval = None # Time interval to calculate Parameters in seconds
1185
1186 useLocalTime = True
1039 useLocalTime = True
1187
1188 # Fitting
1040 # Fitting
1189
1190 data_error = None # Error of the estimation
1041 data_error = None # Error of the estimation
1191
1192 constants = None
1042 constants = None
1193
1194 library = None
1043 library = None
1195
1196 # Output signal
1044 # Output signal
1197
1198 outputInterval = None # Time interval to calculate output signal in seconds
1045 outputInterval = None # Time interval to calculate output signal in seconds
1199
1200 data_output = None # Out signal
1046 data_output = None # Out signal
1201
1202 nAvg = None
1047 nAvg = None
1203
1204 noise_estimation = None
1048 noise_estimation = None
1205
1206 GauSPC = None # Fit gaussian SPC
1049 GauSPC = None # Fit gaussian SPC
1207
1050
1208 def __init__(self):
1051 def __init__(self):
@@ -1248,4 +1091,252 class Parameters(Spectra):
1248 return self.spc_noise
1091 return self.spc_noise
1249
1092
1250 timeInterval = property(getTimeInterval)
1093 timeInterval = property(getTimeInterval)
1251 noise = property(getNoise, setValue, "I'm the 'Noise' property.") No newline at end of file
1094 noise = property(getNoise, setValue, "I'm the 'Noise' property.")
1095
1096
1097 class PlotterData(object):
1098 '''
1099 Object to hold data to be plotted
1100 '''
1101
1102 MAXNUMX = 100
1103 MAXNUMY = 100
1104
1105 def __init__(self, code, throttle_value, exp_code, buffering=True):
1106
1107 self.throttle = throttle_value
1108 self.exp_code = exp_code
1109 self.buffering = buffering
1110 self.ready = False
1111 self.localtime = False
1112 self.data = {}
1113 self.meta = {}
1114 self.__times = []
1115 self.__heights = []
1116
1117 if 'snr' in code:
1118 self.plottypes = ['snr']
1119 elif code == 'spc':
1120 self.plottypes = ['spc', 'noise', 'rti']
1121 elif code == 'rti':
1122 self.plottypes = ['noise', 'rti']
1123 else:
1124 self.plottypes = [code]
1125
1126 for plot in self.plottypes:
1127 self.data[plot] = {}
1128
1129 def __str__(self):
1130 dum = ['{}{}'.format(key, self.shape(key)) for key in self.data]
1131 return 'Data[{}][{}]'.format(';'.join(dum), len(self.__times))
1132
1133 def __len__(self):
1134 return len(self.__times)
1135
1136 def __getitem__(self, key):
1137
1138 if key not in self.data:
1139 raise KeyError(log.error('Missing key: {}'.format(key)))
1140 if 'spc' in key or not self.buffering:
1141 ret = self.data[key]
1142 else:
1143 ret = numpy.array([self.data[key][x] for x in self.times])
1144 if ret.ndim > 1:
1145 ret = numpy.swapaxes(ret, 0, 1)
1146 return ret
1147
1148 def __contains__(self, key):
1149 return key in self.data
1150
1151 def setup(self):
1152 '''
1153 Configure object
1154 '''
1155
1156 self.type = ''
1157 self.ready = False
1158 self.data = {}
1159 self.__times = []
1160 self.__heights = []
1161 self.__all_heights = set()
1162 for plot in self.plottypes:
1163 if 'snr' in plot:
1164 plot = 'snr'
1165 self.data[plot] = {}
1166
1167 if 'spc' in self.data or 'rti' in self.data:
1168 self.data['noise'] = {}
1169 if 'noise' not in self.plottypes:
1170 self.plottypes.append('noise')
1171
1172 def shape(self, key):
1173 '''
1174 Get the shape of the one-element data for the given key
1175 '''
1176
1177 if len(self.data[key]):
1178 if 'spc' in key or not self.buffering:
1179 return self.data[key].shape
1180 return self.data[key][self.__times[0]].shape
1181 return (0,)
1182
1183 def update(self, dataOut, tm):
1184 '''
1185 Update data object with new dataOut
1186 '''
1187
1188 if tm in self.__times:
1189 return
1190
1191 self.type = dataOut.type
1192 self.parameters = getattr(dataOut, 'parameters', [])
1193 if hasattr(dataOut, 'pairsList'):
1194 self.pairs = dataOut.pairsList
1195 if hasattr(dataOut, 'meta'):
1196 self.meta = dataOut.meta
1197 self.channels = dataOut.channelList
1198 self.interval = dataOut.getTimeInterval()
1199 self.localtime = dataOut.useLocalTime
1200 if 'spc' in self.plottypes or 'cspc' in self.plottypes:
1201 self.xrange = (dataOut.getFreqRange(1)/1000.,
1202 dataOut.getAcfRange(1), dataOut.getVelRange(1))
1203 self.__heights.append(dataOut.heightList)
1204 self.__all_heights.update(dataOut.heightList)
1205 self.__times.append(tm)
1206
1207 for plot in self.plottypes:
1208 if plot == 'spc':
1209 z = dataOut.data_spc/dataOut.normFactor
1210 buffer = 10*numpy.log10(z)
1211 if plot == 'cspc':
1212 buffer = dataOut.data_cspc
1213 if plot == 'noise':
1214 buffer = 10*numpy.log10(dataOut.getNoise()/dataOut.normFactor)
1215 if plot == 'rti':
1216 buffer = dataOut.getPower()
1217 if plot == 'snr_db':
1218 buffer = dataOut.data_SNR
1219 if plot == 'snr':
1220 buffer = 10*numpy.log10(dataOut.data_SNR)
1221 if plot == 'dop':
1222 buffer = 10*numpy.log10(dataOut.data_DOP)
1223 if plot == 'mean':
1224 buffer = dataOut.data_MEAN
1225 if plot == 'std':
1226 buffer = dataOut.data_STD
1227 if plot == 'coh':
1228 buffer = dataOut.getCoherence()
1229 if plot == 'phase':
1230 buffer = dataOut.getCoherence(phase=True)
1231 if plot == 'output':
1232 buffer = dataOut.data_output
1233 if plot == 'param':
1234 buffer = dataOut.data_param
1235
1236 if 'spc' in plot:
1237 self.data[plot] = buffer
1238 else:
1239 if self.buffering:
1240 self.data[plot][tm] = buffer
1241 else:
1242 self.data[plot] = buffer
1243
1244 def normalize_heights(self):
1245 '''
1246 Ensure same-dimension of the data for different heighList
1247 '''
1248
1249 H = numpy.array(list(self.__all_heights))
1250 H.sort()
1251 for key in self.data:
1252 shape = self.shape(key)[:-1] + H.shape
1253 for tm, obj in list(self.data[key].items()):
1254 h = self.__heights[self.__times.index(tm)]
1255 if H.size == h.size:
1256 continue
1257 index = numpy.where(numpy.in1d(H, h))[0]
1258 dummy = numpy.zeros(shape) + numpy.nan
1259 if len(shape) == 2:
1260 dummy[:, index] = obj
1261 else:
1262 dummy[index] = obj
1263 self.data[key][tm] = dummy
1264
1265 self.__heights = [H for tm in self.__times]
1266
1267 def jsonify(self, decimate=False):
1268 '''
1269 Convert data to json
1270 '''
1271
1272 data = {}
1273 tm = self.times[-1]
1274 dy = int(self.heights.size/self.MAXNUMY) + 1
1275 for key in self.data:
1276 if key in ('spc', 'cspc') or not self.buffering:
1277 dx = int(self.data[key].shape[1]/self.MAXNUMX) + 1
1278 data[key] = self.roundFloats(
1279 self.data[key][::, ::dx, ::dy].tolist())
1280 else:
1281 data[key] = self.roundFloats(self.data[key][tm].tolist())
1282
1283 ret = {'data': data}
1284 ret['exp_code'] = self.exp_code
1285 ret['time'] = float(tm)
1286 ret['interval'] = float(self.interval)
1287 ret['localtime'] = self.localtime
1288 ret['yrange'] = self.roundFloats(self.heights[::dy].tolist())
1289 if 'spc' in self.data or 'cspc' in self.data:
1290 ret['xrange'] = self.roundFloats(self.xrange[2][::dx].tolist())
1291 else:
1292 ret['xrange'] = []
1293 if hasattr(self, 'pairs'):
1294 ret['pairs'] = [(int(p[0]), int(p[1])) for p in self.pairs]
1295 else:
1296 ret['pairs'] = []
1297
1298 for key, value in list(self.meta.items()):
1299 ret[key] = value
1300
1301 return json.dumps(ret)
1302
1303 @property
1304 def times(self):
1305 '''
1306 Return the list of times of the current data
1307 '''
1308
1309 ret = numpy.array(self.__times)
1310 ret.sort()
1311 return ret
1312
1313 @property
1314 def min_time(self):
1315 '''
1316 Return the minimun time value
1317 '''
1318
1319 return self.times[0]
1320
1321 @property
1322 def max_time(self):
1323 '''
1324 Return the maximun time value
1325 '''
1326
1327 return self.times[-1]
1328
1329 @property
1330 def heights(self):
1331 '''
1332 Return the list of heights of the current data
1333 '''
1334
1335 return numpy.array(self.__heights[-1])
1336
1337 @staticmethod
1338 def roundFloats(obj):
1339 if isinstance(obj, list):
1340 return list(map(PlotterData.roundFloats, obj))
1341 elif isinstance(obj, float):
1342 return round(obj, 2)
@@ -4,4 +4,3 from .jroplot_heispectra import *
4 from .jroplot_correlation import *
4 from .jroplot_correlation import *
5 from .jroplot_parameters import *
5 from .jroplot_parameters import *
6 from .jroplot_data import *
6 from .jroplot_data import *
7 from .jroplotter import *
@@ -5,7 +5,7 import copy
5 from schainpy.model import *
5 from schainpy.model import *
6 from .figure import Figure, isRealtime
6 from .figure import Figure, isRealtime
7
7
8 class CorrelationPlot(Figure):
8 class CorrelationPlot_(Figure):
9 isConfig = None
9 isConfig = None
10 __nsubplots = None
10 __nsubplots = None
11
11
This diff has been collapsed as it changes many lines, (685 lines changed) Show them Hide them
@@ -1,41 +1,32
1 '''
2 New Plots Operations
3
4 @author: juan.espinoza@jro.igp.gob.pe
5 '''
6
1
7
2 import os
3 import time
8 import time
4 import glob
5 import datetime
9 import datetime
6 from multiprocessing import Process
7
8 import zmq
9 import numpy
10 import numpy
10 import matplotlib
11 import matplotlib.pyplot as plt
12 from matplotlib.patches import Polygon
13 from mpl_toolkits.axes_grid1 import make_axes_locatable
14 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
15
11
16 from schainpy.model.proc.jroproc_base import Operation
12 from schainpy.model.graphics.jroplot_base import Plot, plt
17 from schainpy.utils import log
13 from schainpy.utils import log
18
14
19 jet_values = matplotlib.pyplot.get_cmap('jet', 100)(numpy.arange(100))[10:90]
20 blu_values = matplotlib.pyplot.get_cmap(
21 'seismic_r', 20)(numpy.arange(20))[10:15]
22 ncmap = matplotlib.colors.LinearSegmentedColormap.from_list(
23 'jro', numpy.vstack((blu_values, jet_values)))
24 matplotlib.pyplot.register_cmap(cmap=ncmap)
25
26 CMAPS = [plt.get_cmap(s) for s in ('jro', 'jet', 'viridis', 'plasma', 'inferno', 'Greys', 'seismic', 'bwr', 'coolwarm')]
27
28 EARTH_RADIUS = 6.3710e3
15 EARTH_RADIUS = 6.3710e3
29
16
17
30 def ll2xy(lat1, lon1, lat2, lon2):
18 def ll2xy(lat1, lon1, lat2, lon2):
31
19
32 p = 0.017453292519943295
20 p = 0.017453292519943295
33 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
21 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
22 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
34 r = 12742 * numpy.arcsin(numpy.sqrt(a))
23 r = 12742 * numpy.arcsin(numpy.sqrt(a))
35 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)*numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
24 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
25 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
36 theta = -theta + numpy.pi/2
26 theta = -theta + numpy.pi/2
37 return r*numpy.cos(theta), r*numpy.sin(theta)
27 return r*numpy.cos(theta), r*numpy.sin(theta)
38
28
29
39 def km2deg(km):
30 def km2deg(km):
40 '''
31 '''
41 Convert distance in km to degrees
32 Convert distance in km to degrees
@@ -43,536 +34,8 def km2deg(km):
43
34
44 return numpy.rad2deg(km/EARTH_RADIUS)
35 return numpy.rad2deg(km/EARTH_RADIUS)
45
36
46 def figpause(interval):
47 backend = plt.rcParams['backend']
48 if backend in matplotlib.rcsetup.interactive_bk:
49 figManager = matplotlib._pylab_helpers.Gcf.get_active()
50 if figManager is not None:
51 canvas = figManager.canvas
52 if canvas.figure.stale:
53 canvas.draw()
54 try:
55 canvas.start_event_loop(interval)
56 except:
57 pass
58 return
59
60 def popup(message):
61 '''
62 '''
63
64 fig = plt.figure(figsize=(12, 8), facecolor='r')
65 text = '\n'.join([s.strip() for s in message.split(':')])
66 fig.text(0.01, 0.5, text, ha='left', va='center', size='20', weight='heavy', color='w')
67 fig.show()
68 figpause(1000)
69
70
71 class PlotData(Operation, Process):
72 '''
73 Base class for Schain plotting operations
74 '''
75
76 CODE = 'Figure'
77 colormap = 'jro'
78 bgcolor = 'white'
79 CONFLATE = False
80 __missing = 1E30
81
82 __attrs__ = ['show', 'save', 'xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax',
83 'zlimits', 'xlabel', 'ylabel', 'xaxis','cb_label', 'title',
84 'colorbar', 'bgcolor', 'width', 'height', 'localtime', 'oneFigure',
85 'showprofile', 'decimation', 'ftp']
86
87 def __init__(self, **kwargs):
88
89 Operation.__init__(self, plot=True, **kwargs)
90 Process.__init__(self)
91
92 self.kwargs['code'] = self.CODE
93 self.mp = False
94 self.data = None
95 self.isConfig = False
96 self.figures = []
97 self.axes = []
98 self.cb_axes = []
99 self.localtime = kwargs.pop('localtime', True)
100 self.show = kwargs.get('show', True)
101 self.save = kwargs.get('save', False)
102 self.ftp = kwargs.get('ftp', False)
103 self.colormap = kwargs.get('colormap', self.colormap)
104 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
105 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
106 self.colormaps = kwargs.get('colormaps', None)
107 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
108 self.showprofile = kwargs.get('showprofile', False)
109 self.title = kwargs.get('wintitle', self.CODE.upper())
110 self.cb_label = kwargs.get('cb_label', None)
111 self.cb_labels = kwargs.get('cb_labels', None)
112 self.labels = kwargs.get('labels', None)
113 self.xaxis = kwargs.get('xaxis', 'frequency')
114 self.zmin = kwargs.get('zmin', None)
115 self.zmax = kwargs.get('zmax', None)
116 self.zlimits = kwargs.get('zlimits', None)
117 self.xmin = kwargs.get('xmin', None)
118 self.xmax = kwargs.get('xmax', None)
119 self.xrange = kwargs.get('xrange', 24)
120 self.xscale = kwargs.get('xscale', None)
121 self.ymin = kwargs.get('ymin', None)
122 self.ymax = kwargs.get('ymax', None)
123 self.yscale = kwargs.get('yscale', None)
124 self.xlabel = kwargs.get('xlabel', None)
125 self.decimation = kwargs.get('decimation', None)
126 self.showSNR = kwargs.get('showSNR', False)
127 self.oneFigure = kwargs.get('oneFigure', True)
128 self.width = kwargs.get('width', None)
129 self.height = kwargs.get('height', None)
130 self.colorbar = kwargs.get('colorbar', True)
131 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
132 self.channels = kwargs.get('channels', None)
133 self.titles = kwargs.get('titles', [])
134 self.polar = False
135 self.grid = kwargs.get('grid', False)
136
137 def __fmtTime(self, x, pos):
138 '''
139 '''
140
141 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
142
143 def __setup(self):
144 '''
145 Common setup for all figures, here figures and axes are created
146 '''
147
148 if self.CODE not in self.data:
149 raise ValueError(log.error('Missing data for {}'.format(self.CODE),
150 self.name))
151
152 self.setup()
153
154 self.time_label = 'LT' if self.localtime else 'UTC'
155 if self.data.localtime:
156 self.getDateTime = datetime.datetime.fromtimestamp
157 else:
158 self.getDateTime = datetime.datetime.utcfromtimestamp
159
160 if self.width is None:
161 self.width = 8
162
163 self.figures = []
164 self.axes = []
165 self.cb_axes = []
166 self.pf_axes = []
167 self.cmaps = []
168
169 size = '15%' if self.ncols == 1 else '30%'
170 pad = '4%' if self.ncols == 1 else '8%'
171
172 if self.oneFigure:
173 if self.height is None:
174 self.height = 1.4 * self.nrows + 1
175 fig = plt.figure(figsize=(self.width, self.height),
176 edgecolor='k',
177 facecolor='w')
178 self.figures.append(fig)
179 for n in range(self.nplots):
180 ax = fig.add_subplot(self.nrows, self.ncols,
181 n + 1, polar=self.polar)
182 ax.tick_params(labelsize=8)
183 ax.firsttime = True
184 ax.index = 0
185 ax.press = None
186 self.axes.append(ax)
187 if self.showprofile:
188 cax = self.__add_axes(ax, size=size, pad=pad)
189 cax.tick_params(labelsize=8)
190 self.pf_axes.append(cax)
191 else:
192 if self.height is None:
193 self.height = 3
194 for n in range(self.nplots):
195 fig = plt.figure(figsize=(self.width, self.height),
196 edgecolor='k',
197 facecolor='w')
198 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
199 ax.tick_params(labelsize=8)
200 ax.firsttime = True
201 ax.index = 0
202 ax.press = None
203 self.figures.append(fig)
204 self.axes.append(ax)
205 if self.showprofile:
206 cax = self.__add_axes(ax, size=size, pad=pad)
207 cax.tick_params(labelsize=8)
208 self.pf_axes.append(cax)
209
210 for n in range(self.nrows):
211 if self.colormaps is not None:
212 cmap = plt.get_cmap(self.colormaps[n])
213 else:
214 cmap = plt.get_cmap(self.colormap)
215 cmap.set_bad(self.bgcolor, 1.)
216 self.cmaps.append(cmap)
217
218 for fig in self.figures:
219 fig.canvas.mpl_connect('key_press_event', self.OnKeyPress)
220 fig.canvas.mpl_connect('scroll_event', self.OnBtnScroll)
221 fig.canvas.mpl_connect('button_press_event', self.onBtnPress)
222 fig.canvas.mpl_connect('motion_notify_event', self.onMotion)
223 fig.canvas.mpl_connect('button_release_event', self.onBtnRelease)
224 if self.show:
225 fig.show()
226
227 def OnKeyPress(self, event):
228 '''
229 Event for pressing keys (up, down) change colormap
230 '''
231 ax = event.inaxes
232 if ax in self.axes:
233 if event.key == 'down':
234 ax.index += 1
235 elif event.key == 'up':
236 ax.index -= 1
237 if ax.index < 0:
238 ax.index = len(CMAPS) - 1
239 elif ax.index == len(CMAPS):
240 ax.index = 0
241 cmap = CMAPS[ax.index]
242 ax.cbar.set_cmap(cmap)
243 ax.cbar.draw_all()
244 ax.plt.set_cmap(cmap)
245 ax.cbar.patch.figure.canvas.draw()
246 self.colormap = cmap.name
247
248 def OnBtnScroll(self, event):
249 '''
250 Event for scrolling, scale figure
251 '''
252 cb_ax = event.inaxes
253 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
254 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
255 pt = ax.cbar.ax.bbox.get_points()[:, 1]
256 nrm = ax.cbar.norm
257 vmin, vmax, p0, p1, pS = (
258 nrm.vmin, nrm.vmax, pt[0], pt[1], event.y)
259 scale = 2 if event.step == 1 else 0.5
260 point = vmin + (vmax - vmin) / (p1 - p0) * (pS - p0)
261 ax.cbar.norm.vmin = point - scale * (point - vmin)
262 ax.cbar.norm.vmax = point - scale * (point - vmax)
263 ax.plt.set_norm(ax.cbar.norm)
264 ax.cbar.draw_all()
265 ax.cbar.patch.figure.canvas.draw()
266
267 def onBtnPress(self, event):
268 '''
269 Event for mouse button press
270 '''
271 cb_ax = event.inaxes
272 if cb_ax is None:
273 return
274
275 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
276 cb_ax.press = event.x, event.y
277 else:
278 cb_ax.press = None
279
280 def onMotion(self, event):
281 '''
282 Event for move inside colorbar
283 '''
284 cb_ax = event.inaxes
285 if cb_ax is None:
286 return
287 if cb_ax not in [ax.cbar.ax for ax in self.axes if ax.cbar]:
288 return
289 if cb_ax.press is None:
290 return
291
292 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
293 xprev, yprev = cb_ax.press
294 dx = event.x - xprev
295 dy = event.y - yprev
296 cb_ax.press = event.x, event.y
297 scale = ax.cbar.norm.vmax - ax.cbar.norm.vmin
298 perc = 0.03
299
300 if event.button == 1:
301 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
302 ax.cbar.norm.vmax -= (perc * scale) * numpy.sign(dy)
303 elif event.button == 3:
304 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
305 ax.cbar.norm.vmax += (perc * scale) * numpy.sign(dy)
306
307 ax.cbar.draw_all()
308 ax.plt.set_norm(ax.cbar.norm)
309 ax.cbar.patch.figure.canvas.draw()
310
311 def onBtnRelease(self, event):
312 '''
313 Event for mouse button release
314 '''
315 cb_ax = event.inaxes
316 if cb_ax is not None:
317 cb_ax.press = None
318
319 def __add_axes(self, ax, size='30%', pad='8%'):
320 '''
321 Add new axes to the given figure
322 '''
323 divider = make_axes_locatable(ax)
324 nax = divider.new_horizontal(size=size, pad=pad)
325 ax.figure.add_axes(nax)
326 return nax
327
328 self.setup()
329
330 def setup(self):
331 '''
332 This method should be implemented in the child class, the following
333 attributes should be set:
334
335 self.nrows: number of rows
336 self.ncols: number of cols
337 self.nplots: number of plots (channels or pairs)
338 self.ylabel: label for Y axes
339 self.titles: list of axes title
340
341 '''
342 raise NotImplementedError
343
344 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
345 '''
346 Create a masked array for missing data
347 '''
348 if x_buffer.shape[0] < 2:
349 return x_buffer, y_buffer, z_buffer
350
351 deltas = x_buffer[1:] - x_buffer[0:-1]
352 x_median = numpy.median(deltas)
353
354 index = numpy.where(deltas > 5 * x_median)
355
356 if len(index[0]) != 0:
357 z_buffer[::, index[0], ::] = self.__missing
358 z_buffer = numpy.ma.masked_inside(z_buffer,
359 0.99 * self.__missing,
360 1.01 * self.__missing)
361
362 return x_buffer, y_buffer, z_buffer
363
364 def decimate(self):
365
366 # dx = int(len(self.x)/self.__MAXNUMX) + 1
367 dy = int(len(self.y) / self.decimation) + 1
368
369 # x = self.x[::dx]
370 x = self.x
371 y = self.y[::dy]
372 z = self.z[::, ::, ::dy]
373
374 return x, y, z
375
37
376 def format(self):
38 class SpectraPlot(Plot):
377 '''
378 Set min and max values, labels, ticks and titles
379 '''
380
381 if self.xmin is None:
382 xmin = self.min_time
383 else:
384 if self.xaxis is 'time':
385 dt = self.getDateTime(self.min_time)
386 xmin = (dt.replace(hour=int(self.xmin), minute=0, second=0) -
387 datetime.datetime(1970, 1, 1)).total_seconds()
388 if self.data.localtime:
389 xmin += time.timezone
390 else:
391 xmin = self.xmin
392
393 if self.xmax is None:
394 xmax = xmin + self.xrange * 60 * 60
395 else:
396 if self.xaxis is 'time':
397 dt = self.getDateTime(self.max_time)
398 xmax = (dt.replace(hour=int(self.xmax), minute=59, second=59) -
399 datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=1)).total_seconds()
400 if self.data.localtime:
401 xmax += time.timezone
402 else:
403 xmax = self.xmax
404
405 ymin = self.ymin if self.ymin else numpy.nanmin(self.y)
406 ymax = self.ymax if self.ymax else numpy.nanmax(self.y)
407
408 Y = numpy.array([1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000])
409 i = 1 if numpy.where(abs(ymax-ymin) <= Y)[0][0] < 0 else numpy.where(abs(ymax-ymin) <= Y)[0][0]
410 ystep = Y[i] / 10.
411
412 if self.xaxis is not 'time':
413 X = numpy.array([1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000])/2.
414 i = 1 if numpy.where(abs(xmax-xmin) <= X)[0][0] < 0 else numpy.where(abs(xmax-xmin) <= X)[0][0]
415 xstep = X[i] / 10.
416
417 for n, ax in enumerate(self.axes):
418 if ax.firsttime:
419 ax.set_facecolor(self.bgcolor)
420 ax.yaxis.set_major_locator(MultipleLocator(ystep))
421 if self.xscale:
422 ax.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{0:g}'.format(x*self.xscale)))
423 if self.xscale:
424 ax.yaxis.set_major_formatter(FuncFormatter(lambda x, pos: '{0:g}'.format(x*self.yscale)))
425 if self.xaxis is 'time':
426 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
427 ax.xaxis.set_major_locator(LinearLocator(9))
428 else:
429 ax.xaxis.set_major_locator(MultipleLocator(xstep))
430 if self.xlabel is not None:
431 ax.set_xlabel(self.xlabel)
432 ax.set_ylabel(self.ylabel)
433 ax.firsttime = False
434 if self.showprofile:
435 self.pf_axes[n].set_ylim(ymin, ymax)
436 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
437 self.pf_axes[n].set_xlabel('dB')
438 self.pf_axes[n].grid(b=True, axis='x')
439 [tick.set_visible(False)
440 for tick in self.pf_axes[n].get_yticklabels()]
441 if self.colorbar:
442 ax.cbar = plt.colorbar(
443 ax.plt, ax=ax, fraction=0.05, pad=0.02, aspect=10)
444 ax.cbar.ax.tick_params(labelsize=8)
445 ax.cbar.ax.press = None
446 if self.cb_label:
447 ax.cbar.set_label(self.cb_label, size=8)
448 elif self.cb_labels:
449 ax.cbar.set_label(self.cb_labels[n], size=8)
450 else:
451 ax.cbar = None
452 if self.grid:
453 ax.grid(True)
454
455 if not self.polar:
456 ax.set_xlim(xmin, xmax)
457 ax.set_ylim(ymin, ymax)
458 ax.set_title('{} {} {}'.format(
459 self.titles[n],
460 self.getDateTime(self.max_time).strftime('%Y-%m-%dT%H:%M:%S'),
461 self.time_label),
462 size=8)
463 else:
464 ax.set_title('{}'.format(self.titles[n]), size=8)
465 ax.set_ylim(0, 90)
466 ax.set_yticks(numpy.arange(0, 90, 20))
467 ax.yaxis.labelpad = 40
468
469 def __plot(self):
470 '''
471 '''
472 log.log('Plotting', self.name)
473
474 try:
475 self.plot()
476 self.format()
477 except Exception as e:
478 log.warning('{} Plot could not be updated... check data'.format(self.CODE), self.name)
479 log.error(str(e), '')
480 return
481
482 for n, fig in enumerate(self.figures):
483 if self.nrows == 0 or self.nplots == 0:
484 log.warning('No data', self.name)
485 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
486 fig.canvas.manager.set_window_title(self.CODE)
487 continue
488
489 fig.tight_layout()
490 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
491 self.getDateTime(self.max_time).strftime('%Y/%m/%d')))
492 fig.canvas.draw()
493
494 if self.save and (self.data.ended or not self.data.buffering):
495
496 if self.save_labels:
497 labels = self.save_labels
498 else:
499 labels = list(range(self.nrows))
500
501 if self.oneFigure:
502 label = ''
503 else:
504 label = '-{}'.format(labels[n])
505 figname = os.path.join(
506 self.save,
507 self.CODE,
508 '{}{}_{}.png'.format(
509 self.CODE,
510 label,
511 self.getDateTime(self.saveTime).strftime(
512 '%Y%m%d_%H%M%S'),
513 )
514 )
515 log.log('Saving figure: {}'.format(figname), self.name)
516 if not os.path.isdir(os.path.dirname(figname)):
517 os.makedirs(os.path.dirname(figname))
518 fig.savefig(figname)
519
520 def plot(self):
521 '''
522 '''
523 raise NotImplementedError
524
525 def run(self):
526
527 log.log('Starting', self.name)
528
529 context = zmq.Context()
530 receiver = context.socket(zmq.SUB)
531 receiver.setsockopt(zmq.SUBSCRIBE, '')
532 receiver.setsockopt(zmq.CONFLATE, self.CONFLATE)
533
534 if 'server' in self.kwargs['parent']:
535 receiver.connect(
536 'ipc:///tmp/{}.plots'.format(self.kwargs['parent']['server']))
537 else:
538 receiver.connect("ipc:///tmp/zmq.plots")
539
540 while True:
541 try:
542 self.data = receiver.recv_pyobj(flags=zmq.NOBLOCK)
543 if self.data.localtime and self.localtime:
544 self.times = self.data.times
545 elif self.data.localtime and not self.localtime:
546 self.times = self.data.times + time.timezone
547 elif not self.data.localtime and self.localtime:
548 self.times = self.data.times - time.timezone
549 else:
550 self.times = self.data.times
551
552 self.min_time = self.times[0]
553 self.max_time = self.times[-1]
554
555 if self.isConfig is False:
556 self.__setup()
557 self.isConfig = True
558
559 self.__plot()
560
561 except zmq.Again as e:
562 if self.data and self.data.ended:
563 break
564 log.log('Waiting for data...')
565 if self.data:
566 figpause(self.data.throttle)
567 else:
568 time.sleep(2)
569
570 def close(self):
571 if self.data:
572 self.__plot()
573
574
575 class PlotSpectraData(PlotData):
576 '''
39 '''
577 Plot for Spectra data
40 Plot for Spectra data
578 '''
41 '''
@@ -644,10 +107,9 class PlotSpectraData(PlotData):
644 ax.plt_mean.set_data(mean, y)
107 ax.plt_mean.set_data(mean, y)
645
108
646 self.titles.append('CH {}: {:3.2f}dB'.format(n, noise))
109 self.titles.append('CH {}: {:3.2f}dB'.format(n, noise))
647 self.saveTime = self.max_time
648
110
649
111
650 class PlotCrossSpectraData(PlotData):
112 class CrossSpectraPlot(Plot):
651
113
652 CODE = 'cspc'
114 CODE = 'cspc'
653 zmin_coh = None
115 zmin_coh = None
@@ -741,10 +203,8 class PlotCrossSpectraData(PlotData):
741 ax.plt.set_array(phase.T.ravel())
203 ax.plt.set_array(phase.T.ravel())
742 self.titles.append('Phase CH{} * CH{}'.format(pair[0], pair[1]))
204 self.titles.append('Phase CH{} * CH{}'.format(pair[0], pair[1]))
743
205
744 self.saveTime = self.max_time
745
746
206
747 class PlotSpectraMeanData(PlotSpectraData):
207 class SpectraMeanPlot(SpectraPlot):
748 '''
208 '''
749 Plot for Spectra and Mean
209 Plot for Spectra and Mean
750 '''
210 '''
@@ -752,7 +212,7 class PlotSpectraMeanData(PlotSpectraData):
752 colormap = 'jro'
212 colormap = 'jro'
753
213
754
214
755 class PlotRTIData(PlotData):
215 class RTIPlot(Plot):
756 '''
216 '''
757 Plot for RTI data
217 Plot for RTI data
758 '''
218 '''
@@ -771,7 +231,7 class PlotRTIData(PlotData):
771 self.CODE.upper(), x) for x in range(self.nrows)]
231 self.CODE.upper(), x) for x in range(self.nrows)]
772
232
773 def plot(self):
233 def plot(self):
774 self.x = self.times
234 self.x = self.data.times
775 self.y = self.data.heights
235 self.y = self.data.heights
776 self.z = self.data[self.CODE]
236 self.z = self.data[self.CODE]
777 self.z = numpy.ma.masked_invalid(self.z)
237 self.z = numpy.ma.masked_invalid(self.z)
@@ -781,7 +241,7 class PlotRTIData(PlotData):
781 else:
241 else:
782 x, y, z = self.fill_gaps(*self.decimate())
242 x, y, z = self.fill_gaps(*self.decimate())
783
243
784 for n, ax in enumerate(self.axes):
244 for n, ax in enumerate(self.axes):
785 self.zmin = self.zmin if self.zmin else numpy.min(self.z)
245 self.zmin = self.zmin if self.zmin else numpy.min(self.z)
786 self.zmax = self.zmax if self.zmax else numpy.max(self.z)
246 self.zmax = self.zmax if self.zmax else numpy.max(self.z)
787 if ax.firsttime:
247 if ax.firsttime:
@@ -807,10 +267,8 class PlotRTIData(PlotData):
807 ax.plot_noise.set_data(numpy.repeat(
267 ax.plot_noise.set_data(numpy.repeat(
808 self.data['noise'][n][-1], len(self.y)), self.y)
268 self.data['noise'][n][-1], len(self.y)), self.y)
809
269
810 self.saveTime = self.min_time
811
812
270
813 class PlotCOHData(PlotRTIData):
271 class CoherencePlot(RTIPlot):
814 '''
272 '''
815 Plot for Coherence data
273 Plot for Coherence data
816 '''
274 '''
@@ -833,7 +291,7 class PlotCOHData(PlotRTIData):
833 'Phase Map Ch{} * Ch{}'.format(x[0], x[1]) for x in self.data.pairs]
291 'Phase Map Ch{} * Ch{}'.format(x[0], x[1]) for x in self.data.pairs]
834
292
835
293
836 class PlotPHASEData(PlotCOHData):
294 class PhasePlot(CoherencePlot):
837 '''
295 '''
838 Plot for Phase map data
296 Plot for Phase map data
839 '''
297 '''
@@ -842,7 +300,7 class PlotPHASEData(PlotCOHData):
842 colormap = 'seismic'
300 colormap = 'seismic'
843
301
844
302
845 class PlotNoiseData(PlotData):
303 class NoisePlot(Plot):
846 '''
304 '''
847 Plot for noise
305 Plot for noise
848 '''
306 '''
@@ -860,8 +318,8 class PlotNoiseData(PlotData):
860
318
861 def plot(self):
319 def plot(self):
862
320
863 x = self.times
321 x = self.data.times
864 xmin = self.min_time
322 xmin = self.data.min_time
865 xmax = xmin + self.xrange * 60 * 60
323 xmax = xmin + self.xrange * 60 * 60
866 Y = self.data[self.CODE]
324 Y = self.data[self.CODE]
867
325
@@ -877,10 +335,9 class PlotNoiseData(PlotData):
877
335
878 self.ymin = numpy.nanmin(Y) - 5
336 self.ymin = numpy.nanmin(Y) - 5
879 self.ymax = numpy.nanmax(Y) + 5
337 self.ymax = numpy.nanmax(Y) + 5
880 self.saveTime = self.min_time
881
338
882
339
883 class PlotSNRData(PlotRTIData):
340 class SnrPlot(RTIPlot):
884 '''
341 '''
885 Plot for SNR Data
342 Plot for SNR Data
886 '''
343 '''
@@ -889,7 +346,7 class PlotSNRData(PlotRTIData):
889 colormap = 'jet'
346 colormap = 'jet'
890
347
891
348
892 class PlotDOPData(PlotRTIData):
349 class DopplerPlot(RTIPlot):
893 '''
350 '''
894 Plot for DOPPLER Data
351 Plot for DOPPLER Data
895 '''
352 '''
@@ -898,7 +355,7 class PlotDOPData(PlotRTIData):
898 colormap = 'jet'
355 colormap = 'jet'
899
356
900
357
901 class PlotSkyMapData(PlotData):
358 class SkyMapPlot(Plot):
902 '''
359 '''
903 Plot for meteors detection data
360 Plot for meteors detection data
904 '''
361 '''
@@ -938,16 +395,15 class PlotSkyMapData(PlotData):
938 else:
395 else:
939 ax.plot.set_data(x, y)
396 ax.plot.set_data(x, y)
940
397
941 dt1 = self.getDateTime(self.min_time).strftime('%y/%m/%d %H:%M:%S')
398 dt1 = self.getDateTime(self.data.min_time).strftime('%y/%m/%d %H:%M:%S')
942 dt2 = self.getDateTime(self.max_time).strftime('%y/%m/%d %H:%M:%S')
399 dt2 = self.getDateTime(self.data.max_time).strftime('%y/%m/%d %H:%M:%S')
943 title = 'Meteor Detection Sky Map\n %s - %s \n Number of events: %5.0f\n' % (dt1,
400 title = 'Meteor Detection Sky Map\n %s - %s \n Number of events: %5.0f\n' % (dt1,
944 dt2,
401 dt2,
945 len(x))
402 len(x))
946 self.titles[0] = title
403 self.titles[0] = title
947 self.saveTime = self.max_time
948
404
949
405
950 class PlotParamData(PlotRTIData):
406 class ParametersPlot(RTIPlot):
951 '''
407 '''
952 Plot for data_param object
408 Plot for data_param object
953 '''
409 '''
@@ -973,7 +429,7 class PlotParamData(PlotRTIData):
973
429
974 def plot(self):
430 def plot(self):
975 self.data.normalize_heights()
431 self.data.normalize_heights()
976 self.x = self.times
432 self.x = self.data.times
977 self.y = self.data.heights
433 self.y = self.data.heights
978 if self.showSNR:
434 if self.showSNR:
979 self.z = numpy.concatenate(
435 self.z = numpy.concatenate(
@@ -990,7 +446,7 class PlotParamData(PlotRTIData):
990 x, y, z = self.fill_gaps(*self.decimate())
446 x, y, z = self.fill_gaps(*self.decimate())
991
447
992 for n, ax in enumerate(self.axes):
448 for n, ax in enumerate(self.axes):
993
449
994 self.zmax = self.zmax if self.zmax is not None else numpy.max(
450 self.zmax = self.zmax if self.zmax is not None else numpy.max(
995 self.z[n])
451 self.z[n])
996 self.zmin = self.zmin if self.zmin is not None else numpy.min(
452 self.zmin = self.zmin if self.zmin is not None else numpy.min(
@@ -1015,10 +471,8 class PlotParamData(PlotRTIData):
1015 cmap=self.cmaps[n]
471 cmap=self.cmaps[n]
1016 )
472 )
1017
473
1018 self.saveTime = self.min_time
1019
1020
474
1021 class PlotOutputData(PlotParamData):
475 class OutputPlot(ParametersPlot):
1022 '''
476 '''
1023 Plot data_output object
477 Plot data_output object
1024 '''
478 '''
@@ -1027,9 +481,9 class PlotOutputData(PlotParamData):
1027 colormap = 'seismic'
481 colormap = 'seismic'
1028
482
1029
483
1030 class PlotPolarMapData(PlotData):
484 class PolarMapPlot(Plot):
1031 '''
485 '''
1032 Plot for meteors detection data
486 Plot for weather radar
1033 '''
487 '''
1034
488
1035 CODE = 'param'
489 CODE = 'param'
@@ -1058,20 +512,24 class PlotPolarMapData(PlotData):
1058 self.cb_labels = self.data.meta['units']
512 self.cb_labels = self.data.meta['units']
1059 self.lat = self.data.meta['latitude']
513 self.lat = self.data.meta['latitude']
1060 self.lon = self.data.meta['longitude']
514 self.lon = self.data.meta['longitude']
1061 self.xmin, self.xmax = float(km2deg(self.xmin) + self.lon), float(km2deg(self.xmax) + self.lon)
515 self.xmin, self.xmax = float(
1062 self.ymin, self.ymax = float(km2deg(self.ymin) + self.lat), float(km2deg(self.ymax) + self.lat)
516 km2deg(self.xmin) + self.lon), float(km2deg(self.xmax) + self.lon)
517 self.ymin, self.ymax = float(
518 km2deg(self.ymin) + self.lat), float(km2deg(self.ymax) + self.lat)
1063 # self.polar = True
519 # self.polar = True
1064
520
1065 def plot(self):
521 def plot(self):
1066
522
1067 for n, ax in enumerate(self.axes):
523 for n, ax in enumerate(self.axes):
1068 data = self.data['param'][self.channels[n]]
524 data = self.data['param'][self.channels[n]]
1069
525
1070 zeniths = numpy.linspace(0, self.data.meta['max_range'], data.shape[1])
526 zeniths = numpy.linspace(
1071 if self.mode == 'E':
527 0, self.data.meta['max_range'], data.shape[1])
528 if self.mode == 'E':
1072 azimuths = -numpy.radians(self.data.heights)+numpy.pi/2
529 azimuths = -numpy.radians(self.data.heights)+numpy.pi/2
1073 r, theta = numpy.meshgrid(zeniths, azimuths)
530 r, theta = numpy.meshgrid(zeniths, azimuths)
1074 x, y = r*numpy.cos(theta)*numpy.cos(numpy.radians(self.data.meta['elevation'])), r*numpy.sin(theta)*numpy.cos(numpy.radians(self.data.meta['elevation']))
531 x, y = r*numpy.cos(theta)*numpy.cos(numpy.radians(self.data.meta['elevation'])), r*numpy.sin(
532 theta)*numpy.cos(numpy.radians(self.data.meta['elevation']))
1075 x = km2deg(x) + self.lon
533 x = km2deg(x) + self.lon
1076 y = km2deg(y) + self.lat
534 y = km2deg(y) + self.lat
1077 else:
535 else:
@@ -1083,35 +541,36 class PlotPolarMapData(PlotData):
1083 if ax.firsttime:
541 if ax.firsttime:
1084 if self.zlimits is not None:
542 if self.zlimits is not None:
1085 self.zmin, self.zmax = self.zlimits[n]
543 self.zmin, self.zmax = self.zlimits[n]
1086 ax.plt = ax.pcolormesh(#r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
544 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
1087 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
545 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
1088 vmin=self.zmin,
546 vmin=self.zmin,
1089 vmax=self.zmax,
547 vmax=self.zmax,
1090 cmap=self.cmaps[n])
548 cmap=self.cmaps[n])
1091 else:
549 else:
1092 if self.zlimits is not None:
550 if self.zlimits is not None:
1093 self.zmin, self.zmax = self.zlimits[n]
551 self.zmin, self.zmax = self.zlimits[n]
1094 ax.collections.remove(ax.collections[0])
552 ax.collections.remove(ax.collections[0])
1095 ax.plt = ax.pcolormesh(# r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
553 ax.plt = ax.pcolormesh( # r, theta, numpy.ma.array(data, mask=numpy.isnan(data)),
1096 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
554 x, y, numpy.ma.array(data, mask=numpy.isnan(data)),
1097 vmin=self.zmin,
555 vmin=self.zmin,
1098 vmax=self.zmax,
556 vmax=self.zmax,
1099 cmap=self.cmaps[n])
557 cmap=self.cmaps[n])
1100
558
1101 if self.mode == 'A':
559 if self.mode == 'A':
1102 continue
560 continue
1103
561
1104 # plot district names
562 # plot district names
1105 f = open('/data/workspace/schain_scripts/distrito.csv')
563 f = open('/data/workspace/schain_scripts/distrito.csv')
1106 for line in f:
564 for line in f:
1107 label, lon, lat = [s.strip() for s in line.split(',') if s]
565 label, lon, lat = [s.strip() for s in line.split(',') if s]
1108 lat = float(lat)
566 lat = float(lat)
1109 lon = float(lon)
567 lon = float(lon)
1110 # ax.plot(lon, lat, '.b', ms=2)
568 # ax.plot(lon, lat, '.b', ms=2)
1111 ax.text(lon, lat, label.decode('utf8'), ha='center', va='bottom', size='8', color='black')
569 ax.text(lon, lat, label.decode('utf8'), ha='center',
1112
570 va='bottom', size='8', color='black')
571
1113 # plot limites
572 # plot limites
1114 limites =[]
573 limites = []
1115 tmp = []
574 tmp = []
1116 for line in open('/data/workspace/schain_scripts/lima.csv'):
575 for line in open('/data/workspace/schain_scripts/lima.csv'):
1117 if '#' in line:
576 if '#' in line:
@@ -1122,7 +581,8 class PlotPolarMapData(PlotData):
1122 values = line.strip().split(',')
581 values = line.strip().split(',')
1123 tmp.append((float(values[0]), float(values[1])))
582 tmp.append((float(values[0]), float(values[1])))
1124 for points in limites:
583 for points in limites:
1125 ax.add_patch(Polygon(points, ec='k', fc='none', ls='--', lw=0.5))
584 ax.add_patch(
585 Polygon(points, ec='k', fc='none', ls='--', lw=0.5))
1126
586
1127 # plot Cuencas
587 # plot Cuencas
1128 for cuenca in ('rimac', 'lurin', 'mala', 'chillon', 'chilca', 'chancay-huaral'):
588 for cuenca in ('rimac', 'lurin', 'mala', 'chillon', 'chilca', 'chancay-huaral'):
@@ -1133,22 +593,21 class PlotPolarMapData(PlotData):
1133
593
1134 # plot grid
594 # plot grid
1135 for r in (15, 30, 45, 60):
595 for r in (15, 30, 45, 60):
1136 ax.add_artist(plt.Circle((self.lon, self.lat), km2deg(r), color='0.6', fill=False, lw=0.2))
596 ax.add_artist(plt.Circle((self.lon, self.lat),
597 km2deg(r), color='0.6', fill=False, lw=0.2))
1137 ax.text(
598 ax.text(
1138 self.lon + (km2deg(r))*numpy.cos(60*numpy.pi/180),
599 self.lon + (km2deg(r))*numpy.cos(60*numpy.pi/180),
1139 self.lat + (km2deg(r))*numpy.sin(60*numpy.pi/180),
600 self.lat + (km2deg(r))*numpy.sin(60*numpy.pi/180),
1140 '{}km'.format(r),
601 '{}km'.format(r),
1141 ha='center', va='bottom', size='8', color='0.6', weight='heavy')
602 ha='center', va='bottom', size='8', color='0.6', weight='heavy')
1142
603
1143 if self.mode == 'E':
604 if self.mode == 'E':
1144 title = 'El={}$^\circ$'.format(self.data.meta['elevation'])
605 title = 'El={}$^\circ$'.format(self.data.meta['elevation'])
1145 label = 'E{:02d}'.format(int(self.data.meta['elevation']))
606 label = 'E{:02d}'.format(int(self.data.meta['elevation']))
1146 else:
607 else:
1147 title = 'Az={}$^\circ$'.format(self.data.meta['azimuth'])
608 title = 'Az={}$^\circ$'.format(self.data.meta['azimuth'])
1148 label = 'A{:02d}'.format(int(self.data.meta['azimuth']))
609 label = 'A{:02d}'.format(int(self.data.meta['azimuth']))
1149
1150 self.save_labels = ['{}-{}'.format(lbl, label) for lbl in self.labels]
1151 self.titles = ['{} {}'.format(self.data.parameters[x], title) for x in self.channels]
1152 self.saveTime = self.max_time
1153
610
1154 No newline at end of file
611 self.save_labels = ['{}-{}'.format(lbl, label) for lbl in self.labels]
612 self.titles = ['{} {}'.format(
613 self.data.parameters[x], title) for x in self.channels]
@@ -10,7 +10,7 import numpy
10 from .figure import Figure, isRealtime
10 from .figure import Figure, isRealtime
11 from .plotting_codes import *
11 from .plotting_codes import *
12
12
13 class SpectraHeisScope(Figure):
13 class SpectraHeisScope_(Figure):
14
14
15
15
16 isConfig = None
16 isConfig = None
@@ -173,7 +173,7 class SpectraHeisScope(Figure):
173 wr_period=wr_period,
173 wr_period=wr_period,
174 thisDatetime=thisDatetime)
174 thisDatetime=thisDatetime)
175
175
176 class RTIfromSpectraHeis(Figure):
176 class RTIfromSpectraHeis_(Figure):
177
177
178 isConfig = None
178 isConfig = None
179 __nsubplots = None
179 __nsubplots = None
@@ -7,7 +7,7 from .plotting_codes import *
7 from schainpy.model.proc.jroproc_base import MPDecorator
7 from schainpy.model.proc.jroproc_base import MPDecorator
8 from schainpy.utils import log
8 from schainpy.utils import log
9
9
10 class FitGauPlot(Figure):
10 class FitGauPlot_(Figure):
11
11
12 isConfig = None
12 isConfig = None
13 __nsubplots = None
13 __nsubplots = None
@@ -218,7 +218,7 class FitGauPlot(Figure):
218
218
219
219
220
220
221 class MomentsPlot(Figure):
221 class MomentsPlot_(Figure):
222
222
223 isConfig = None
223 isConfig = None
224 __nsubplots = None
224 __nsubplots = None
@@ -405,7 +405,7 class MomentsPlot(Figure):
405 thisDatetime=thisDatetime)
405 thisDatetime=thisDatetime)
406
406
407
407
408 class SkyMapPlot(Figure):
408 class SkyMapPlot_(Figure):
409
409
410 __isConfig = None
410 __isConfig = None
411 __nsubplots = None
411 __nsubplots = None
@@ -561,7 +561,7 class SkyMapPlot(Figure):
561
561
562
562
563
563
564 class WindProfilerPlot(Figure):
564 class WindProfilerPlot_(Figure):
565
565
566 __isConfig = None
566 __isConfig = None
567 __nsubplots = None
567 __nsubplots = None
@@ -774,7 +774,7 class WindProfilerPlot(Figure):
774 update_figfile = True
774 update_figfile = True
775
775
776 @MPDecorator
776 @MPDecorator
777 class ParametersPlot(Figure):
777 class ParametersPlot_(Figure):
778
778
779 __isConfig = None
779 __isConfig = None
780 __nsubplots = None
780 __nsubplots = None
@@ -986,7 +986,7 class ParametersPlot(Figure):
986
986
987 return dataOut
987 return dataOut
988 @MPDecorator
988 @MPDecorator
989 class Parameters1Plot(Figure):
989 class Parameters1Plot_(Figure):
990
990
991 __isConfig = None
991 __isConfig = None
992 __nsubplots = None
992 __nsubplots = None
@@ -1237,7 +1237,7 class Parameters1Plot(Figure):
1237 update_figfile=False)
1237 update_figfile=False)
1238 return dataOut
1238 return dataOut
1239
1239
1240 class SpectralFittingPlot(Figure):
1240 class SpectralFittingPlot_(Figure):
1241
1241
1242 __isConfig = None
1242 __isConfig = None
1243 __nsubplots = None
1243 __nsubplots = None
@@ -1415,7 +1415,7 class SpectralFittingPlot(Figure):
1415 thisDatetime=thisDatetime)
1415 thisDatetime=thisDatetime)
1416
1416
1417
1417
1418 class EWDriftsPlot(Figure):
1418 class EWDriftsPlot_(Figure):
1419
1419
1420 __isConfig = None
1420 __isConfig = None
1421 __nsubplots = None
1421 __nsubplots = None
@@ -1621,7 +1621,7 class EWDriftsPlot(Figure):
1621
1621
1622
1622
1623
1623
1624 class PhasePlot(Figure):
1624 class PhasePlot_(Figure):
1625
1625
1626 __isConfig = None
1626 __isConfig = None
1627 __nsubplots = None
1627 __nsubplots = None
@@ -1785,7 +1785,7 class PhasePlot(Figure):
1785
1785
1786
1786
1787
1787
1788 class NSMeteorDetection1Plot(Figure):
1788 class NSMeteorDetection1Plot_(Figure):
1789
1789
1790 isConfig = None
1790 isConfig = None
1791 __nsubplots = None
1791 __nsubplots = None
@@ -1969,7 +1969,7 class NSMeteorDetection1Plot(Figure):
1969 thisDatetime=thisDatetime)
1969 thisDatetime=thisDatetime)
1970
1970
1971
1971
1972 class NSMeteorDetection2Plot(Figure):
1972 class NSMeteorDetection2Plot_(Figure):
1973
1973
1974 isConfig = None
1974 isConfig = None
1975 __nsubplots = None
1975 __nsubplots = None
@@ -14,7 +14,7 from schainpy.model.proc.jroproc_base import MPDecorator
14 from schainpy.utils import log
14 from schainpy.utils import log
15
15
16 @MPDecorator
16 @MPDecorator
17 class SpectraPlot(Figure):
17 class SpectraPlot_(Figure):
18
18
19 isConfig = None
19 isConfig = None
20 __nsubplots = None
20 __nsubplots = None
@@ -226,7 +226,7 class SpectraPlot(Figure):
226
226
227 return dataOut
227 return dataOut
228 @MPDecorator
228 @MPDecorator
229 class CrossSpectraPlot(Figure):
229 class CrossSpectraPlot_(Figure):
230
230
231 isConfig = None
231 isConfig = None
232 __nsubplots = None
232 __nsubplots = None
@@ -453,7 +453,7 class CrossSpectraPlot(Figure):
453 return dataOut
453 return dataOut
454
454
455 @MPDecorator
455 @MPDecorator
456 class RTIPlot(Figure):
456 class RTIPlot_(Figure):
457
457
458 __isConfig = None
458 __isConfig = None
459 __nsubplots = None
459 __nsubplots = None
@@ -667,7 +667,7 class RTIPlot(Figure):
667 return dataOut
667 return dataOut
668
668
669 @MPDecorator
669 @MPDecorator
670 class CoherenceMap(Figure):
670 class CoherenceMap_(Figure):
671 isConfig = None
671 isConfig = None
672 __nsubplots = None
672 __nsubplots = None
673
673
@@ -878,7 +878,7 class CoherenceMap(Figure):
878 return dataOut
878 return dataOut
879
879
880 @MPDecorator
880 @MPDecorator
881 class PowerProfilePlot(Figure):
881 class PowerProfilePlot_(Figure):
882
882
883 isConfig = None
883 isConfig = None
884 __nsubplots = None
884 __nsubplots = None
@@ -1008,7 +1008,7 class PowerProfilePlot(Figure):
1008 return dataOut
1008 return dataOut
1009
1009
1010 @MPDecorator
1010 @MPDecorator
1011 class SpectraCutPlot(Figure):
1011 class SpectraCutPlot_(Figure):
1012
1012
1013 isConfig = None
1013 isConfig = None
1014 __nsubplots = None
1014 __nsubplots = None
@@ -1145,7 +1145,7 class SpectraCutPlot(Figure):
1145 return dataOut
1145 return dataOut
1146
1146
1147 @MPDecorator
1147 @MPDecorator
1148 class Noise(Figure):
1148 class Noise_(Figure):
1149
1149
1150 isConfig = None
1150 isConfig = None
1151 __nsubplots = None
1151 __nsubplots = None
@@ -1352,7 +1352,7 class Noise(Figure):
1352 return dataOut
1352 return dataOut
1353
1353
1354 @MPDecorator
1354 @MPDecorator
1355 class BeaconPhase(Figure):
1355 class BeaconPhase_(Figure):
1356
1356
1357 __isConfig = None
1357 __isConfig = None
1358 __nsubplots = None
1358 __nsubplots = None
@@ -12,7 +12,7 from .figure import Figure
12
12
13
13
14 @MPDecorator
14 @MPDecorator
15 class Scope(Figure):
15 class Scope_(Figure):
16
16
17 isConfig = None
17 isConfig = None
18
18
@@ -11,13 +11,15 Based on:
11 $Author: murco $
11 $Author: murco $
12 $Id: jroproc_base.py 1 2012-11-12 18:56:07Z murco $
12 $Id: jroproc_base.py 1 2012-11-12 18:56:07Z murco $
13 '''
13 '''
14 from platform import python_version
14
15 import inspect
15 import inspect
16 import zmq
16 import zmq
17 import time
17 import time
18 import pickle
18 import pickle
19 import os
19 import os
20 from multiprocessing import Process
20 from multiprocessing import Process
21 from zmq.utils.monitor import recv_monitor_message
22
21 from schainpy.utils import log
23 from schainpy.utils import log
22
24
23
25
@@ -35,15 +37,6 class ProcessingUnit(object):
35
37
36
38
37 """
39 """
38
39 METHODS = {}
40 dataIn = None
41 dataInList = []
42 id = None
43 inputId = None
44 dataOut = None
45 dictProcs = None
46 isConfig = False
47
40
48 def __init__(self):
41 def __init__(self):
49
42
@@ -51,15 +44,15 class ProcessingUnit(object):
51 self.dataOut = None
44 self.dataOut = None
52 self.isConfig = False
45 self.isConfig = False
53 self.operations = []
46 self.operations = []
47 self.plots = []
54
48
55 def getAllowedArgs(self):
49 def getAllowedArgs(self):
56 if hasattr(self, '__attrs__'):
50 if hasattr(self, '__attrs__'):
57 return self.__attrs__
51 return self.__attrs__
58 else:
52 else:
59 return inspect.getargspec(self.run).args
53 return inspect.getargspec(self.run).args
60
61 def addOperation(self, conf, operation):
62
54
55 def addOperation(self, conf, operation):
63 """
56 """
64 This method is used in the controller, and update the dictionary containing the operations to execute. The dict
57 This method is used in the controller, and update the dictionary containing the operations to execute. The dict
65 posses the id of the operation process (IPC purposes)
58 posses the id of the operation process (IPC purposes)
@@ -76,7 +69,11 class ProcessingUnit(object):
76 objId : identificador del objeto, necesario para comunicar con master(procUnit)
69 objId : identificador del objeto, necesario para comunicar con master(procUnit)
77 """
70 """
78
71
79 self.operations.append((operation, conf.type, conf.id, conf.getKwargs()))
72 self.operations.append(
73 (operation, conf.type, conf.id, conf.getKwargs()))
74
75 if 'plot' in self.name.lower():
76 self.plots.append(operation.CODE)
80
77
81 def getOperationObj(self, objId):
78 def getOperationObj(self, objId):
82
79
@@ -86,7 +83,6 class ProcessingUnit(object):
86 return self.operations[objId]
83 return self.operations[objId]
87
84
88 def operation(self, **kwargs):
85 def operation(self, **kwargs):
89
90 """
86 """
91 Operacion directa sobre la data (dataOut.data). Es necesario actualizar los valores de los
87 Operacion directa sobre la data (dataOut.data). Es necesario actualizar los valores de los
92 atributos del objeto dataOut
88 atributos del objeto dataOut
@@ -96,7 +92,7 class ProcessingUnit(object):
96 **kwargs : Diccionario de argumentos de la funcion a ejecutar
92 **kwargs : Diccionario de argumentos de la funcion a ejecutar
97 """
93 """
98
94
99 raise NotImplementedError
95 raise NotImplementedError
100
96
101 def setup(self):
97 def setup(self):
102
98
@@ -107,9 +103,10 class ProcessingUnit(object):
107 raise NotImplementedError
103 raise NotImplementedError
108
104
109 def close(self):
105 def close(self):
110 #Close every thread, queue or any other object here is it is neccesary.
106
111 return
107 return
112
108
109
113 class Operation(object):
110 class Operation(object):
114
111
115 """
112 """
@@ -126,22 +123,15 class Operation(object):
126 Ejemplo: Integraciones coherentes, necesita la informacion previa de los n perfiles anteriores (bufffer)
123 Ejemplo: Integraciones coherentes, necesita la informacion previa de los n perfiles anteriores (bufffer)
127
124
128 """
125 """
129 id = None
130 __buffer = None
131 dest = None
132 isConfig = False
133 readyFlag = None
134
126
135 def __init__(self):
127 def __init__(self):
136
128
137 self.buffer = None
129 self.id = None
138 self.dest = None
139 self.isConfig = False
130 self.isConfig = False
140 self.readyFlag = False
141
131
142 if not hasattr(self, 'name'):
132 if not hasattr(self, 'name'):
143 self.name = self.__class__.__name__
133 self.name = self.__class__.__name__
144
134
145 def getAllowedArgs(self):
135 def getAllowedArgs(self):
146 if hasattr(self, '__attrs__'):
136 if hasattr(self, '__attrs__'):
147 return self.__attrs__
137 return self.__attrs__
@@ -154,9 +144,7 class Operation(object):
154
144
155 raise NotImplementedError
145 raise NotImplementedError
156
146
157
158 def run(self, dataIn, **kwargs):
147 def run(self, dataIn, **kwargs):
159
160 """
148 """
161 Realiza las operaciones necesarias sobre la dataIn.data y actualiza los
149 Realiza las operaciones necesarias sobre la dataIn.data y actualiza los
162 atributos del objeto dataIn.
150 atributos del objeto dataIn.
@@ -180,18 +168,17 class Operation(object):
180
168
181 def close(self):
169 def close(self):
182
170
183 pass
171 return
184
172
185
173
186 def MPDecorator(BaseClass):
174 def MPDecorator(BaseClass):
187
188 """
175 """
189 Multiprocessing class decorator
176 Multiprocessing class decorator
190
177
191 This function add multiprocessing features to a BaseClass. Also, it handle
178 This function add multiprocessing features to a BaseClass. Also, it handle
192 the communication beetween processes (readers, procUnits and operations).
179 the communication beetween processes (readers, procUnits and operations).
193 """
180 """
194
181
195 class MPClass(BaseClass, Process):
182 class MPClass(BaseClass, Process):
196
183
197 def __init__(self, *args, **kwargs):
184 def __init__(self, *args, **kwargs):
@@ -203,42 +190,38 def MPDecorator(BaseClass):
203 self.sender = None
190 self.sender = None
204 self.receiver = None
191 self.receiver = None
205 self.name = BaseClass.__name__
192 self.name = BaseClass.__name__
193 self.start_time = time.time()
206
194
207 if len(self.args) is 3:
195 if len(self.args) is 3:
208 self.typeProc = "ProcUnit"
196 self.typeProc = "ProcUnit"
209 self.id = args[0]
197 self.id = args[0]
210 self.inputId = args[1]
198 self.inputId = args[1]
211 self.project_id = args[2]
199 self.project_id = args[2]
212 else:
200 elif len(self.args) is 2:
213 self.id = args[0]
201 self.id = args[0]
214 self.inputId = args[0]
202 self.inputId = args[0]
215 self.project_id = args[1]
203 self.project_id = args[1]
216 self.typeProc = "Operation"
204 self.typeProc = "Operation"
217
205
218 def getAllowedArgs(self):
219
220 if hasattr(self, '__attrs__'):
221 return self.__attrs__
222 else:
223 return inspect.getargspec(BaseClass.run).args
224
225 def subscribe(self):
206 def subscribe(self):
226 '''
207 '''
227 This function create a socket to receive objects from the
208 This function create a socket to receive objects from the
228 topic `inputId`.
209 topic `inputId`.
229 '''
210 '''
230
211
231 c = zmq.Context()
212 c = zmq.Context()
232 self.receiver = c.socket(zmq.SUB)
213 self.receiver = c.socket(zmq.SUB)
233 self.receiver.connect('ipc:///tmp/schain/{}_pub'.format(self.project_id))
214 self.receiver.connect(
215 'ipc:///tmp/schain/{}_pub'.format(self.project_id))
234 self.receiver.setsockopt(zmq.SUBSCRIBE, self.inputId.encode())
216 self.receiver.setsockopt(zmq.SUBSCRIBE, self.inputId.encode())
235
217
236 def listen(self):
218 def listen(self):
237 '''
219 '''
238 This function waits for objects and deserialize using pickle
220 This function waits for objects and deserialize using pickle
239 '''
221 '''
240
222
241 data = pickle.loads(self.receiver.recv_multipart()[1])
223 data = pickle.loads(self.receiver.recv_multipart()[1])
224
242 return data
225 return data
243
226
244 def set_publisher(self):
227 def set_publisher(self):
@@ -248,14 +231,14 def MPDecorator(BaseClass):
248
231
249 time.sleep(1)
232 time.sleep(1)
250 c = zmq.Context()
233 c = zmq.Context()
251 self.sender = c.socket(zmq.PUB)
234 self.sender = c.socket(zmq.PUB)
252 self.sender.connect('ipc:///tmp/schain/{}_sub'.format(self.project_id))
235 self.sender.connect(
236 'ipc:///tmp/schain/{}_sub'.format(self.project_id))
253
237
254 def publish(self, data, id):
238 def publish(self, data, id):
255 '''
239 '''
256 This function publish an object, to a specific topic.
240 This function publish an object, to a specific topic.
257 '''
241 '''
258
259 self.sender.send_multipart([str(id).encode(), pickle.dumps(data)])
242 self.sender.send_multipart([str(id).encode(), pickle.dumps(data)])
260
243
261 def runReader(self):
244 def runReader(self):
@@ -263,28 +246,32 def MPDecorator(BaseClass):
263 Run fuction for read units
246 Run fuction for read units
264 '''
247 '''
265 while True:
248 while True:
266
267 BaseClass.run(self, **self.kwargs)
268
249
269 if self.dataOut.error[0] == -1:
250 BaseClass.run(self, **self.kwargs)
270 log.error(self.dataOut.error[1])
271 self.publish('end', self.id)
272 #self.sender.send_multipart([str(self.project_id).encode(), 'end'.encode()])
273 break
274
251
275 for op, optype, id, kwargs in self.operations:
252 for op, optype, opId, kwargs in self.operations:
276 if optype=='self':
253 if optype == 'self':
277 op(**kwargs)
254 op(**kwargs)
278 elif optype=='other':
255 elif optype == 'other':
279 self.dataOut = op.run(self.dataOut, **self.kwargs)
256 self.dataOut = op.run(self.dataOut, **self.kwargs)
280 elif optype=='external':
257 elif optype == 'external':
281 self.publish(self.dataOut, opId)
258 self.publish(self.dataOut, opId)
282
259
283 if self.dataOut.flagNoData:
260 if self.dataOut.flagNoData and self.dataOut.error is None:
284 continue
261 continue
285
262
286 self.publish(self.dataOut, self.id)
263 self.publish(self.dataOut, self.id)
287
264
265 if self.dataOut.error:
266 if self.dataOut.error[0] == -1:
267 log.error(self.dataOut.error[1], self.name)
268 if self.dataOut.error[0] == 1:
269 log.success(self.dataOut.error[1], self.name)
270 # self.sender.send_multipart([str(self.project_id).encode(), 'end'.encode()])
271 break
272
273 time.sleep(1)
274
288 def runProc(self):
275 def runProc(self):
289 '''
276 '''
290 Run function for proccessing units
277 Run function for proccessing units
@@ -293,49 +280,45 def MPDecorator(BaseClass):
293 while True:
280 while True:
294 self.dataIn = self.listen()
281 self.dataIn = self.listen()
295
282
296 if self.dataIn == 'end':
283 if self.dataIn.flagNoData and self.dataIn.error is None:
297 self.publish('end', self.id)
298 for op, optype, opId, kwargs in self.operations:
299 if optype == 'external':
300 self.publish('end', opId)
301 break
302
303 if self.dataIn.flagNoData:
304 continue
284 continue
305
285
306 BaseClass.run(self, **self.kwargs)
286 BaseClass.run(self, **self.kwargs)
307
287
308 for op, optype, opId, kwargs in self.operations:
288 for op, optype, opId, kwargs in self.operations:
309 if optype=='self':
289 if optype == 'self':
310 op(**kwargs)
290 op(**kwargs)
311 elif optype=='other':
291 elif optype == 'other':
312 self.dataOut = op.run(self.dataOut, **kwargs)
292 self.dataOut = op.run(self.dataOut, **kwargs)
313 elif optype=='external':
293 elif optype == 'external':
314 self.publish(self.dataOut, opId)
294 self.publish(self.dataOut, opId)
315
295
316 if self.dataOut.flagNoData:
317 continue
318
319 self.publish(self.dataOut, self.id)
296 self.publish(self.dataOut, self.id)
297 if self.dataIn.error:
298 break
299
300 time.sleep(1)
320
301
321 def runOp(self):
302 def runOp(self):
322 '''
303 '''
323 Run function for operations
304 Run function for external operations (this operations just receive data
305 ex: plots, writers, publishers)
324 '''
306 '''
325
307
326 while True:
308 while True:
327
309
328 dataOut = self.listen()
310 dataOut = self.listen()
329
311
330 if dataOut == 'end':
331 break
332
333 BaseClass.run(self, dataOut, **self.kwargs)
312 BaseClass.run(self, dataOut, **self.kwargs)
334
313
314 if dataOut.error:
315 break
316 time.sleep(1)
317
335 def run(self):
318 def run(self):
336
319
337 if self.typeProc is "ProcUnit":
320 if self.typeProc is "ProcUnit":
338
321
339 if self.inputId is not None:
322 if self.inputId is not None:
340 self.subscribe()
323 self.subscribe()
341 self.set_publisher()
324 self.set_publisher()
@@ -346,22 +329,48 def MPDecorator(BaseClass):
346 self.runReader()
329 self.runReader()
347
330
348 elif self.typeProc is "Operation":
331 elif self.typeProc is "Operation":
349
332
350 self.subscribe()
333 self.subscribe()
351 self.runOp()
334 self.runOp()
352
335
353 else:
336 else:
354 raise ValueError("Unknown type")
337 raise ValueError("Unknown type")
355
338
356 print("%s done" % BaseClass.__name__)
357 self.close()
339 self.close()
358
340
341 def event_monitor(self, monitor):
342
343 events = {}
344
345 for name in dir(zmq):
346 if name.startswith('EVENT_'):
347 value = getattr(zmq, name)
348 events[value] = name
349
350 while monitor.poll():
351 evt = recv_monitor_message(monitor)
352 if evt['event'] == 32:
353 self.connections += 1
354 if evt['event'] == 512:
355 pass
356
357 evt.update({'description': events[evt['event']]})
358
359 if evt['event'] == zmq.EVENT_MONITOR_STOPPED:
360 break
361 monitor.close()
362 print('event monitor thread done!')
363
359 def close(self):
364 def close(self):
360
365
366 BaseClass.close(self)
367
361 if self.sender:
368 if self.sender:
362 self.sender.close()
369 self.sender.close()
363
370
364 if self.receiver:
371 if self.receiver:
365 self.receiver.close()
372 self.receiver.close()
366
373
367 return MPClass No newline at end of file
374 log.success('Done...(Time:{:4.2f} secs)'.format(time.time()-self.start_time), self.name)
375
376 return MPClass
This diff has been collapsed as it changes many lines, (612 lines changed) Show them Hide them
@@ -7,11 +7,9 import glob
7 import time
7 import time
8 import json
8 import json
9 import numpy
9 import numpy
10 import paho.mqtt.client as mqtt
11 import zmq
10 import zmq
12 import datetime
11 import datetime
13 import ftplib
12 import ftplib
14 from zmq.utils.monitor import recv_monitor_message
15 from functools import wraps
13 from functools import wraps
16 from threading import Thread
14 from threading import Thread
17 from multiprocessing import Process
15 from multiprocessing import Process
@@ -54,428 +52,52 def get_plot_code(s):
54 else:
52 else:
55 return 24
53 return 24
56
54
57 def roundFloats(obj):
58 if isinstance(obj, list):
59 return list(map(roundFloats, obj))
60 elif isinstance(obj, float):
61 return round(obj, 2)
62
63 def decimate(z, MAXNUMY):
55 def decimate(z, MAXNUMY):
64 dy = int(len(z[0])/MAXNUMY) + 1
56 dy = int(len(z[0])/MAXNUMY) + 1
65
57
66 return z[::, ::dy]
58 return z[::, ::dy]
67
59
68 class throttle(object):
69 '''
70 Decorator that prevents a function from being called more than once every
71 time period.
72 To create a function that cannot be called more than once a minute, but
73 will sleep until it can be called:
74 @throttle(minutes=1)
75 def foo():
76 pass
77
78 for i in range(10):
79 foo()
80 print "This function has run %s times." % i
81 '''
82
83 def __init__(self, seconds=0, minutes=0, hours=0):
84 self.throttle_period = datetime.timedelta(
85 seconds=seconds, minutes=minutes, hours=hours
86 )
87
88 self.time_of_last_call = datetime.datetime.min
89
90 def __call__(self, fn):
91 @wraps(fn)
92 def wrapper(*args, **kwargs):
93 coerce = kwargs.pop('coerce', None)
94 if coerce:
95 self.time_of_last_call = datetime.datetime.now()
96 return fn(*args, **kwargs)
97 else:
98 now = datetime.datetime.now()
99 time_since_last_call = now - self.time_of_last_call
100 time_left = self.throttle_period - time_since_last_call
101
102 if time_left > datetime.timedelta(seconds=0):
103 return
104
105 self.time_of_last_call = datetime.datetime.now()
106 return fn(*args, **kwargs)
107
108 return wrapper
109
110 class Data(object):
111 '''
112 Object to hold data to be plotted
113 '''
114
115 def __init__(self, plottypes, throttle_value, exp_code, buffering=True):
116 self.plottypes = plottypes
117 self.throttle = throttle_value
118 self.exp_code = exp_code
119 self.buffering = buffering
120 self.ended = False
121 self.localtime = False
122 self.meta = {}
123 self.__times = []
124 self.__heights = []
125
126 def __str__(self):
127 dum = ['{}{}'.format(key, self.shape(key)) for key in self.data]
128 return 'Data[{}][{}]'.format(';'.join(dum), len(self.__times))
129
130 def __len__(self):
131 return len(self.__times)
132
133 def __getitem__(self, key):
134 if key not in self.data:
135 raise KeyError(log.error('Missing key: {}'.format(key)))
136
137 if 'spc' in key or not self.buffering:
138 ret = self.data[key]
139 else:
140 ret = numpy.array([self.data[key][x] for x in self.times])
141 if ret.ndim > 1:
142 ret = numpy.swapaxes(ret, 0, 1)
143 return ret
144
145 def __contains__(self, key):
146 return key in self.data
147
148 def setup(self):
149 '''
150 Configure object
151 '''
152
153 self.type = ''
154 self.ended = False
155 self.data = {}
156 self.__times = []
157 self.__heights = []
158 self.__all_heights = set()
159 for plot in self.plottypes:
160 if 'snr' in plot:
161 plot = 'snr'
162 self.data[plot] = {}
163
164 def shape(self, key):
165 '''
166 Get the shape of the one-element data for the given key
167 '''
168
169 if len(self.data[key]):
170 if 'spc' in key or not self.buffering:
171 return self.data[key].shape
172 return self.data[key][self.__times[0]].shape
173 return (0,)
174
175 def update(self, dataOut, tm):
176 '''
177 Update data object with new dataOut
178 '''
179
180 if tm in self.__times:
181 return
182
183 self.type = dataOut.type
184 self.parameters = getattr(dataOut, 'parameters', [])
185 if hasattr(dataOut, 'pairsList'):
186 self.pairs = dataOut.pairsList
187 if hasattr(dataOut, 'meta'):
188 self.meta = dataOut.meta
189 self.channels = dataOut.channelList
190 self.interval = dataOut.getTimeInterval()
191 self.localtime = dataOut.useLocalTime
192 if 'spc' in self.plottypes or 'cspc' in self.plottypes:
193 self.xrange = (dataOut.getFreqRange(1)/1000., dataOut.getAcfRange(1), dataOut.getVelRange(1))
194 self.__heights.append(dataOut.heightList)
195 self.__all_heights.update(dataOut.heightList)
196 self.__times.append(tm)
197
198 for plot in self.plottypes:
199 if plot == 'spc':
200 z = dataOut.data_spc/dataOut.normFactor
201 buffer = 10*numpy.log10(z)
202 if plot == 'cspc':
203 buffer = dataOut.data_cspc
204 if plot == 'noise':
205 buffer = 10*numpy.log10(dataOut.getNoise()/dataOut.normFactor)
206 if plot == 'rti':
207 buffer = dataOut.getPower()
208 if plot == 'snr_db':
209 buffer = dataOut.data_SNR
210 if plot == 'snr':
211 buffer = 10*numpy.log10(dataOut.data_SNR)
212 if plot == 'dop':
213 buffer = 10*numpy.log10(dataOut.data_DOP)
214 if plot == 'mean':
215 buffer = dataOut.data_MEAN
216 if plot == 'std':
217 buffer = dataOut.data_STD
218 if plot == 'coh':
219 buffer = dataOut.getCoherence()
220 if plot == 'phase':
221 buffer = dataOut.getCoherence(phase=True)
222 if plot == 'output':
223 buffer = dataOut.data_output
224 if plot == 'param':
225 buffer = dataOut.data_param
226
227 if 'spc' in plot:
228 self.data[plot] = buffer
229 else:
230 if self.buffering:
231 self.data[plot][tm] = buffer
232 else:
233 self.data[plot] = buffer
234
235 def normalize_heights(self):
236 '''
237 Ensure same-dimension of the data for different heighList
238 '''
239
240 H = numpy.array(list(self.__all_heights))
241 H.sort()
242 for key in self.data:
243 shape = self.shape(key)[:-1] + H.shape
244 for tm, obj in list(self.data[key].items()):
245 h = self.__heights[self.__times.index(tm)]
246 if H.size == h.size:
247 continue
248 index = numpy.where(numpy.in1d(H, h))[0]
249 dummy = numpy.zeros(shape) + numpy.nan
250 if len(shape) == 2:
251 dummy[:, index] = obj
252 else:
253 dummy[index] = obj
254 self.data[key][tm] = dummy
255
256 self.__heights = [H for tm in self.__times]
257
258 def jsonify(self, decimate=False):
259 '''
260 Convert data to json
261 '''
262
263 data = {}
264 tm = self.times[-1]
265 dy = int(self.heights.size/MAXNUMY) + 1
266 for key in self.data:
267 if key in ('spc', 'cspc') or not self.buffering:
268 dx = int(self.data[key].shape[1]/MAXNUMX) + 1
269 data[key] = roundFloats(self.data[key][::, ::dx, ::dy].tolist())
270 else:
271 data[key] = roundFloats(self.data[key][tm].tolist())
272
273 ret = {'data': data}
274 ret['exp_code'] = self.exp_code
275 ret['time'] = tm
276 ret['interval'] = self.interval
277 ret['localtime'] = self.localtime
278 ret['yrange'] = roundFloats(self.heights[::dy].tolist())
279 if 'spc' in self.data or 'cspc' in self.data:
280 ret['xrange'] = roundFloats(self.xrange[2][::dx].tolist())
281 else:
282 ret['xrange'] = []
283 if hasattr(self, 'pairs'):
284 ret['pairs'] = self.pairs
285 else:
286 ret['pairs'] = []
287
288 for key, value in list(self.meta.items()):
289 ret[key] = value
290
291 return json.dumps(ret)
292
293 @property
294 def times(self):
295 '''
296 Return the list of times of the current data
297 '''
298
299 ret = numpy.array(self.__times)
300 ret.sort()
301 return ret
302
303 @property
304 def heights(self):
305 '''
306 Return the list of heights of the current data
307 '''
308
309 return numpy.array(self.__heights[-1])
310
60
311 class PublishData(Operation):
61 class PublishData(Operation):
312 '''
62 '''
313 Operation to send data over zmq.
63 Operation to send data over zmq.
314 '''
64 '''
315
65
316 __attrs__ = ['host', 'port', 'delay', 'zeromq', 'mqtt', 'verbose']
66 __attrs__ = ['host', 'port', 'delay', 'verbose']
317
67
318 def __init__(self, **kwargs):
68 def __init__(self, **kwargs):
319 """Inicio."""
69 """Inicio."""
320 Operation.__init__(self, **kwargs)
70 Operation.__init__(self, **kwargs)
321 self.isConfig = False
71 self.isConfig = False
322 self.client = None
323 self.zeromq = None
324 self.mqtt = None
325
72
326 def on_disconnect(self, client, userdata, rc):
73 def setup(self, server='zmq.pipe', delay=0, verbose=True, **kwargs):
327 if rc != 0:
328 log.warning('Unexpected disconnection.')
329 self.connect()
330
331 def connect(self):
332 log.warning('trying to connect')
333 try:
334 self.client.connect(
335 host=self.host,
336 port=self.port,
337 keepalive=60*10,
338 bind_address='')
339 self.client.loop_start()
340 # self.client.publish(
341 # self.topic + 'SETUP',
342 # json.dumps(setup),
343 # retain=True
344 # )
345 except:
346 log.error('MQTT Conection error.')
347 self.client = False
348
349 def setup(self, port=1883, username=None, password=None, clientId="user", zeromq=1, verbose=True, **kwargs):
350 self.counter = 0
74 self.counter = 0
351 self.topic = kwargs.get('topic', 'schain')
352 self.delay = kwargs.get('delay', 0)
75 self.delay = kwargs.get('delay', 0)
353 self.plottype = kwargs.get('plottype', 'spectra')
354 self.host = kwargs.get('host', "10.10.10.82")
355 self.port = kwargs.get('port', 3000)
356 self.clientId = clientId
357 self.cnt = 0
76 self.cnt = 0
358 self.zeromq = zeromq
359 self.mqtt = kwargs.get('plottype', 0)
360 self.client = None
361 self.verbose = verbose
77 self.verbose = verbose
362 setup = []
78 setup = []
363 if mqtt is 1:
79 context = zmq.Context()
364 self.client = mqtt.Client(
80 self.zmq_socket = context.socket(zmq.PUSH)
365 client_id=self.clientId + self.topic + 'SCHAIN',
81 server = kwargs.get('server', 'zmq.pipe')
366 clean_session=True)
82
367 self.client.on_disconnect = self.on_disconnect
83 if 'tcp://' in server:
368 self.connect()
84 address = server
369 for plot in self.plottype:
85 else:
370 setup.append({
86 address = 'ipc:///tmp/%s' % server
371 'plot': plot,
87
372 'topic': self.topic + plot,
88 self.zmq_socket.connect(address)
373 'title': getattr(self, plot + '_' + 'title', False),
89 time.sleep(1)
374 'xlabel': getattr(self, plot + '_' + 'xlabel', False),
375 'ylabel': getattr(self, plot + '_' + 'ylabel', False),
376 'xrange': getattr(self, plot + '_' + 'xrange', False),
377 'yrange': getattr(self, plot + '_' + 'yrange', False),
378 'zrange': getattr(self, plot + '_' + 'zrange', False),
379 })
380 if zeromq is 1:
381 context = zmq.Context()
382 self.zmq_socket = context.socket(zmq.PUSH)
383 server = kwargs.get('server', 'zmq.pipe')
384
385 if 'tcp://' in server:
386 address = server
387 else:
388 address = 'ipc:///tmp/%s' % server
389
390 self.zmq_socket.connect(address)
391 time.sleep(1)
392
90
393
91
394 def publish_data(self):
92 def publish_data(self):
395 self.dataOut.finished = False
93 self.dataOut.finished = False
396 if self.mqtt is 1:
94
397 yData = self.dataOut.heightList[:2].tolist()
95 if self.verbose:
398 if self.plottype == 'spectra':
96 log.log(
399 data = getattr(self.dataOut, 'data_spc')
97 'Sending {} - {}'.format(self.dataOut.type, self.dataOut.datatime),
400 z = data/self.dataOut.normFactor
98 self.name
401 zdB = 10*numpy.log10(z)
99 )
402 xlen, ylen = zdB[0].shape
100 self.zmq_socket.send_pyobj(self.dataOut)
403 dx = int(xlen/MAXNUMX) + 1
404 dy = int(ylen/MAXNUMY) + 1
405 Z = [0 for i in self.dataOut.channelList]
406 for i in self.dataOut.channelList:
407 Z[i] = zdB[i][::dx, ::dy].tolist()
408 payload = {
409 'timestamp': self.dataOut.utctime,
410 'data': roundFloats(Z),
411 'channels': ['Ch %s' % ch for ch in self.dataOut.channelList],
412 'interval': self.dataOut.getTimeInterval(),
413 'type': self.plottype,
414 'yData': yData
415 }
416
417 elif self.plottype in ('rti', 'power'):
418 data = getattr(self.dataOut, 'data_spc')
419 z = data/self.dataOut.normFactor
420 avg = numpy.average(z, axis=1)
421 avgdB = 10*numpy.log10(avg)
422 xlen, ylen = z[0].shape
423 dy = numpy.floor(ylen/self.__MAXNUMY) + 1
424 AVG = [0 for i in self.dataOut.channelList]
425 for i in self.dataOut.channelList:
426 AVG[i] = avgdB[i][::dy].tolist()
427 payload = {
428 'timestamp': self.dataOut.utctime,
429 'data': roundFloats(AVG),
430 'channels': ['Ch %s' % ch for ch in self.dataOut.channelList],
431 'interval': self.dataOut.getTimeInterval(),
432 'type': self.plottype,
433 'yData': yData
434 }
435 elif self.plottype == 'noise':
436 noise = self.dataOut.getNoise()/self.dataOut.normFactor
437 noisedB = 10*numpy.log10(noise)
438 payload = {
439 'timestamp': self.dataOut.utctime,
440 'data': roundFloats(noisedB.reshape(-1, 1).tolist()),
441 'channels': ['Ch %s' % ch for ch in self.dataOut.channelList],
442 'interval': self.dataOut.getTimeInterval(),
443 'type': self.plottype,
444 'yData': yData
445 }
446 elif self.plottype == 'snr':
447 data = getattr(self.dataOut, 'data_SNR')
448 avgdB = 10*numpy.log10(data)
449
450 ylen = data[0].size
451 dy = numpy.floor(ylen/self.__MAXNUMY) + 1
452 AVG = [0 for i in self.dataOut.channelList]
453 for i in self.dataOut.channelList:
454 AVG[i] = avgdB[i][::dy].tolist()
455 payload = {
456 'timestamp': self.dataOut.utctime,
457 'data': roundFloats(AVG),
458 'channels': ['Ch %s' % ch for ch in self.dataOut.channelList],
459 'type': self.plottype,
460 'yData': yData
461 }
462 else:
463 print("Tipo de grafico invalido")
464 payload = {
465 'data': 'None',
466 'timestamp': 'None',
467 'type': None
468 }
469
470 self.client.publish(self.topic + self.plottype, json.dumps(payload), qos=0)
471
472 if self.zeromq is 1:
473 if self.verbose:
474 log.log(
475 'Sending {} - {}'.format(self.dataOut.type, self.dataOut.datatime),
476 self.name
477 )
478 self.zmq_socket.send_pyobj(self.dataOut)
479
101
480 def run(self, dataOut, **kwargs):
102 def run(self, dataOut, **kwargs):
481 self.dataOut = dataOut
103 self.dataOut = dataOut
@@ -487,15 +109,12 class PublishData(Operation):
487 time.sleep(self.delay)
109 time.sleep(self.delay)
488
110
489 def close(self):
111 def close(self):
490 if self.zeromq is 1:
112
491 self.dataOut.finished = True
113 self.dataOut.finished = True
492 self.zmq_socket.send_pyobj(self.dataOut)
114 self.zmq_socket.send_pyobj(self.dataOut)
493 time.sleep(0.1)
115 time.sleep(0.1)
494 self.zmq_socket.close()
116 self.zmq_socket.close()
495 if self.client:
117
496 self.client.loop_stop()
497 self.client.disconnect()
498
499
118
500 class ReceiverData(ProcessingUnit):
119 class ReceiverData(ProcessingUnit):
501
120
@@ -536,185 +155,6 class ReceiverData(ProcessingUnit):
536 'Receiving')
155 'Receiving')
537
156
538
157
539 class PlotterReceiver(ProcessingUnit, Process):
540
541 throttle_value = 5
542 __attrs__ = ['server', 'plottypes', 'realtime', 'localtime', 'throttle',
543 'exp_code', 'web_server', 'buffering']
544
545 def __init__(self, **kwargs):
546
547 ProcessingUnit.__init__(self, **kwargs)
548 Process.__init__(self)
549 self.mp = False
550 self.isConfig = False
551 self.isWebConfig = False
552 self.connections = 0
553 server = kwargs.get('server', 'zmq.pipe')
554 web_server = kwargs.get('web_server', None)
555 if 'tcp://' in server:
556 address = server
557 else:
558 address = 'ipc:///tmp/%s' % server
559 self.address = address
560 self.web_address = web_server
561 self.plottypes = [s.strip() for s in kwargs.get('plottypes', 'rti').split(',')]
562 self.realtime = kwargs.get('realtime', False)
563 self.localtime = kwargs.get('localtime', True)
564 self.buffering = kwargs.get('buffering', True)
565 self.throttle_value = kwargs.get('throttle', 5)
566 self.exp_code = kwargs.get('exp_code', None)
567 self.sendData = self.initThrottle(self.throttle_value)
568 self.dates = []
569 self.setup()
570
571 def setup(self):
572
573 self.data = Data(self.plottypes, self.throttle_value, self.exp_code, self.buffering)
574 self.isConfig = True
575
576 def event_monitor(self, monitor):
577
578 events = {}
579
580 for name in dir(zmq):
581 if name.startswith('EVENT_'):
582 value = getattr(zmq, name)
583 events[value] = name
584
585 while monitor.poll():
586 evt = recv_monitor_message(monitor)
587 if evt['event'] == 32:
588 self.connections += 1
589 if evt['event'] == 512:
590 pass
591
592 evt.update({'description': events[evt['event']]})
593
594 if evt['event'] == zmq.EVENT_MONITOR_STOPPED:
595 break
596 monitor.close()
597 print('event monitor thread done!')
598
599 def initThrottle(self, throttle_value):
600
601 @throttle(seconds=throttle_value)
602 def sendDataThrottled(fn_sender, data):
603 fn_sender(data)
604
605 return sendDataThrottled
606
607 def send(self, data):
608 log.log('Sending {}'.format(data), self.name)
609 self.sender.send_pyobj(data)
610
611 def run(self):
612
613 log.log(
614 'Starting from {}'.format(self.address),
615 self.name
616 )
617
618 self.context = zmq.Context()
619 self.receiver = self.context.socket(zmq.PULL)
620 self.receiver.bind(self.address)
621 monitor = self.receiver.get_monitor_socket()
622 self.sender = self.context.socket(zmq.PUB)
623 if self.web_address:
624 log.success(
625 'Sending to web: {}'.format(self.web_address),
626 self.name
627 )
628 self.sender_web = self.context.socket(zmq.REQ)
629 self.sender_web.connect(self.web_address)
630 self.poll = zmq.Poller()
631 self.poll.register(self.sender_web, zmq.POLLIN)
632 time.sleep(1)
633
634 if 'server' in self.kwargs:
635 self.sender.bind("ipc:///tmp/{}.plots".format(self.kwargs['server']))
636 else:
637 self.sender.bind("ipc:///tmp/zmq.plots")
638
639 time.sleep(2)
640
641 t = Thread(target=self.event_monitor, args=(monitor,))
642 t.start()
643
644 while True:
645 dataOut = self.receiver.recv_pyobj()
646 if not dataOut.flagNoData:
647 if dataOut.type == 'Parameters':
648 tm = dataOut.utctimeInit
649 else:
650 tm = dataOut.utctime
651 if dataOut.useLocalTime:
652 if not self.localtime:
653 tm += time.timezone
654 dt = datetime.datetime.fromtimestamp(tm).date()
655 else:
656 if self.localtime:
657 tm -= time.timezone
658 dt = datetime.datetime.utcfromtimestamp(tm).date()
659 coerce = False
660 if dt not in self.dates:
661 if self.data:
662 self.data.ended = True
663 self.send(self.data)
664 coerce = True
665 self.data.setup()
666 self.dates.append(dt)
667
668 self.data.update(dataOut, tm)
669
670 if dataOut.finished is True:
671 self.connections -= 1
672 if self.connections == 0 and dt in self.dates:
673 self.data.ended = True
674 self.send(self.data)
675 # self.data.setup()
676 time.sleep(1)
677 break
678 else:
679 if self.realtime:
680 self.send(self.data)
681 if self.web_address:
682 retries = 5
683 while True:
684 self.sender_web.send(self.data.jsonify())
685 socks = dict(self.poll.poll(5000))
686 if socks.get(self.sender_web) == zmq.POLLIN:
687 reply = self.sender_web.recv_string()
688 if reply == 'ok':
689 log.log("Response from server ok", self.name)
690 break
691 else:
692 log.warning("Malformed reply from server: {}".format(reply), self.name)
693
694 else:
695 log.warning("No response from server, retrying...", self.name)
696 self.sender_web.setsockopt(zmq.LINGER, 0)
697 self.sender_web.close()
698 self.poll.unregister(self.sender_web)
699 retries -= 1
700 if retries == 0:
701 log.error("Server seems to be offline, abandoning", self.name)
702 self.sender_web = self.context.socket(zmq.REQ)
703 self.sender_web.connect(self.web_address)
704 self.poll.register(self.sender_web, zmq.POLLIN)
705 time.sleep(1)
706 break
707 self.sender_web = self.context.socket(zmq.REQ)
708 self.sender_web.connect(self.web_address)
709 self.poll.register(self.sender_web, zmq.POLLIN)
710 time.sleep(1)
711 else:
712 self.sendData(self.send, self.data, coerce=coerce)
713 coerce = False
714
715 return
716
717
718 class SendToFTP(Operation, Process):
158 class SendToFTP(Operation, Process):
719
159
720 '''
160 '''
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now