##// END OF EJS Templates
Update README
Juan C. Espinoza -
r1289:303db73c0c9d
parent child
Show More
@@ -1,155 +1,158
1 1 # Signal Chain
2 2
3 3 ## Introduction
4 4
5 5 Signal Chain (SCh) is a radar data processing library developed using [Python](www.python.org) at JRO. SCh provides modules to read, write, process and plot data.
6 6
7 7 ## Installation
8 8
9 Install system dependencies, clone the latest version from [here](http://jro-dev.igp.gob.pe/rhodecode/schain/) and install it as a normal python package.
9 Install system dependencies, clone the latest version from [here](http://jro-dev.igp.gob.pe/rhodecode/schain/) and install it as a normal python package, we strongly recommend to use Anaconda or a virtual environment for the installation.
10 10
11 ### Linux based system
11 ### Dependencies
12 - GCC (gcc or gfortran)
13 - Python.h (python-dev or python-devel)
14 - Python-TK (python-tk)
15 - HDF5 libraries (libhdf5-dev)
16
17 ### Linux based system (e.g. ubuntu)
12 18 ```
13 $ sudo apt-get install python-pip python-dev gfortran libpng-dev freetype* libblas-dev liblapack-dev libatlas-base-dev python-qt4 python-tk libssl-dev libhdf5-dev
14 19 $ git clone http://jro-dev.igp.gob.pe/rhodecode/schain/
15 20 $ cd schain
21 $ git checkout `schain-branch` (optional)
16 22 $ sudo pip install ./
17
18 23 ```
19 24
20 25 ### MAC Os
21 26 ```
22 27 $ brew install python
23 $ brew install cartr/qt4/pyqt
24 28 $ git clone http://jro-dev.igp.gob.pe/rhodecode/schain/
25 29 $ cd schain
26 $ pip install ./
27 ```
28
29 **It is recommended to install schain in a virtual environment**
30 ```
31 $ virtualenv /path/to/virtual
32 $ source /path/to/virtual/bin/activate
33 (virtual) $ cd schain
34 (virtual) $ pip install ./
35 (virtual) $ bash link_PyQt4.sh
30 $ git checkout `schain-branch` (optional)
31 $ sudo pip install ./
36 32 ```
37 33
38 34 ### Docker
39 35
40 36 Download Dockerfile from the repository, and create a docker image
41 37
42 38 ```
43 39 $ docker build -t schain .
44 40 ```
45 41
46 42 You can run a container using an xml file or a schain script also you need to mount a volume for the data input and for the output files/plots
47 43 ```
48 44 $ docker run -it --rm --volume /path/to/host/data:/data schain xml /data/test.xml
49 45 $ docker run -it --rm --volume /path/to/host/data:/data --entrypoint /urs/local/bin/python schain /data/test.py
50 46 ```
51 47
48 ## CLI (command line interface)
49
50 Signal Chain provides the following commands:
51
52 - schainGUI: Open the GUI
53 - schain: Signal chain command line
54
55
52 56 ## First Script
53 57
54 Read Spectra data (.pdata) - remove dc - plot spectra & RTI
58 Here you can find an script to read Spectra data (.pdata), remove dc and plot spectra & RTI
55 59
56 Import SCh and creating a project
60 First import SCh and creating a project
57 61
58 62 ```python
59 63 #!/usr/bin/python
60 64
61 65 from schainpy.controller import Project
62 66
63 controller = Project()
64 controller.setup(id = '100',
65 name='test',
66 description='Basic experiment')
67
68
67 prj = Project()
68 prj.setup(
69 id = '100',
70 name='test',
71 description='Basic experiment'
72 )
69 73 ```
70 74
71 Adding read unit and operations
75 Add read unit and operations
72 76
73 77 ```python
74 read_unit = controller.addReadUnit(datatype='Spectra',
75 path='/path/to/pdata/',
76 startDate='2014/01/31',
77 endDate='2014/03/31',
78 startTime='00:00:00',
79 endTime='23:59:59',
80 online=0,
81 walk=0)
82
83 proc_unit = controller.addProcUnit(datatype='Spectra',
84 inputId=read_unit.getId())
78 read_unit = prj.addReadUnit(
79 datatype='Spectra',
80 path='/path/to/pdata/',
81 startDate='2014/01/31',
82 endDate='2014/03/31',
83 startTime='00:00:00',
84 endTime='23:59:59',
85 online=0,
86 walk=0
87 )
88
89 proc_unit = prj.addProcUnit(datatype='Spectra', inputId=read_unit.getId())
85 90
86 91 op = proc_unit.addOperation(name='selectChannels')
87 op.addParameter(name='channelList', value='0,1', format='intlist')
92 op.addParameter(name='channelList', value='0,1')
88 93
89 94 op = proc_unit.addOperation(name='selectHeights')
90 op.addParameter(name='minHei', value='80', format='float')
91 op.addParameter(name='maxHei', value='200', format='float')
95 op.addParameter(name='minHei', value='80')
96 op.addParameter(name='maxHei', value='200')
92 97
93 98 op = proc_unit.addOperation(name='removeDC')
94 99
95 100 ```
96 101
97 Plotting data & start project
102 Plot data & start project
98 103
99 104 ```python
100 op = proc_unit.addOperation(name='SpectraPlot', optype='other')
101 op.addParameter(name='id', value='1', format='int')
102 op.addParameter(name='wintitle', value='Spectra', format='str')
105 op = proc_unit.addOperation(name='SpectraPlot')
106 op.addParameter(name='id', value='1')
107 op.addParameter(name='wintitle', value='Spectra')
103 108
104 op = procUnitConfObj1.addOperation(name='RTIPlot', optype='other')
105 op.addParameter(name='id', value='2', format='int')
106 op.addParameter(name='wintitle', value='RTI', format='str')
109 op = procUnitConfObj1.addOperation(name='RTIPlot')
110 op.addParameter(name='id', value='2')
111 op.addParameter(name='wintitle', value='RTI')
107 112
108 controller.start()
113 prj.start()
109 114
110 115 ```
111 116
112 117 Full script
113 118
114 119
115 120 ```python
116 121 #!/usr/bin/python
117 122
118 from schainpy.controller import Project
123 from schainpy.prj import Project
119 124
120 controller = Project()
121 controller.setup(id = '100',
125 prj = Project()
126 prj.setup(id = '100',
122 127 name='test',
123 128 description='Basic experiment')
124 read_unit = controller.addReadUnit(datatype='Spectra',
129 read_unit = prj.addReadUnit(datatype='Spectra',
125 130 path='/path/to/pdata/',
126 131 startDate='2014/01/31',
127 132 endDate='2014/03/31',
128 133 startTime='00:00:00',
129 134 endTime='23:59:59',
130 135 online=0,
131 136 walk=0)
132 137
133 proc_unit = controller.addProcUnit(datatype='Spectra',
138 proc_unit = prj.addProcUnit(datatype='Spectra',
134 139 inputId=read_unit.getId())
135 140
136 141 op = proc_unit.addOperation(name='selectChannels')
137 op.addParameter(name='channelList', value='0,1', format='intlist')
142 op.addParameter(name='channelList', value='0,1')
138 143
139 144 op = proc_unit.addOperation(name='selectHeights')
140 op.addParameter(name='minHei', value='80', format='float')
141 op.addParameter(name='maxHei', value='200', format='float')
145 op.addParameter(name='minHei', value='80')
146 op.addParameter(name='maxHei', value='200')
142 147
143 148 op = proc_unit.addOperation(name='removeDC')
144 149
145 op = proc_unit.addOperation(name='SpectraPlot', optype='other')
146 op.addParameter(name='id', value='6', format='int')
150 op = proc_unit.addOperation(name='SpectraPlot')
147 151 op.addParameter(name='wintitle', value='Spectra', format='str')
148 152
149 op = procUnitConfObj1.addOperation(name='RTIPlot', optype='other')
150 op.addParameter(name='id', value='2', format='int')
153 op = procUnitConfObj1.addOperation(name='RTIPlot')
151 154 op.addParameter(name='wintitle', value='RTI', format='str')
152 155
153 controller.start()
156 prj.start()
154 157
155 158 ``` No newline at end of file
@@ -1,700 +1,700
1 1
2 2 import os
3 3 import sys
4 4 import zmq
5 5 import time
6 6 import numpy
7 7 import datetime
8 8 from functools import wraps
9 9 from threading import Thread
10 10 import matplotlib
11 11
12 12 if 'BACKEND' in os.environ:
13 13 matplotlib.use(os.environ['BACKEND'])
14 14 elif 'linux' in sys.platform:
15 15 matplotlib.use("TkAgg")
16 16 elif 'darwin' in sys.platform:
17 17 matplotlib.use('WxAgg')
18 18 else:
19 19 from schainpy.utils import log
20 20 log.warning('Using default Backend="Agg"', 'INFO')
21 21 matplotlib.use('Agg')
22 22
23 23 import matplotlib.pyplot as plt
24 24 from matplotlib.patches import Polygon
25 25 from mpl_toolkits.axes_grid1 import make_axes_locatable
26 26 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
27 27
28 28 from schainpy.model.data.jrodata import PlotterData
29 29 from schainpy.model.proc.jroproc_base import ProcessingUnit, Operation, MPDecorator
30 30 from schainpy.utils import log
31 31
32 32 jet_values = matplotlib.pyplot.get_cmap('jet', 100)(numpy.arange(100))[10:90]
33 33 blu_values = matplotlib.pyplot.get_cmap(
34 34 'seismic_r', 20)(numpy.arange(20))[10:15]
35 35 ncmap = matplotlib.colors.LinearSegmentedColormap.from_list(
36 36 'jro', numpy.vstack((blu_values, jet_values)))
37 37 matplotlib.pyplot.register_cmap(cmap=ncmap)
38 38
39 39 CMAPS = [plt.get_cmap(s) for s in ('jro', 'jet', 'viridis',
40 40 'plasma', 'inferno', 'Greys', 'seismic', 'bwr', 'coolwarm')]
41 41
42 42 EARTH_RADIUS = 6.3710e3
43 43
44 44 def ll2xy(lat1, lon1, lat2, lon2):
45 45
46 46 p = 0.017453292519943295
47 47 a = 0.5 - numpy.cos((lat2 - lat1) * p)/2 + numpy.cos(lat1 * p) * \
48 48 numpy.cos(lat2 * p) * (1 - numpy.cos((lon2 - lon1) * p)) / 2
49 49 r = 12742 * numpy.arcsin(numpy.sqrt(a))
50 50 theta = numpy.arctan2(numpy.sin((lon2-lon1)*p)*numpy.cos(lat2*p), numpy.cos(lat1*p)
51 51 * numpy.sin(lat2*p)-numpy.sin(lat1*p)*numpy.cos(lat2*p)*numpy.cos((lon2-lon1)*p))
52 52 theta = -theta + numpy.pi/2
53 53 return r*numpy.cos(theta), r*numpy.sin(theta)
54 54
55 55
56 56 def km2deg(km):
57 57 '''
58 58 Convert distance in km to degrees
59 59 '''
60 60
61 61 return numpy.rad2deg(km/EARTH_RADIUS)
62 62
63 63
64 64 def figpause(interval):
65 65 backend = plt.rcParams['backend']
66 66 if backend in matplotlib.rcsetup.interactive_bk:
67 67 figManager = matplotlib._pylab_helpers.Gcf.get_active()
68 68 if figManager is not None:
69 69 canvas = figManager.canvas
70 70 if canvas.figure.stale:
71 71 canvas.draw()
72 72 try:
73 73 canvas.start_event_loop(interval)
74 74 except:
75 75 pass
76 76 return
77 77
78 78
79 79 def popup(message):
80 80 '''
81 81 '''
82 82
83 83 fig = plt.figure(figsize=(12, 8), facecolor='r')
84 84 text = '\n'.join([s.strip() for s in message.split(':')])
85 85 fig.text(0.01, 0.5, text, ha='left', va='center',
86 86 size='20', weight='heavy', color='w')
87 87 fig.show()
88 88 figpause(1000)
89 89
90 90
91 91 class Throttle(object):
92 92 '''
93 93 Decorator that prevents a function from being called more than once every
94 94 time period.
95 95 To create a function that cannot be called more than once a minute, but
96 96 will sleep until it can be called:
97 97 @Throttle(minutes=1)
98 98 def foo():
99 99 pass
100 100
101 101 for i in range(10):
102 102 foo()
103 103 print "This function has run %s times." % i
104 104 '''
105 105
106 106 def __init__(self, seconds=0, minutes=0, hours=0):
107 107 self.throttle_period = datetime.timedelta(
108 108 seconds=seconds, minutes=minutes, hours=hours
109 109 )
110 110
111 111 self.time_of_last_call = datetime.datetime.min
112 112
113 113 def __call__(self, fn):
114 114 @wraps(fn)
115 115 def wrapper(*args, **kwargs):
116 116 coerce = kwargs.pop('coerce', None)
117 117 if coerce:
118 118 self.time_of_last_call = datetime.datetime.now()
119 119 return fn(*args, **kwargs)
120 120 else:
121 121 now = datetime.datetime.now()
122 122 time_since_last_call = now - self.time_of_last_call
123 123 time_left = self.throttle_period - time_since_last_call
124 124
125 125 if time_left > datetime.timedelta(seconds=0):
126 126 return
127 127
128 128 self.time_of_last_call = datetime.datetime.now()
129 129 return fn(*args, **kwargs)
130 130
131 131 return wrapper
132 132
133 133 def apply_throttle(value):
134 134
135 135 @Throttle(seconds=value)
136 136 def fnThrottled(fn):
137 137 fn()
138 138
139 139 return fnThrottled
140 140
141 141
142 142 @MPDecorator
143 143 class Plot(Operation):
144 144 '''
145 145 Base class for Schain plotting operations
146 146 '''
147 147
148 148 CODE = 'Figure'
149 149 colormap = 'jet'
150 150 bgcolor = 'white'
151 151 buffering = True
152 152 __missing = 1E30
153 153
154 154 __attrs__ = ['show', 'save', 'ymin', 'ymax', 'zmin', 'zmax', 'title',
155 155 'showprofile']
156 156
157 157 def __init__(self):
158 158
159 159 Operation.__init__(self)
160 160 self.isConfig = False
161 161 self.isPlotConfig = False
162 162 self.save_counter = 1
163 163 self.sender_counter = 1
164 164 self.data = None
165 165 self.firsttime = True
166 166 self.plots_adjust = {'left': 0.125, 'right': 0.9, 'bottom': 0.15, 'top': 0.9, 'wspace': 0.2, 'hspace': 0.2}
167 167
168 168 def __fmtTime(self, x, pos):
169 169 '''
170 170 '''
171 171
172 172 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
173 173
174 174 def __setup(self, **kwargs):
175 175 '''
176 176 Initialize variables
177 177 '''
178 178
179 179 self.figures = []
180 180 self.axes = []
181 181 self.cb_axes = []
182 182 self.localtime = kwargs.pop('localtime', True)
183 183 self.show = kwargs.get('show', True)
184 184 self.save = kwargs.get('save', False)
185 185 self.save_period = kwargs.get('save_period', 1)
186 186 self.ftp = kwargs.get('ftp', False)
187 187 self.colormap = kwargs.get('colormap', self.colormap)
188 188 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
189 189 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
190 190 self.colormaps = kwargs.get('colormaps', None)
191 191 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
192 192 self.showprofile = kwargs.get('showprofile', False)
193 193 self.title = kwargs.get('wintitle', self.CODE.upper())
194 194 self.cb_label = kwargs.get('cb_label', None)
195 195 self.cb_labels = kwargs.get('cb_labels', None)
196 196 self.labels = kwargs.get('labels', None)
197 197 self.xaxis = kwargs.get('xaxis', 'frequency')
198 198 self.zmin = kwargs.get('zmin', None)
199 199 self.zmax = kwargs.get('zmax', None)
200 200 self.zlimits = kwargs.get('zlimits', None)
201 201 self.xmin = kwargs.get('xmin', None)
202 202 self.xmax = kwargs.get('xmax', None)
203 203 self.xrange = kwargs.get('xrange', 24)
204 204 self.xscale = kwargs.get('xscale', None)
205 205 self.ymin = kwargs.get('ymin', None)
206 206 self.ymax = kwargs.get('ymax', None)
207 207 self.yscale = kwargs.get('yscale', None)
208 208 self.xlabel = kwargs.get('xlabel', None)
209 209 self.attr_time = kwargs.get('attr_time', 'utctime')
210 210 self.decimation = kwargs.get('decimation', None)
211 211 self.showSNR = kwargs.get('showSNR', False)
212 212 self.oneFigure = kwargs.get('oneFigure', True)
213 213 self.width = kwargs.get('width', None)
214 214 self.height = kwargs.get('height', None)
215 215 self.colorbar = kwargs.get('colorbar', True)
216 216 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
217 217 self.channels = kwargs.get('channels', None)
218 218 self.titles = kwargs.get('titles', [])
219 219 self.polar = False
220 220 self.type = kwargs.get('type', 'iq')
221 221 self.grid = kwargs.get('grid', False)
222 222 self.pause = kwargs.get('pause', False)
223 223 self.save_code = kwargs.get('save_code', None)
224 224 self.realtime = kwargs.get('realtime', True)
225 225 self.throttle = kwargs.get('throttle', 0)
226 226 self.exp_code = kwargs.get('exp_code', None)
227 227 self.plot_server = kwargs.get('plot_server', False)
228 228 self.sender_period = kwargs.get('sender_period', 1)
229 229 self.height_index = kwargs.get('height_index', None)
230 230 self.__throttle_plot = apply_throttle(self.throttle)
231 231 self.data = PlotterData(
232 232 self.CODE, self.throttle, self.exp_code, self.buffering, snr=self.showSNR)
233 233
234 234 if self.plot_server:
235 235 if not self.plot_server.startswith('tcp://'):
236 236 self.plot_server = 'tcp://{}'.format(self.plot_server)
237 237 log.success(
238 238 'Sending to server: {}'.format(self.plot_server),
239 239 self.name
240 240 )
241 241 if 'plot_name' in kwargs:
242 242 self.plot_name = kwargs['plot_name']
243 243
244 244 def __setup_plot(self):
245 245 '''
246 246 Common setup for all figures, here figures and axes are created
247 247 '''
248 248
249 249 self.setup()
250 250
251 251 self.time_label = 'LT' if self.localtime else 'UTC'
252 252
253 253 if self.width is None:
254 254 self.width = 8
255 255
256 256 self.figures = []
257 257 self.axes = []
258 258 self.cb_axes = []
259 259 self.pf_axes = []
260 260 self.cmaps = []
261 261
262 262 size = '15%' if self.ncols == 1 else '30%'
263 263 pad = '4%' if self.ncols == 1 else '8%'
264 264
265 265 if self.oneFigure:
266 266 if self.height is None:
267 267 self.height = 1.4 * self.nrows + 1
268 268 fig = plt.figure(figsize=(self.width, self.height),
269 269 edgecolor='k',
270 270 facecolor='w')
271 271 self.figures.append(fig)
272 272 for n in range(self.nplots):
273 273 ax = fig.add_subplot(self.nrows, self.ncols,
274 274 n + 1, polar=self.polar)
275 275 ax.tick_params(labelsize=8)
276 276 ax.firsttime = True
277 277 ax.index = 0
278 278 ax.press = None
279 279 self.axes.append(ax)
280 280 if self.showprofile:
281 281 cax = self.__add_axes(ax, size=size, pad=pad)
282 282 cax.tick_params(labelsize=8)
283 283 self.pf_axes.append(cax)
284 284 else:
285 285 if self.height is None:
286 286 self.height = 3
287 287 for n in range(self.nplots):
288 288 fig = plt.figure(figsize=(self.width, self.height),
289 289 edgecolor='k',
290 290 facecolor='w')
291 291 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
292 292 ax.tick_params(labelsize=8)
293 293 ax.firsttime = True
294 294 ax.index = 0
295 295 ax.press = None
296 296 self.figures.append(fig)
297 297 self.axes.append(ax)
298 298 if self.showprofile:
299 299 cax = self.__add_axes(ax, size=size, pad=pad)
300 300 cax.tick_params(labelsize=8)
301 301 self.pf_axes.append(cax)
302 302
303 303 for n in range(self.nrows):
304 304 if self.colormaps is not None:
305 305 cmap = plt.get_cmap(self.colormaps[n])
306 306 else:
307 307 cmap = plt.get_cmap(self.colormap)
308 308 cmap.set_bad(self.bgcolor, 1.)
309 309 self.cmaps.append(cmap)
310 310
311 311 def __add_axes(self, ax, size='30%', pad='8%'):
312 312 '''
313 313 Add new axes to the given figure
314 314 '''
315 315 divider = make_axes_locatable(ax)
316 316 nax = divider.new_horizontal(size=size, pad=pad)
317 317 ax.figure.add_axes(nax)
318 318 return nax
319 319
320 320 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
321 321 '''
322 322 Create a masked array for missing data
323 323 '''
324 324 if x_buffer.shape[0] < 2:
325 325 return x_buffer, y_buffer, z_buffer
326 326
327 327 deltas = x_buffer[1:] - x_buffer[0:-1]
328 328 x_median = numpy.median(deltas)
329 329
330 330 index = numpy.where(deltas > 5 * x_median)
331 331
332 332 if len(index[0]) != 0:
333 333 z_buffer[::, index[0], ::] = self.__missing
334 334 z_buffer = numpy.ma.masked_inside(z_buffer,
335 335 0.99 * self.__missing,
336 336 1.01 * self.__missing)
337 337
338 338 return x_buffer, y_buffer, z_buffer
339 339
340 340 def decimate(self):
341 341
342 342 # dx = int(len(self.x)/self.__MAXNUMX) + 1
343 343 dy = int(len(self.y) / self.decimation) + 1
344 344
345 345 # x = self.x[::dx]
346 346 x = self.x
347 347 y = self.y[::dy]
348 348 z = self.z[::, ::, ::dy]
349 349
350 350 return x, y, z
351 351
352 352 def format(self):
353 353 '''
354 354 Set min and max values, labels, ticks and titles
355 355 '''
356 356
357 357 if self.xmin is None:
358 358 xmin = self.data.min_time
359 359 else:
360 360 if self.xaxis is 'time':
361 361 dt = self.getDateTime(self.data.min_time)
362 362 xmin = (dt.replace(hour=int(self.xmin), minute=0, second=0) -
363 363 datetime.datetime(1970, 1, 1)).total_seconds()
364 364 if self.data.localtime:
365 365 xmin += time.timezone
366 366 else:
367 367 xmin = self.xmin
368 368
369 369 if self.xmax is None:
370 370 xmax = xmin + self.xrange * 60 * 60
371 371 else:
372 372 if self.xaxis is 'time':
373 373 dt = self.getDateTime(self.data.max_time)
374 374 xmax = self.xmax - 1
375 375 xmax = (dt.replace(hour=int(xmax), minute=59, second=59) -
376 376 datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=1)).total_seconds()
377 377 if self.data.localtime:
378 378 xmax += time.timezone
379 379 else:
380 380 xmax = self.xmax
381 381
382 382 ymin = self.ymin if self.ymin else numpy.nanmin(self.y)
383 383 ymax = self.ymax if self.ymax else numpy.nanmax(self.y)
384 384
385 385 for n, ax in enumerate(self.axes):
386 386 if ax.firsttime:
387 387
388 388 dig = int(numpy.log10(ymax))
389 389 if dig == 0:
390 390 digD = len(str(ymax)) - 2
391 391 ydec = ymax*(10**digD)
392 392
393 393 dig = int(numpy.log10(ydec))
394 394 ystep = ((ydec + (10**(dig)))//10**(dig))*(10**(dig))
395 395 ystep = ystep/5
396 396 ystep = ystep/(10**digD)
397 397
398 398 else:
399 399 ystep = ((ymax + (10**(dig)))//10**(dig))*(10**(dig))
400 400 ystep = ystep/5
401 401
402 402 if self.xaxis is not 'time':
403 403
404 404 dig = int(numpy.log10(xmax))
405 405
406 406 if dig <= 0:
407 407 digD = len(str(xmax)) - 2
408 408 xdec = xmax*(10**digD)
409 409
410 410 dig = int(numpy.log10(xdec))
411 411 xstep = ((xdec + (10**(dig)))//10**(dig))*(10**(dig))
412 412 xstep = xstep*0.5
413 413 xstep = xstep/(10**digD)
414 414
415 415 else:
416 416 xstep = ((xmax + (10**(dig)))//10**(dig))*(10**(dig))
417 417 xstep = xstep/5
418 418
419 419 ax.set_facecolor(self.bgcolor)
420 420 ax.yaxis.set_major_locator(MultipleLocator(ystep))
421 421 if self.xscale:
422 422 ax.xaxis.set_major_formatter(FuncFormatter(
423 423 lambda x, pos: '{0:g}'.format(x*self.xscale)))
424 424 if self.xscale:
425 425 ax.yaxis.set_major_formatter(FuncFormatter(
426 426 lambda x, pos: '{0:g}'.format(x*self.yscale)))
427 427 if self.xaxis is 'time':
428 428 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
429 429 ax.xaxis.set_major_locator(LinearLocator(9))
430 430 else:
431 431 ax.xaxis.set_major_locator(MultipleLocator(xstep))
432 432 if self.xlabel is not None:
433 433 ax.set_xlabel(self.xlabel)
434 434 ax.set_ylabel(self.ylabel)
435 435 ax.firsttime = False
436 436 if self.showprofile:
437 437 self.pf_axes[n].set_ylim(ymin, ymax)
438 438 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
439 439 self.pf_axes[n].set_xlabel('dB')
440 440 self.pf_axes[n].grid(b=True, axis='x')
441 441 [tick.set_visible(False)
442 442 for tick in self.pf_axes[n].get_yticklabels()]
443 443 if self.colorbar:
444 444 ax.cbar = plt.colorbar(
445 445 ax.plt, ax=ax, fraction=0.05, pad=0.02, aspect=10)
446 446 ax.cbar.ax.tick_params(labelsize=8)
447 447 ax.cbar.ax.press = None
448 448 if self.cb_label:
449 449 ax.cbar.set_label(self.cb_label, size=8)
450 450 elif self.cb_labels:
451 451 ax.cbar.set_label(self.cb_labels[n], size=8)
452 452 else:
453 453 ax.cbar = None
454 454 if self.grid:
455 455 ax.grid(True)
456 456
457 457 if not self.polar:
458 458 ax.set_xlim(xmin, xmax)
459 459 ax.set_ylim(ymin, ymax)
460 460 ax.set_title('{} {} {}'.format(
461 461 self.titles[n],
462 462 self.getDateTime(self.data.max_time).strftime(
463 463 '%Y-%m-%d %H:%M:%S'),
464 464 self.time_label),
465 465 size=8)
466 466 else:
467 467 ax.set_title('{}'.format(self.titles[n]), size=8)
468 468 ax.set_ylim(0, 90)
469 469 ax.set_yticks(numpy.arange(0, 90, 20))
470 470 ax.yaxis.labelpad = 40
471 471
472 472 if self.firsttime:
473 473 for n, fig in enumerate(self.figures):
474 474 fig.subplots_adjust(**self.plots_adjust)
475 475 self.firsttime = False
476 476
477 477 def clear_figures(self):
478 478 '''
479 479 Reset axes for redraw plots
480 480 '''
481 481
482 482 for ax in self.axes+self.pf_axes+self.cb_axes:
483 483 ax.clear()
484 484 ax.firsttime = True
485 485 if ax.cbar:
486 486 ax.cbar.remove()
487 487
488 488 def __plot(self):
489 489 '''
490 490 Main function to plot, format and save figures
491 491 '''
492 492
493 493 self.plot()
494 494 self.format()
495 495
496 496 for n, fig in enumerate(self.figures):
497 497 if self.nrows == 0 or self.nplots == 0:
498 498 log.warning('No data', self.name)
499 499 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
500 500 fig.canvas.manager.set_window_title(self.CODE)
501 501 continue
502 502
503 503 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
504 504 self.getDateTime(self.data.max_time).strftime('%Y/%m/%d')))
505 505 fig.canvas.draw()
506 506 if self.show:
507 507 fig.show()
508 508 figpause(0.01)
509 509
510 510 if self.save:
511 511 self.save_figure(n)
512 512
513 513 if self.plot_server:
514 514 self.send_to_server()
515 515
516 516 def save_figure(self, n):
517 517 '''
518 518 '''
519 519
520 520 if self.save_counter < self.save_period:
521 521 self.save_counter += 1
522 522 return
523 523
524 524 self.save_counter = 1
525 525
526 526 fig = self.figures[n]
527 527
528 528 if self.save_code:
529 529 if isinstance(self.save_code, str):
530 530 labels = [self.save_code for x in self.figures]
531 531 else:
532 532 labels = self.save_code
533 533 else:
534 534 labels = [self.CODE for x in self.figures]
535 535
536 536 figname = os.path.join(
537 537 self.save,
538 538 labels[n],
539 539 '{}_{}.png'.format(
540 540 labels[n],
541 541 self.getDateTime(self.data.max_time).strftime(
542 542 '%Y%m%d_%H%M%S'
543 543 ),
544 544 )
545 545 )
546 546 log.log('Saving figure: {}'.format(figname), self.name)
547 547 if not os.path.isdir(os.path.dirname(figname)):
548 548 os.makedirs(os.path.dirname(figname))
549 549 fig.savefig(figname)
550 550
551 551 if self.throttle == 0:
552 552 figname = os.path.join(
553 553 self.save,
554 554 '{}_{}.png'.format(
555 555 labels[n],
556 556 self.getDateTime(self.data.min_time).strftime(
557 557 '%Y%m%d'
558 558 ),
559 559 )
560 560 )
561 561 fig.savefig(figname)
562 562
563 563 def send_to_server(self):
564 564 '''
565 565 '''
566 566
567 567 if self.sender_counter < self.sender_period:
568 568 self.sender_counter += 1
569 569 return
570 570
571 571 self.sender_counter = 1
572 572 self.data.meta['titles'] = self.titles
573 573 retries = 2
574 574 while True:
575 575 self.socket.send_string(self.data.jsonify(self.plot_name, self.plot_type))
576 576 socks = dict(self.poll.poll(5000))
577 577 if socks.get(self.socket) == zmq.POLLIN:
578 578 reply = self.socket.recv_string()
579 579 if reply == 'ok':
580 580 log.log("Response from server ok", self.name)
581 581 break
582 582 else:
583 583 log.warning(
584 584 "Malformed reply from server: {}".format(reply), self.name)
585 585
586 586 else:
587 587 log.warning(
588 588 "No response from server, retrying...", self.name)
589 589 self.socket.setsockopt(zmq.LINGER, 0)
590 590 self.socket.close()
591 591 self.poll.unregister(self.socket)
592 592 retries -= 1
593 593 if retries == 0:
594 594 log.error(
595 595 "Server seems to be offline, abandoning", self.name)
596 596 self.socket = self.context.socket(zmq.REQ)
597 597 self.socket.connect(self.plot_server)
598 598 self.poll.register(self.socket, zmq.POLLIN)
599 599 time.sleep(1)
600 600 break
601 601 self.socket = self.context.socket(zmq.REQ)
602 602 self.socket.connect(self.plot_server)
603 603 self.poll.register(self.socket, zmq.POLLIN)
604 604 time.sleep(0.5)
605 605
606 606 def setup(self):
607 607 '''
608 608 This method should be implemented in the child class, the following
609 609 attributes should be set:
610 610
611 611 self.nrows: number of rows
612 612 self.ncols: number of cols
613 613 self.nplots: number of plots (channels or pairs)
614 614 self.ylabel: label for Y axes
615 615 self.titles: list of axes title
616 616
617 617 '''
618 618 raise NotImplementedError
619 619
620 620 def plot(self):
621 621 '''
622 622 Must be defined in the child class
623 623 '''
624 624 raise NotImplementedError
625 625
626 626 def run(self, dataOut, **kwargs):
627 627 '''
628 628 Main plotting routine
629 629 '''
630 630
631 631 if self.isConfig is False:
632 632 self.__setup(**kwargs)
633 633
634 634 t = getattr(dataOut, self.attr_time)
635 635
636 636 if dataOut.useLocalTime:
637 637 self.getDateTime = datetime.datetime.fromtimestamp
638 638 if not self.localtime:
639 639 t += time.timezone
640 640 else:
641 641 self.getDateTime = datetime.datetime.utcfromtimestamp
642 642 if self.localtime:
643 643 t -= time.timezone
644 644
645 645 if 'buffer' in self.plot_type:
646 646 if self.xmin is None:
647 647 self.tmin = t
648 648 self.xmin = self.getDateTime(t).hour
649 649 else:
650 650 self.tmin = (
651 651 self.getDateTime(t).replace(
652 652 hour=int(self.xmin),
653 653 minute=0,
654 654 second=0) - self.getDateTime(0)).total_seconds()
655 655
656 656 self.data.setup()
657 657 self.isConfig = True
658 658 if self.plot_server:
659 659 self.context = zmq.Context()
660 660 self.socket = self.context.socket(zmq.REQ)
661 661 self.socket.connect(self.plot_server)
662 662 self.poll = zmq.Poller()
663 663 self.poll.register(self.socket, zmq.POLLIN)
664 664
665 665 tm = getattr(dataOut, self.attr_time)
666 666
667 667 if not dataOut.useLocalTime and self.localtime:
668 668 tm -= time.timezone
669 669 if dataOut.useLocalTime and not self.localtime:
670 670 tm += time.timezone
671 671
672 672 if self.xaxis is 'time' and self.data and (tm - self.tmin) >= self.xrange*60*60:
673 673 self.save_counter = self.save_period
674 674 self.__plot()
675 675 self.xmin += self.xrange
676 676 if self.xmin >= 24:
677 677 self.xmin -= 24
678 678 self.tmin += self.xrange*60*60
679 679 self.data.setup()
680 680 self.clear_figures()
681 681
682 682 self.data.update(dataOut, tm)
683 683
684 684 if self.isPlotConfig is False:
685 685 self.__setup_plot()
686 686 self.isPlotConfig = True
687 687
688 688 if self.throttle == 0:
689 689 self.__plot()
690 690 else:
691 691 self.__throttle_plot(self.__plot)#, coerce=coerce)
692 692
693 693 def close(self):
694 694
695 if not self.data.flagNoData:
695 if self.data and not self.data.flagNoData:
696 696 self.save_counter = self.save_period
697 697 self.__plot()
698 if not self.data.flagNoData and self.pause:
698 if self.data and not self.data.flagNoData and self.pause:
699 699 figpause(10)
700 700
@@ -1,207 +1,207
1 1 '''
2 2 Base clases to create Processing units and operations, the MPDecorator
3 3 must be used in plotting and writing operations to allow to run as an
4 4 external process.
5 5 '''
6 6
7 7 import inspect
8 8 import zmq
9 9 import time
10 10 import pickle
11 11 import traceback
12 12 try:
13 13 from queue import Queue
14 14 except:
15 15 from Queue import Queue
16 16 from threading import Thread
17 17 from multiprocessing import Process, Queue
18 18 from schainpy.utils import log
19 19
20 20
21 21 class ProcessingUnit(object):
22 22 '''
23 23 Base class to create Signal Chain Units
24 24 '''
25 25
26 26 proc_type = 'processing'
27 27
28 28 def __init__(self):
29 29
30 30 self.dataIn = None
31 31 self.dataOut = None
32 32 self.isConfig = False
33 33 self.operations = []
34 34
35 35 def setInput(self, unit):
36 36
37 37 self.dataIn = unit.dataOut
38 38
39 39 def getAllowedArgs(self):
40 40 if hasattr(self, '__attrs__'):
41 41 return self.__attrs__
42 42 else:
43 43 return inspect.getargspec(self.run).args
44 44
45 45 def addOperation(self, conf, operation):
46 46 '''
47 47 '''
48 48
49 49 self.operations.append((operation, conf.type, conf.getKwargs()))
50 50
51 51 def getOperationObj(self, objId):
52 52
53 53 if objId not in list(self.operations.keys()):
54 54 return None
55 55
56 56 return self.operations[objId]
57 57
58 58 def call(self, **kwargs):
59 59 '''
60 60 '''
61 61
62 62 try:
63 63 if self.dataIn is not None and self.dataIn.flagNoData and not self.dataIn.error:
64 64 return self.dataIn.isReady()
65 65 elif self.dataIn is None or not self.dataIn.error:
66 66 self.run(**kwargs)
67 67 elif self.dataIn.error:
68 68 self.dataOut.error = self.dataIn.error
69 69 self.dataOut.flagNoData = True
70 70 except:
71 71 err = traceback.format_exc()
72 72 if 'SchainWarning' in err:
73 73 log.warning(err.split('SchainWarning:')[-1].split('\n')[0].strip(), self.name)
74 74 elif 'SchainError' in err:
75 75 log.error(err.split('SchainError:')[-1].split('\n')[0].strip(), self.name)
76 76 else:
77 log.error(err, self.name)
77 log.error(err.split('\n')[-2], self.name)
78 78 self.dataOut.error = True
79 79
80 80 for op, optype, opkwargs in self.operations:
81 81 if optype == 'other' and not self.dataOut.flagNoData:
82 82 self.dataOut = op.run(self.dataOut, **opkwargs)
83 83 elif optype == 'external' and not self.dataOut.flagNoData:
84 84 op.queue.put(self.dataOut)
85 85 elif optype == 'external' and self.dataOut.error:
86 86 op.queue.put(self.dataOut)
87 87
88 88 return 'Error' if self.dataOut.error else self.dataOut.isReady()
89 89
90 90 def setup(self):
91 91
92 92 raise NotImplementedError
93 93
94 94 def run(self):
95 95
96 96 raise NotImplementedError
97 97
98 98 def close(self):
99 99
100 100 return
101 101
102 102
103 103 class Operation(object):
104 104
105 105 '''
106 106 '''
107 107
108 108 proc_type = 'operation'
109 109
110 110 def __init__(self):
111 111
112 112 self.id = None
113 113 self.isConfig = False
114 114
115 115 if not hasattr(self, 'name'):
116 116 self.name = self.__class__.__name__
117 117
118 118 def getAllowedArgs(self):
119 119 if hasattr(self, '__attrs__'):
120 120 return self.__attrs__
121 121 else:
122 122 return inspect.getargspec(self.run).args
123 123
124 124 def setup(self):
125 125
126 126 self.isConfig = True
127 127
128 128 raise NotImplementedError
129 129
130 130 def run(self, dataIn, **kwargs):
131 131 """
132 132 Realiza las operaciones necesarias sobre la dataIn.data y actualiza los
133 133 atributos del objeto dataIn.
134 134
135 135 Input:
136 136
137 137 dataIn : objeto del tipo JROData
138 138
139 139 Return:
140 140
141 141 None
142 142
143 143 Affected:
144 144 __buffer : buffer de recepcion de datos.
145 145
146 146 """
147 147 if not self.isConfig:
148 148 self.setup(**kwargs)
149 149
150 150 raise NotImplementedError
151 151
152 152 def close(self):
153 153
154 154 return
155 155
156 156
157 157 def MPDecorator(BaseClass):
158 158 """
159 159 Multiprocessing class decorator
160 160
161 161 This function add multiprocessing features to a BaseClass.
162 162 """
163 163
164 164 class MPClass(BaseClass, Process):
165 165
166 166 def __init__(self, *args, **kwargs):
167 167 super(MPClass, self).__init__()
168 168 Process.__init__(self)
169 169
170 170 self.args = args
171 171 self.kwargs = kwargs
172 172 self.t = time.time()
173 173 self.op_type = 'external'
174 174 self.name = BaseClass.__name__
175 175 self.__doc__ = BaseClass.__doc__
176 176
177 177 if 'plot' in self.name.lower() and not self.name.endswith('_'):
178 178 self.name = '{}{}'.format(self.CODE.upper(), 'Plot')
179 179
180 180 self.start_time = time.time()
181 181 self.err_queue = args[3]
182 182 self.queue = Queue(maxsize=1)
183 183 self.myrun = BaseClass.run
184 184
185 185 def run(self):
186 186
187 187 while True:
188 188
189 189 dataOut = self.queue.get()
190 190
191 191 if not dataOut.error:
192 192 try:
193 193 BaseClass.run(self, dataOut, **self.kwargs)
194 194 except:
195 195 err = traceback.format_exc()
196 196 log.error(err.split('\n')[-2], self.name)
197 197 else:
198 198 break
199 199
200 200 self.close()
201 201
202 202 def close(self):
203 203
204 204 BaseClass.close(self)
205 205 log.success('Done...(Time:{:4.2f} secs)'.format(time.time()-self.start_time), self.name)
206 206
207 207 return MPClass
General Comments 0
You need to be logged in to leave comments. Login now