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