##// END OF EJS Templates
Fix merge
jespinoza -
r1107:0e8d1b4cd150 merge
parent child
Show More
@@ -0,0 +1,343
1 '''
2 Created on Set 10, 2017
3
4 @author: Juan C. Espinoza
5 '''
6
7
8 import os
9 import sys
10 import time
11 import glob
12 import datetime
13
14 import numpy
15
16 from schainpy.model.proc.jroproc_base import ProcessingUnit
17 from schainpy.model.data.jrodata import Parameters
18 from schainpy.model.io.jroIO_base import JRODataReader, isNumber
19 from schainpy.utils import log
20
21 FILE_HEADER_STRUCTURE = numpy.dtype([
22 ('year', 'f'),
23 ('doy', 'f'),
24 ('nint', 'f'),
25 ('navg', 'f'),
26 ('fh', 'f'),
27 ('dh', 'f'),
28 ('nheights', 'f'),
29 ('ipp', 'f')
30 ])
31
32 REC_HEADER_STRUCTURE = numpy.dtype([
33 ('magic', 'f'),
34 ('hours', 'f'),
35 ('interval', 'f'),
36 ('h0', 'f'),
37 ('nheights', 'f'),
38 ('snr1', 'f'),
39 ('snr2', 'f'),
40 ('snr', 'f'),
41 ])
42
43 DATA_STRUCTURE = numpy.dtype([
44 ('range', '<u4'),
45 ('status', '<u4'),
46 ('zonal', '<f4'),
47 ('meridional', '<f4'),
48 ('vertical', '<f4'),
49 ('zonal_a', '<f4'),
50 ('meridional_a', '<f4'),
51 ('corrected_fading', '<f4'), # seconds
52 ('uncorrected_fading', '<f4'), # seconds
53 ('time_diff', '<f4'),
54 ('major_axis', '<f4'),
55 ('axial_ratio', '<f4'),
56 ('orientation', '<f4'),
57 ('sea_power', '<u4'),
58 ('sea_algorithm', '<u4')
59 ])
60
61
62 class JULIAParamReader(JRODataReader, ProcessingUnit):
63 '''
64 Julia data (eej, spf, 150km) *.dat files
65 '''
66
67 ext = '.dat'
68
69 def __init__(self, **kwargs):
70
71 ProcessingUnit.__init__(self, **kwargs)
72
73 self.dataOut = Parameters()
74 self.counter_records = 0
75 self.flagNoMoreFiles = 0
76 self.isConfig = False
77 self.filename = None
78 self.clockpulse = 0.15
79 self.kd = 213.6
80
81 def setup(self,
82 path=None,
83 startDate=None,
84 endDate=None,
85 ext=None,
86 startTime=datetime.time(0, 0, 0),
87 endTime=datetime.time(23, 59, 59),
88 timezone=0,
89 format=None,
90 **kwargs):
91
92 self.path = path
93 self.startDate = startDate
94 self.endDate = endDate
95 self.startTime = startTime
96 self.endTime = endTime
97 self.datatime = datetime.datetime(1900, 1, 1)
98 self.format = format
99
100 if self.path is None:
101 raise ValueError, "The path is not valid"
102
103 if ext is None:
104 ext = self.ext
105
106 self.search_files(self.path, startDate, endDate, ext)
107 self.timezone = timezone
108 self.fileIndex = 0
109
110 if not self.fileList:
111 log.warning('There is no files matching these date in the folder: {}'.format(
112 path), self.name)
113
114 self.setNextFile()
115
116 def search_files(self, path, startDate, endDate, ext):
117 '''
118 Searching for BLTR rawdata file in path
119 Creating a list of file to proces included in [startDate,endDate]
120
121 Input:
122 path - Path to find BLTR rawdata files
123 startDate - Select file from this date
124 enDate - Select file until this date
125 ext - Extension of the file to read
126 '''
127
128 log.success('Searching files in {} '.format(path), self.name)
129 fileList0 = glob.glob1(path, '{}*{}'.format(self.format.upper(), ext))
130 fileList0.sort()
131
132 self.fileList = []
133 self.dateFileList = []
134
135 for thisFile in fileList0:
136 year = thisFile[2:4]
137 if not isNumber(year):
138 continue
139
140 month = thisFile[4:6]
141 if not isNumber(month):
142 continue
143
144 day = thisFile[6:8]
145 if not isNumber(day):
146 continue
147
148 year, month, day = int(year), int(month), int(day)
149 dateFile = datetime.date(year+2000, month, day)
150
151 if (startDate > dateFile) or (endDate < dateFile):
152 continue
153
154 self.fileList.append(thisFile)
155 self.dateFileList.append(dateFile)
156
157 return
158
159 def setNextFile(self):
160
161 file_id = self.fileIndex
162
163 if file_id == len(self.fileList):
164 log.success('No more files in the folder', self.name)
165 self.flagNoMoreFiles = 1
166 return 0
167
168 log.success('Opening {}'.format(self.fileList[file_id]), self.name)
169 filename = os.path.join(self.path, self.fileList[file_id])
170
171 dirname, name = os.path.split(filename)
172 self.siteFile = name.split('.')[0]
173 if self.filename is not None:
174 self.fp.close()
175 self.filename = filename
176 self.fp = open(self.filename, 'rb')
177
178 self.header_file = numpy.fromfile(self.fp, FILE_HEADER_STRUCTURE, 1)
179 yy = self.header_file['year'] - 1900 * (self.header_file['year'] > 3000)
180 self.year = int(yy + 1900 * (yy < 1000))
181 self.doy = int(self.header_file['doy'])
182 self.dH = round(self.header_file['dh'], 2)
183 self.ipp = round(self.header_file['ipp'], 2)
184 self.sizeOfFile = os.path.getsize(self.filename)
185 self.counter_records = 0
186 self.flagIsNewFile = 0
187 self.fileIndex += 1
188
189 return 1
190
191 def readNextBlock(self):
192
193 while True:
194 if not self.readBlock():
195 self.flagIsNewFile = 1
196 if not self.setNextFile():
197 return 0
198
199 if (self.datatime < datetime.datetime.combine(self.startDate, self.startTime)) or \
200 (self.datatime > datetime.datetime.combine(self.endDate, self.endTime)):
201 log.warning(
202 'Reading Record No. {} -> {} [Skipping]'.format(
203 self.counter_records,
204 self.datatime.ctime()),
205 self.name)
206 continue
207 break
208
209 log.log('Reading Record No. {} -> {}'.format(
210 self.counter_records,
211 self.datatime.ctime()), self.name)
212
213 return 1
214
215 def readBlock(self):
216
217 pointer = self.fp.tell()
218 heights, dt = self.readHeader()
219 self.fp.seek(pointer)
220 buffer_h = []
221 buffer_d = []
222 while True:
223 pointer = self.fp.tell()
224 if pointer == self.sizeOfFile:
225 return 0
226 heights, datatime = self.readHeader()
227 if dt == datatime:
228 buffer_h.append(heights)
229 buffer_d.append(self.readData(len(heights)))
230 continue
231 self.fp.seek(pointer)
232 break
233
234 if dt.date() > self.datatime.date():
235 self.flagDiscontinuousBlock = 1
236 self.datatime = dt
237 self.time = (dt - datetime.datetime(1970, 1, 1)).total_seconds() + time.timezone
238 self.heights = numpy.concatenate(buffer_h)
239 self.buffer = numpy.zeros((5, len(self.heights))) + numpy.nan
240 self.buffer[0, :] = numpy.concatenate([buf[0] for buf in buffer_d])
241 self.buffer[1, :] = numpy.concatenate([buf[1] for buf in buffer_d])
242 self.buffer[2, :] = numpy.concatenate([buf[2] for buf in buffer_d])
243 self.buffer[3, :] = numpy.concatenate([buf[3] for buf in buffer_d])
244 self.buffer[4, :] = numpy.concatenate([buf[4] for buf in buffer_d])
245
246 self.counter_records += 1
247
248 return 1
249
250 def readHeader(self):
251 '''
252 Parse recordHeader
253 '''
254
255 self.header_rec = numpy.fromfile(self.fp, REC_HEADER_STRUCTURE, 1)
256 self.interval = self.header_rec['interval']
257 if self.header_rec['magic'] == 888.:
258 self.header_rec['h0'] = round(self.header_rec['h0'], 2)
259 nheights = int(self.header_rec['nheights'])
260 hours = float(self.header_rec['hours'][0])
261 heights = numpy.arange(nheights) * self.dH + self.header_rec['h0']
262 datatime = datetime.datetime(self.year, 1, 1) + datetime.timedelta(days=self.doy-1, hours=hours)
263 return heights, datatime
264 else:
265 return False
266
267 def readData(self, N):
268 '''
269 Parse data
270 '''
271
272 buffer = numpy.fromfile(self.fp, 'f', 8*N).reshape(N, 8)
273
274 pow0 = buffer[:, 0]
275 pow1 = buffer[:, 1]
276 acf0 = (buffer[:,2] + buffer[:,3]*1j) / pow0
277 acf1 = (buffer[:,4] + buffer[:,5]*1j) / pow1
278 dccf = (buffer[:,6] + buffer[:,7]*1j) / (pow0*pow1)
279
280 ### SNR
281 sno = (pow0 + pow1 - self.header_rec['snr']) / self.header_rec['snr']
282 sno10 = numpy.log10(sno)
283 # dsno = 1.0 / numpy.sqrt(self.header_file['nint'] * self.header_file['navg']) * (1 + (1 / sno))
284
285 ### Vertical Drift
286 sp = numpy.sqrt(numpy.abs(acf0)*numpy.abs(acf1))
287 sp[numpy.where(numpy.abs(sp) >= 1.0)] = numpy.sqrt(0.9999)
288
289 vzo = -numpy.arctan2(acf0.imag + acf1.imag,acf0.real + acf1.real)*1.5E5*1.5/(self.ipp*numpy.pi)
290 dvzo = numpy.sqrt(1.0 - sp*sp)*0.338*1.5E5/(numpy.sqrt(self.header_file['nint']*self.header_file['navg'])*sp*self.ipp)
291 err = numpy.where(dvzo <= 0.1)
292 dvzo[err] = 0.1
293
294 #Zonal Drifts
295 dt = self.header_file['nint']*self.ipp / 1.5E5
296 coh = numpy.sqrt(numpy.abs(dccf))
297 err = numpy.where(coh >= 1.0)
298 coh[err] = numpy.sqrt(0.99999)
299
300 err = numpy.where(coh <= 0.1)
301 coh[err] = numpy.sqrt(0.1)
302
303 vxo = numpy.arctan2(dccf.imag, dccf.real)*self.header_rec['h0']*1.0E3/(self.kd*dt)
304 dvxo = numpy.sqrt(1.0 - coh*coh)*self.header_rec['h0']*1.0E3/(numpy.sqrt(self.header_file['nint']*self.header_file['navg'])*coh*self.kd*dt)
305
306 err = numpy.where(dvxo <= 0.1)
307 dvxo[err] = 0.1
308
309 return vzo, dvzo, vxo, dvxo, sno10
310
311 def set_output(self):
312 '''
313 Storing data from databuffer to dataOut object
314 '''
315
316 self.dataOut.data_SNR = self.buffer[4].reshape(1, -1)
317 self.dataOut.heightList = self.heights
318 self.dataOut.data_param = self.buffer[0:4,]
319 self.dataOut.utctimeInit = self.time
320 self.dataOut.utctime = self.time
321 self.dataOut.useLocalTime = True
322 self.dataOut.paramInterval = self.interval
323 self.dataOut.timezone = self.timezone
324 self.dataOut.sizeOfFile = self.sizeOfFile
325 self.dataOut.flagNoData = False
326 self.dataOut.flagDiscontinuousBlock = self.flagDiscontinuousBlock
327
328 def getData(self):
329 '''
330 Storing data from databuffer to dataOut object
331 '''
332 if self.flagNoMoreFiles:
333 self.dataOut.flagNoData = True
334 log.success('No file left to process', self.name)
335 return 0
336
337 if not self.readNextBlock():
338 self.dataOut.flagNoData = True
339 return 0
340
341 self.set_output()
342
343 return 1
@@ -1,109 +1,112
1 1 ## CHANGELOG:
2 2
3 3 ### 2.3
4 * Added support for Madrigal formats (reading/writing).
5 * Added support for reading BLTR parameters (*.sswma).
6 * Added support for reading Julia format (*.dat).
4 7 * Added high order function `MPProject` for multiprocessing scripts.
5 8 * Added two new Processing Units `PublishData` and `ReceiverData` for receiving and sending dataOut through multiple ways (tcp, ipc, inproc).
6 9 * Added a new graphics Processing Unit `PlotterReceiver`. It is decoupled from normal processing sequence with support for data generated by multiprocessing scripts.
7 10 * Added support for sending realtime graphic to web server.
8 11 * GUI command `schain` is now `schainGUI`.
9 12 * Added a CLI tool named `schain`.
10 13 * Scripts templates can be now generated with `schain generate`.
11 14 * Now it is possible to search Processing Units and Operations with `schain search [module]` to get the right name and its allowed parameters.
12 15 * `schain xml` to run xml scripts.
13 16 * Added suggestions when parameters are poorly written.
14 17 * `Controller.start()` now runs in a different process than the process calling it.
15 18 * Added `schainpy.utils.log` for log standarization.
16 19 * Running script on online mode no longer ignores date and hour. Issue #1109.
17 20 * Added support for receving voltage data directly from JARS (tcp, ipc).
18 21 * Updated README for MAC OS GUI installation.
19 22 * Setup now installs numpy.
20 23
21 24 ### 2.2.6
22 25 * Graphics generated by the GUI are now the same as generated by scripts. Issue #1074.
23 26 * Added support for C extensions.
24 27 * Function `hildebrand_sehkon` optimized with a C wrapper.
25 28 * Numpy version updated.
26 29 * Migration to GIT.
27 30
28 31 ### 2.2.5:
29 32 * splitProfiles and combineProfiles modules were added to VoltageProc and Signal Chain GUI.
30 33 * nProfiles of USRP data (hdf5) is the number of profiles thera are in one second.
31 34 * jroPlotter works directly with data objects instead of dictionaries
32 35 * script "schain" was added to Signal Chain installer
33 36
34 37 ### 2.2.4.1:
35 38 * jroIO_usrp.py is update to read Sandra's data
36 39 * decimation in Spectra and RTI plots is always enabled.
37 40 * time* window option added to GUI
38 41
39 42 ### 2.2.4:
40 43 * jroproc_spectra_lags.py added to schainpy
41 44 * Bug fixed in schainGUI: ProcUnit was created with the same id in some cases.
42 45 * Bug fixed in jroHeaderIO: Header size validation.
43 46
44 47 ### 2.2.3.1:
45 48 * Filtering block by time has been added.
46 49 * Bug fixed plotting RTI, CoherenceMap and others using xmin and xmax parameters. The first day worked
47 50 properly but the next days did not.
48 51
49 52 ### 2.2.3:
50 53 * Bug fixed in GUI: Error getting(reading) Code value
51 54 * Bug fixed in GUI: Flip option always needs channelList field
52 55 * Bug fixed in jrodata: when one branch modified a value in "dataOut" (example: dataOut.code) this value
53 56 was modified for every branch (because this was a reference). It was modified in data.copy()
54 57 * Bug fixed in jroproc_voltage.profileSelector(): rangeList replaces to profileRangeList.
55 58
56 59 ### 2.2.2:
57 60 * VoltageProc: ProfileSelector, Reshape, Decoder with nTxs!=1 and getblock=True was tested
58 61 * Rawdata and testRawdata.py added to Signal Chain project
59 62
60 63 ### 2.2.1:
61 64 * Bugs fixed in GUI
62 65 * Views were improved in GUI
63 66 * Support to MST* ISR experiments
64 67 * Bug fixed getting noise using hyldebrant. (minimum number of points > 20%)
65 68 * handleError added to jroplotter.py
66 69
67 70 ### 2.2.0:
68 71 * GUI: use of external plotter
69 72 * Compatible with matplotlib 1.5.0
70 73
71 74 ### 2.1.5:
72 75 * serializer module added to Signal Chain
73 76 * jroplotter.py added to Signal Chain
74 77
75 78 ### 2.1.4.2:
76 79 * A new Plotter Class was added
77 80 * Project.start() does not accept filename as a parameter anymore
78 81
79 82 ### 2.1.4.1:
80 83 * Send notifications when an error different to ValueError is detected
81 84
82 85 ### 2.1.4:
83 86 * Sending error notifications to signal chain administrator
84 87 * Login to email server added
85 88
86 89 ### 2.1.3.3:
87 90 * Colored Button Icons were added to GUI
88 91
89 92 ### 2.1.3.2:
90 93 * GUI: user interaction enhanced
91 94 * controller_api.py: Safe access to ControllerThead
92 95
93 96 ### 2.1.3.1:
94 97 * GUI: every icon were resized
95 98 * jroproc_voltage.py: Print a message when "Read from code" option is selected and the code is not defined inside data file
96 99
97 100 ### 2.1.3:
98 101 * jroplot_heispectra.py: SpectraHeisScope was not showing the right channels
99 102 * jroproc_voltage.py: Bug fixed selecting profiles (self.nProfiles took a wrong value),
100 103 Bug fixed selecting heights by block (selecting profiles instead heights)
101 104 * jroproc_voltage.py: New feature added: decoding data by block using FFT.
102 105 * jroIO_heispectra.py: Bug fixed in FitsReader. Using local Fits instance instead schainpy.mode.data.jrodata.Fits.
103 106 * jroIO_heispectra.py: Channel index list does not exist.
104 107
105 108 ### 2.1.2:
106 109 * jroutils_ftp.py: Bug fixed, Any error sending file stopped the Server Thread
107 110 Server thread opens and closes remote server each time file list is sent
108 111 * jroplot_spectra.py: Noise path was not being created when noise data is saved.
109 112 * jroIO_base.py: startTime can be greater than endTime. Example: SpreadF [18:00 * 07:00] No newline at end of file
@@ -1,952 +1,955
1 1
2 2 import os
3 3 import time
4 4 import glob
5 5 import datetime
6 6 from multiprocessing import Process
7 7
8 8 import zmq
9 9 import numpy
10 10 import matplotlib
11 11 import matplotlib.pyplot as plt
12 12 from mpl_toolkits.axes_grid1 import make_axes_locatable
13 13 from matplotlib.ticker import FuncFormatter, LinearLocator, MultipleLocator
14 14
15 15 from schainpy.model.proc.jroproc_base import Operation
16 16 from schainpy.utils import log
17 17
18 18 jet_values = matplotlib.pyplot.get_cmap('jet', 100)(numpy.arange(100))[10:90]
19 19 blu_values = matplotlib.pyplot.get_cmap(
20 20 'seismic_r', 20)(numpy.arange(20))[10:15]
21 21 ncmap = matplotlib.colors.LinearSegmentedColormap.from_list(
22 22 'jro', numpy.vstack((blu_values, jet_values)))
23 23 matplotlib.pyplot.register_cmap(cmap=ncmap)
24 24
25 25 CMAPS = [plt.get_cmap(s) for s in ('jro', 'jet', 'RdBu_r', 'seismic')]
26 26
27 27
28 28 def figpause(interval):
29 29 backend = plt.rcParams['backend']
30 30 if backend in matplotlib.rcsetup.interactive_bk:
31 31 figManager = matplotlib._pylab_helpers.Gcf.get_active()
32 32 if figManager is not None:
33 33 canvas = figManager.canvas
34 34 if canvas.figure.stale:
35 35 canvas.draw()
36 36 canvas.start_event_loop(interval)
37 37 return
38 38
39 39
40 40 class PlotData(Operation, Process):
41 41 '''
42 42 Base class for Schain plotting operations
43 43 '''
44 44
45 45 CODE = 'Figure'
46 46 colormap = 'jro'
47 47 bgcolor = 'white'
48 48 CONFLATE = False
49 49 __MAXNUMX = 80
50 50 __missing = 1E30
51 51
52 52 __attrs__ = ['show', 'save', 'xmin', 'xmax', 'ymin', 'ymax', 'zmin', 'zmax',
53 53 'zlimits', 'xlabel', 'ylabel', 'xaxis','cb_label', 'title',
54 54 'colorbar', 'bgcolor', 'width', 'height', 'localtime', 'oneFigure',
55 55 'showprofile', 'decimation']
56 56
57 57 def __init__(self, **kwargs):
58 58
59 59 Operation.__init__(self, plot=True, **kwargs)
60 Process.__init__(self)
60 Process.__init__(self)
61
61 62 self.kwargs['code'] = self.CODE
62 63 self.mp = False
63 64 self.data = None
64 65 self.isConfig = False
65 66 self.figures = []
66 67 self.axes = []
67 68 self.cb_axes = []
68 69 self.localtime = kwargs.pop('localtime', True)
69 70 self.show = kwargs.get('show', True)
70 71 self.save = kwargs.get('save', False)
71 72 self.colormap = kwargs.get('colormap', self.colormap)
72 73 self.colormap_coh = kwargs.get('colormap_coh', 'jet')
73 74 self.colormap_phase = kwargs.get('colormap_phase', 'RdBu_r')
74 75 self.colormaps = kwargs.get('colormaps', None)
75 76 self.bgcolor = kwargs.get('bgcolor', self.bgcolor)
76 77 self.showprofile = kwargs.get('showprofile', False)
77 78 self.title = kwargs.get('wintitle', self.CODE.upper())
78 79 self.cb_label = kwargs.get('cb_label', None)
79 80 self.cb_labels = kwargs.get('cb_labels', None)
80 81 self.xaxis = kwargs.get('xaxis', 'frequency')
81 82 self.zmin = kwargs.get('zmin', None)
82 83 self.zmax = kwargs.get('zmax', None)
83 84 self.zlimits = kwargs.get('zlimits', None)
84 85 self.xmin = kwargs.get('xmin', None)
85 86 self.xmax = kwargs.get('xmax', None)
86 87 self.xrange = kwargs.get('xrange', 24)
87 88 self.ymin = kwargs.get('ymin', None)
88 89 self.ymax = kwargs.get('ymax', None)
89 90 self.xlabel = kwargs.get('xlabel', None)
90 91 self.__MAXNUMY = kwargs.get('decimation', 200)
91 92 self.showSNR = kwargs.get('showSNR', False)
92 93 self.oneFigure = kwargs.get('oneFigure', True)
93 94 self.width = kwargs.get('width', None)
94 95 self.height = kwargs.get('height', None)
95 96 self.colorbar = kwargs.get('colorbar', True)
96 97 self.factors = kwargs.get('factors', [1, 1, 1, 1, 1, 1, 1, 1])
97 self.titles = ['' for __ in range(16)]
98 self.titles = kwargs.get('titles', [])
98 99 self.polar = False
99 100
100 101 def __fmtTime(self, x, pos):
101 102 '''
102 103 '''
103 104
104 105 return '{}'.format(self.getDateTime(x).strftime('%H:%M'))
105 106
106 107 def __setup(self):
107 108 '''
108 109 Common setup for all figures, here figures and axes are created
109 110 '''
110 111
111 112 if self.CODE not in self.data:
112 113 raise ValueError(log.error('Missing data for {}'.format(self.CODE),
113 114 self.name))
114 115
115 116 self.setup()
116 117
117 118 self.time_label = 'LT' if self.localtime else 'UTC'
118 119 if self.data.localtime:
119 120 self.getDateTime = datetime.datetime.fromtimestamp
120 121 else:
121 122 self.getDateTime = datetime.datetime.utcfromtimestamp
122 123
123 124 if self.width is None:
124 125 self.width = 8
125 126
126 127 self.figures = []
127 128 self.axes = []
128 129 self.cb_axes = []
129 130 self.pf_axes = []
130 131 self.cmaps = []
131 132
132 133 size = '15%' if self.ncols == 1 else '30%'
133 134 pad = '4%' if self.ncols == 1 else '8%'
134 135
135 136 if self.oneFigure:
136 137 if self.height is None:
137 138 self.height = 1.4 * self.nrows + 1
138 139 fig = plt.figure(figsize=(self.width, self.height),
139 140 edgecolor='k',
140 141 facecolor='w')
141 142 self.figures.append(fig)
142 143 for n in range(self.nplots):
143 144 ax = fig.add_subplot(self.nrows, self.ncols,
144 145 n + 1, polar=self.polar)
145 146 ax.tick_params(labelsize=8)
146 147 ax.firsttime = True
147 148 ax.index = 0
148 149 ax.press = None
149 150 self.axes.append(ax)
150 151 if self.showprofile:
151 152 cax = self.__add_axes(ax, size=size, pad=pad)
152 153 cax.tick_params(labelsize=8)
153 154 self.pf_axes.append(cax)
154 155 else:
155 156 if self.height is None:
156 157 self.height = 3
157 158 for n in range(self.nplots):
158 159 fig = plt.figure(figsize=(self.width, self.height),
159 160 edgecolor='k',
160 161 facecolor='w')
161 162 ax = fig.add_subplot(1, 1, 1, polar=self.polar)
162 163 ax.tick_params(labelsize=8)
163 164 ax.firsttime = True
164 165 ax.index = 0
165 166 ax.press = None
166 167 self.figures.append(fig)
167 168 self.axes.append(ax)
168 169 if self.showprofile:
169 170 cax = self.__add_axes(ax, size=size, pad=pad)
170 171 cax.tick_params(labelsize=8)
171 172 self.pf_axes.append(cax)
172 173
173 174 for n in range(self.nrows):
174 175 if self.colormaps is not None:
175 176 cmap = plt.get_cmap(self.colormaps[n])
176 177 else:
177 178 cmap = plt.get_cmap(self.colormap)
178 179 cmap.set_bad(self.bgcolor, 1.)
179 180 self.cmaps.append(cmap)
180 181
181 182 for fig in self.figures:
182 183 fig.canvas.mpl_connect('key_press_event', self.OnKeyPress)
183 184 fig.canvas.mpl_connect('scroll_event', self.OnBtnScroll)
184 185 fig.canvas.mpl_connect('button_press_event', self.onBtnPress)
185 186 fig.canvas.mpl_connect('motion_notify_event', self.onMotion)
186 187 fig.canvas.mpl_connect('button_release_event', self.onBtnRelease)
187 188 if self.show:
188 189 fig.show()
189 190
190 191 def OnKeyPress(self, event):
191 192 '''
192 193 Event for pressing keys (up, down) change colormap
193 194 '''
194 195 ax = event.inaxes
195 196 if ax in self.axes:
196 197 if event.key == 'down':
197 198 ax.index += 1
198 199 elif event.key == 'up':
199 200 ax.index -= 1
200 201 if ax.index < 0:
201 202 ax.index = len(CMAPS) - 1
202 203 elif ax.index == len(CMAPS):
203 204 ax.index = 0
204 205 cmap = CMAPS[ax.index]
205 206 ax.cbar.set_cmap(cmap)
206 207 ax.cbar.draw_all()
207 208 ax.plt.set_cmap(cmap)
208 209 ax.cbar.patch.figure.canvas.draw()
209 210 self.colormap = cmap.name
210 211
211 212 def OnBtnScroll(self, event):
212 213 '''
213 214 Event for scrolling, scale figure
214 215 '''
215 216 cb_ax = event.inaxes
216 217 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
217 218 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
218 219 pt = ax.cbar.ax.bbox.get_points()[:, 1]
219 220 nrm = ax.cbar.norm
220 221 vmin, vmax, p0, p1, pS = (
221 222 nrm.vmin, nrm.vmax, pt[0], pt[1], event.y)
222 223 scale = 2 if event.step == 1 else 0.5
223 224 point = vmin + (vmax - vmin) / (p1 - p0) * (pS - p0)
224 225 ax.cbar.norm.vmin = point - scale * (point - vmin)
225 226 ax.cbar.norm.vmax = point - scale * (point - vmax)
226 227 ax.plt.set_norm(ax.cbar.norm)
227 228 ax.cbar.draw_all()
228 229 ax.cbar.patch.figure.canvas.draw()
229 230
230 231 def onBtnPress(self, event):
231 232 '''
232 233 Event for mouse button press
233 234 '''
234 235 cb_ax = event.inaxes
235 236 if cb_ax is None:
236 237 return
237 238
238 239 if cb_ax in [ax.cbar.ax for ax in self.axes if ax.cbar]:
239 240 cb_ax.press = event.x, event.y
240 241 else:
241 242 cb_ax.press = None
242 243
243 244 def onMotion(self, event):
244 245 '''
245 246 Event for move inside colorbar
246 247 '''
247 248 cb_ax = event.inaxes
248 249 if cb_ax is None:
249 250 return
250 251 if cb_ax not in [ax.cbar.ax for ax in self.axes if ax.cbar]:
251 252 return
252 253 if cb_ax.press is None:
253 254 return
254 255
255 256 ax = [ax for ax in self.axes if cb_ax == ax.cbar.ax][0]
256 257 xprev, yprev = cb_ax.press
257 258 dx = event.x - xprev
258 259 dy = event.y - yprev
259 260 cb_ax.press = event.x, event.y
260 261 scale = ax.cbar.norm.vmax - ax.cbar.norm.vmin
261 262 perc = 0.03
262 263
263 264 if event.button == 1:
264 265 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
265 266 ax.cbar.norm.vmax -= (perc * scale) * numpy.sign(dy)
266 267 elif event.button == 3:
267 268 ax.cbar.norm.vmin -= (perc * scale) * numpy.sign(dy)
268 269 ax.cbar.norm.vmax += (perc * scale) * numpy.sign(dy)
269 270
270 271 ax.cbar.draw_all()
271 272 ax.plt.set_norm(ax.cbar.norm)
272 273 ax.cbar.patch.figure.canvas.draw()
273 274
274 275 def onBtnRelease(self, event):
275 276 '''
276 277 Event for mouse button release
277 278 '''
278 279 cb_ax = event.inaxes
279 280 if cb_ax is not None:
280 281 cb_ax.press = None
281 282
282 283 def __add_axes(self, ax, size='30%', pad='8%'):
283 284 '''
284 285 Add new axes to the given figure
285 286 '''
286 287 divider = make_axes_locatable(ax)
287 288 nax = divider.new_horizontal(size=size, pad=pad)
288 289 ax.figure.add_axes(nax)
289 290 return nax
290 291
291 292 self.setup()
292 293
293 294 def setup(self):
294 295 '''
295 296 This method should be implemented in the child class, the following
296 297 attributes should be set:
297 298
298 299 self.nrows: number of rows
299 300 self.ncols: number of cols
300 301 self.nplots: number of plots (channels or pairs)
301 302 self.ylabel: label for Y axes
302 303 self.titles: list of axes title
303 304
304 305 '''
305 306 raise(NotImplementedError, 'Implement this method in child class')
306 307
307 308 def fill_gaps(self, x_buffer, y_buffer, z_buffer):
308 309 '''
309 310 Create a masked array for missing data
310 311 '''
311 312 if x_buffer.shape[0] < 2:
312 313 return x_buffer, y_buffer, z_buffer
313 314
314 315 deltas = x_buffer[1:] - x_buffer[0:-1]
315 316 x_median = numpy.median(deltas)
316 317
317 318 index = numpy.where(deltas > 5 * x_median)
318 319
319 320 if len(index[0]) != 0:
320 321 z_buffer[::, index[0], ::] = self.__missing
321 322 z_buffer = numpy.ma.masked_inside(z_buffer,
322 323 0.99 * self.__missing,
323 324 1.01 * self.__missing)
324 325
325 326 return x_buffer, y_buffer, z_buffer
326 327
327 328 def decimate(self):
328 329
329 330 # dx = int(len(self.x)/self.__MAXNUMX) + 1
330 331 dy = int(len(self.y) / self.__MAXNUMY) + 1
331 332
332 333 # x = self.x[::dx]
333 334 x = self.x
334 335 y = self.y[::dy]
335 336 z = self.z[::, ::, ::dy]
336 337
337 338 return x, y, z
338 339
339 340 def format(self):
340 341 '''
341 342 Set min and max values, labels, ticks and titles
342 343 '''
343 344
344 345 if self.xmin is None:
345 346 xmin = self.min_time
346 347 else:
347 348 if self.xaxis is 'time':
348 349 dt = self.getDateTime(self.min_time)
349 350 xmin = (dt.replace(hour=int(self.xmin), minute=0, second=0) -
350 351 datetime.datetime(1970, 1, 1)).total_seconds()
351 352 if self.data.localtime:
352 353 xmin += time.timezone
353 354 else:
354 355 xmin = self.xmin
355 356
356 357 if self.xmax is None:
357 358 xmax = xmin + self.xrange * 60 * 60
358 359 else:
359 360 if self.xaxis is 'time':
360 361 dt = self.getDateTime(self.max_time)
361 362 xmax = (dt.replace(hour=int(self.xmax), minute=59, second=59) -
362 363 datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=1)).total_seconds()
363 364 if self.data.localtime:
364 365 xmax += time.timezone
365 366 else:
366 367 xmax = self.xmax
367 368
368 369 ymin = self.ymin if self.ymin else numpy.nanmin(self.y)
369 370 ymax = self.ymax if self.ymax else numpy.nanmax(self.y)
370 371
371 372 Y = numpy.array([10, 20, 50, 100, 200, 500, 1000, 2000])
372 373 i = 1 if numpy.where(ymax-ymin < Y)[0][0] < 0 else numpy.where(ymax-ymin < Y)[0][0]
373 374 ystep = Y[i] / 5
374 375
375 376 for n, ax in enumerate(self.axes):
376 377 if ax.firsttime:
377 378 ax.set_facecolor(self.bgcolor)
378 379 ax.yaxis.set_major_locator(MultipleLocator(ystep))
379 380 if self.xaxis is 'time':
380 381 ax.xaxis.set_major_formatter(FuncFormatter(self.__fmtTime))
381 382 ax.xaxis.set_major_locator(LinearLocator(9))
382 383 if self.xlabel is not None:
383 384 ax.set_xlabel(self.xlabel)
384 385 ax.set_ylabel(self.ylabel)
385 386 ax.firsttime = False
386 387 if self.showprofile:
387 388 self.pf_axes[n].set_ylim(ymin, ymax)
388 389 self.pf_axes[n].set_xlim(self.zmin, self.zmax)
389 390 self.pf_axes[n].set_xlabel('dB')
390 391 self.pf_axes[n].grid(b=True, axis='x')
391 392 [tick.set_visible(False)
392 393 for tick in self.pf_axes[n].get_yticklabels()]
393 394 if self.colorbar:
394 395 ax.cbar = plt.colorbar(
395 396 ax.plt, ax=ax, fraction=0.05, pad=0.02, aspect=10)
396 397 ax.cbar.ax.tick_params(labelsize=8)
397 398 ax.cbar.ax.press = None
398 399 if self.cb_label:
399 400 ax.cbar.set_label(self.cb_label, size=8)
400 401 elif self.cb_labels:
401 402 ax.cbar.set_label(self.cb_labels[n], size=8)
402 403 else:
403 404 ax.cbar = None
404 405
405 406 if not self.polar:
406 407 ax.set_xlim(xmin, xmax)
407 408 ax.set_ylim(ymin, ymax)
408 409 ax.set_title('{} - {} {}'.format(
409 410 self.titles[n],
410 411 self.getDateTime(self.max_time).strftime('%H:%M:%S'),
411 412 self.time_label),
412 413 size=8)
413 414 else:
414 415 ax.set_title('{}'.format(self.titles[n]), size=8)
415 416 ax.set_ylim(0, 90)
416 417 ax.set_yticks(numpy.arange(0, 90, 20))
417 418 ax.yaxis.labelpad = 40
418 419
419 420 def __plot(self):
420 421 '''
421 422 '''
422 423 log.success('Plotting', self.name)
423 424
424 425 self.plot()
425 426 self.format()
426 427
427 428 for n, fig in enumerate(self.figures):
428 429 if self.nrows == 0 or self.nplots == 0:
429 430 log.warning('No data', self.name)
430 431 fig.text(0.5, 0.5, 'No Data', fontsize='large', ha='center')
431 432 fig.canvas.manager.set_window_title(self.CODE)
432 433 continue
433 434
434 435 fig.tight_layout()
435 436 fig.canvas.manager.set_window_title('{} - {}'.format(self.title,
436 437 self.getDateTime(self.max_time).strftime('%Y/%m/%d')))
437 # fig.canvas.draw()
438 fig.canvas.draw()
438 439
439 440 if self.save and self.data.ended:
440 441 channels = range(self.nrows)
441 442 if self.oneFigure:
442 443 label = ''
443 444 else:
444 445 label = '_{}'.format(channels[n])
445 446 figname = os.path.join(
446 447 self.save,
447 448 '{}{}_{}.png'.format(
448 449 self.CODE,
449 450 label,
450 self.getDateTime(self.saveTime).strftime('%y%m%d_%H%M%S')
451 self.getDateTime(self.saveTime).strftime(
452 '%y%m%d_%H%M%S'),
451 453 )
452 454 )
453 455 log.log('Saving figure: {}'.format(figname), self.name)
454 456 fig.savefig(figname)
455 457
456 458 def plot(self):
457 459 '''
458 460 '''
459 461 raise(NotImplementedError, 'Implement this method in child class')
460 462
461 463 def run(self):
462 464
463 465 log.success('Starting', self.name)
464 466
465 467 context = zmq.Context()
466 468 receiver = context.socket(zmq.SUB)
467 469 receiver.setsockopt(zmq.SUBSCRIBE, '')
468 470 receiver.setsockopt(zmq.CONFLATE, self.CONFLATE)
469 471
470 472 if 'server' in self.kwargs['parent']:
471 473 receiver.connect(
472 474 'ipc:///tmp/{}.plots'.format(self.kwargs['parent']['server']))
473 475 else:
474 476 receiver.connect("ipc:///tmp/zmq.plots")
475 477
476 478 while True:
477 479 try:
478 480 self.data = receiver.recv_pyobj(flags=zmq.NOBLOCK)
479 481 if self.data.localtime and self.localtime:
480 482 self.times = self.data.times
481 483 elif self.data.localtime and not self.localtime:
482 484 self.times = self.data.times + time.timezone
483 485 elif not self.data.localtime and self.localtime:
484 486 self.times = self.data.times - time.timezone
485 487 else:
486 488 self.times = self.data.times
487 489
488 490 self.min_time = self.times[0]
489 491 self.max_time = self.times[-1]
490 492
491 493 if self.isConfig is False:
492 494 self.__setup()
493 495 self.isConfig = True
494 496
495 497 self.__plot()
496 498
497 499 except zmq.Again as e:
498 500 log.log('Waiting for data...')
499 501 if self.data:
500 502 figpause(self.data.throttle)
501 503 else:
502 504 time.sleep(2)
503 505
504 506 def close(self):
505 507 if self.data:
506 508 self.__plot()
507 509
508 510
509 511 class PlotSpectraData(PlotData):
510 512 '''
511 513 Plot for Spectra data
512 514 '''
513 515
514 516 CODE = 'spc'
515 517 colormap = 'jro'
516 518
517 519 def setup(self):
518 520 self.nplots = len(self.data.channels)
519 521 self.ncols = int(numpy.sqrt(self.nplots) + 0.9)
520 522 self.nrows = int((1.0 * self.nplots / self.ncols) + 0.9)
521 523 self.width = 3.4 * self.ncols
522 524 self.height = 3 * self.nrows
523 525 self.cb_label = 'dB'
524 526 if self.showprofile:
525 527 self.width += 0.8 * self.ncols
526 528
527 529 self.ylabel = 'Range [Km]'
528 530
529 531 def plot(self):
530 532 if self.xaxis == "frequency":
531 533 x = self.data.xrange[0]
532 534 self.xlabel = "Frequency (kHz)"
533 535 elif self.xaxis == "time":
534 536 x = self.data.xrange[1]
535 537 self.xlabel = "Time (ms)"
536 538 else:
537 539 x = self.data.xrange[2]
538 540 self.xlabel = "Velocity (m/s)"
539 541
540 542 if self.CODE == 'spc_mean':
541 543 x = self.data.xrange[2]
542 544 self.xlabel = "Velocity (m/s)"
543 545
544 546 self.titles = []
545 547
546 548 y = self.data.heights
547 549 self.y = y
548 550 z = self.data['spc']
549 551
550 552 for n, ax in enumerate(self.axes):
551 553 noise = self.data['noise'][n][-1]
552 554 if self.CODE == 'spc_mean':
553 555 mean = self.data['mean'][n][-1]
554 556 if ax.firsttime:
555 557 self.xmax = self.xmax if self.xmax else numpy.nanmax(x)
556 558 self.xmin = self.xmin if self.xmin else -self.xmax
557 559 self.zmin = self.zmin if self.zmin else numpy.nanmin(z)
558 560 self.zmax = self.zmax if self.zmax else numpy.nanmax(z)
559 561 ax.plt = ax.pcolormesh(x, y, z[n].T,
560 562 vmin=self.zmin,
561 563 vmax=self.zmax,
562 564 cmap=plt.get_cmap(self.colormap)
563 565 )
564 566
565 567 if self.showprofile:
566 568 ax.plt_profile = self.pf_axes[n].plot(
567 569 self.data['rti'][n][-1], y)[0]
568 570 ax.plt_noise = self.pf_axes[n].plot(numpy.repeat(noise, len(y)), y,
569 571 color="k", linestyle="dashed", lw=1)[0]
570 572 if self.CODE == 'spc_mean':
571 573 ax.plt_mean = ax.plot(mean, y, color='k')[0]
572 574 else:
573 575 ax.plt.set_array(z[n].T.ravel())
574 576 if self.showprofile:
575 577 ax.plt_profile.set_data(self.data['rti'][n][-1], y)
576 578 ax.plt_noise.set_data(numpy.repeat(noise, len(y)), y)
577 579 if self.CODE == 'spc_mean':
578 580 ax.plt_mean.set_data(mean, y)
579 581
580 582 self.titles.append('CH {}: {:3.2f}dB'.format(n, noise))
581 583 self.saveTime = self.max_time
582 584
583 585
584 586 class PlotCrossSpectraData(PlotData):
585 587
586 588 CODE = 'cspc'
587 589 zmin_coh = None
588 590 zmax_coh = None
589 591 zmin_phase = None
590 592 zmax_phase = None
591 593
592 594 def setup(self):
593 595
594 596 self.ncols = 4
595 597 self.nrows = len(self.data.pairs)
596 598 self.nplots = self.nrows * 4
597 599 self.width = 3.4 * self.ncols
598 600 self.height = 3 * self.nrows
599 601 self.ylabel = 'Range [Km]'
600 602 self.showprofile = False
601 603
602 604 def plot(self):
603 605
604 606 if self.xaxis == "frequency":
605 607 x = self.data.xrange[0]
606 608 self.xlabel = "Frequency (kHz)"
607 609 elif self.xaxis == "time":
608 610 x = self.data.xrange[1]
609 611 self.xlabel = "Time (ms)"
610 612 else:
611 613 x = self.data.xrange[2]
612 614 self.xlabel = "Velocity (m/s)"
613 615
614 616 self.titles = []
615 617
616 618 y = self.data.heights
617 619 self.y = y
618 620 spc = self.data['spc']
619 621 cspc = self.data['cspc']
620 622
621 623 for n in range(self.nrows):
622 624 noise = self.data['noise'][n][-1]
623 625 pair = self.data.pairs[n]
624 626 ax = self.axes[4 * n]
625 627 ax3 = self.axes[4 * n + 3]
626 628 if ax.firsttime:
627 629 self.xmax = self.xmax if self.xmax else numpy.nanmax(x)
628 630 self.xmin = self.xmin if self.xmin else -self.xmax
629 631 self.zmin = self.zmin if self.zmin else numpy.nanmin(spc)
630 632 self.zmax = self.zmax if self.zmax else numpy.nanmax(spc)
631 633 ax.plt = ax.pcolormesh(x, y, spc[pair[0]].T,
632 634 vmin=self.zmin,
633 635 vmax=self.zmax,
634 636 cmap=plt.get_cmap(self.colormap)
635 637 )
636 638 else:
637 639 ax.plt.set_array(spc[pair[0]].T.ravel())
638 640 self.titles.append('CH {}: {:3.2f}dB'.format(n, noise))
639 641
640 642 ax = self.axes[4 * n + 1]
641 643 if ax.firsttime:
642 644 ax.plt = ax.pcolormesh(x, y, spc[pair[1]].T,
643 645 vmin=self.zmin,
644 646 vmax=self.zmax,
645 647 cmap=plt.get_cmap(self.colormap)
646 648 )
647 649 else:
648 650 ax.plt.set_array(spc[pair[1]].T.ravel())
649 651 self.titles.append('CH {}: {:3.2f}dB'.format(n, noise))
650 652
651 653 out = cspc[n] / numpy.sqrt(spc[pair[0]] * spc[pair[1]])
652 654 coh = numpy.abs(out)
653 655 phase = numpy.arctan2(out.imag, out.real) * 180 / numpy.pi
654 656
655 657 ax = self.axes[4 * n + 2]
656 658 if ax.firsttime:
657 659 ax.plt = ax.pcolormesh(x, y, coh.T,
658 660 vmin=0,
659 661 vmax=1,
660 662 cmap=plt.get_cmap(self.colormap_coh)
661 663 )
662 664 else:
663 665 ax.plt.set_array(coh.T.ravel())
664 666 self.titles.append(
665 667 'Coherence Ch{} * Ch{}'.format(pair[0], pair[1]))
666 668
667 669 ax = self.axes[4 * n + 3]
668 670 if ax.firsttime:
669 671 ax.plt = ax.pcolormesh(x, y, phase.T,
670 672 vmin=-180,
671 673 vmax=180,
672 674 cmap=plt.get_cmap(self.colormap_phase)
673 675 )
674 676 else:
675 677 ax.plt.set_array(phase.T.ravel())
676 678 self.titles.append('Phase CH{} * CH{}'.format(pair[0], pair[1]))
677 679
678 680 self.saveTime = self.max_time
679 681
680 682
681 683 class PlotSpectraMeanData(PlotSpectraData):
682 684 '''
683 685 Plot for Spectra and Mean
684 686 '''
685 687 CODE = 'spc_mean'
686 688 colormap = 'jro'
687 689
688 690
689 691 class PlotRTIData(PlotData):
690 692 '''
691 693 Plot for RTI data
692 694 '''
693 695
694 696 CODE = 'rti'
695 697 colormap = 'jro'
696 698
697 699 def setup(self):
698 700 self.xaxis = 'time'
699 701 self.ncols = 1
700 702 self.nrows = len(self.data.channels)
701 703 self.nplots = len(self.data.channels)
702 704 self.ylabel = 'Range [Km]'
703 705 self.cb_label = 'dB'
704 706 self.titles = ['{} Channel {}'.format(
705 707 self.CODE.upper(), x) for x in range(self.nrows)]
706 708
707 709 def plot(self):
708 710 self.x = self.times
709 711 self.y = self.data.heights
710 712 self.z = self.data[self.CODE]
711 713 self.z = numpy.ma.masked_invalid(self.z)
712 714
713 715 for n, ax in enumerate(self.axes):
714 716 x, y, z = self.fill_gaps(*self.decimate())
715 717 self.zmin = self.zmin if self.zmin else numpy.min(self.z)
716 718 self.zmax = self.zmax if self.zmax else numpy.max(self.z)
717 719 if ax.firsttime:
718 720 ax.plt = ax.pcolormesh(x, y, z[n].T,
719 721 vmin=self.zmin,
720 722 vmax=self.zmax,
721 723 cmap=plt.get_cmap(self.colormap)
722 724 )
723 725 if self.showprofile:
724 726 ax.plot_profile = self.pf_axes[n].plot(
725 727 self.data['rti'][n][-1], self.y)[0]
726 728 ax.plot_noise = self.pf_axes[n].plot(numpy.repeat(self.data['noise'][n][-1], len(self.y)), self.y,
727 729 color="k", linestyle="dashed", lw=1)[0]
728 730 else:
729 731 ax.collections.remove(ax.collections[0])
730 732 ax.plt = ax.pcolormesh(x, y, z[n].T,
731 733 vmin=self.zmin,
732 734 vmax=self.zmax,
733 735 cmap=plt.get_cmap(self.colormap)
734 736 )
735 737 if self.showprofile:
736 738 ax.plot_profile.set_data(self.data['rti'][n][-1], self.y)
737 739 ax.plot_noise.set_data(numpy.repeat(
738 740 self.data['noise'][n][-1], len(self.y)), self.y)
739 741
740 742 self.saveTime = self.min_time
741 743
742 744
743 745 class PlotCOHData(PlotRTIData):
744 746 '''
745 747 Plot for Coherence data
746 748 '''
747 749
748 750 CODE = 'coh'
749 751
750 752 def setup(self):
751 753 self.xaxis = 'time'
752 754 self.ncols = 1
753 755 self.nrows = len(self.data.pairs)
754 756 self.nplots = len(self.data.pairs)
755 757 self.ylabel = 'Range [Km]'
756 758 if self.CODE == 'coh':
757 759 self.cb_label = ''
758 760 self.titles = [
759 761 'Coherence Map Ch{} * Ch{}'.format(x[0], x[1]) for x in self.data.pairs]
760 762 else:
761 763 self.cb_label = 'Degrees'
762 764 self.titles = [
763 765 'Phase Map Ch{} * Ch{}'.format(x[0], x[1]) for x in self.data.pairs]
764 766
765 767
766 768 class PlotPHASEData(PlotCOHData):
767 769 '''
768 770 Plot for Phase map data
769 771 '''
770 772
771 773 CODE = 'phase'
772 774 colormap = 'seismic'
773 775
774 776
775 777 class PlotNoiseData(PlotData):
776 778 '''
777 779 Plot for noise
778 780 '''
779 781
780 782 CODE = 'noise'
781 783
782 784 def setup(self):
783 785 self.xaxis = 'time'
784 786 self.ncols = 1
785 787 self.nrows = 1
786 788 self.nplots = 1
787 789 self.ylabel = 'Intensity [dB]'
788 790 self.titles = ['Noise']
789 791 self.colorbar = False
790 792
791 793 def plot(self):
792 794
793 795 x = self.times
794 796 xmin = self.min_time
795 797 xmax = xmin + self.xrange * 60 * 60
796 798 Y = self.data[self.CODE]
797 799
798 800 if self.axes[0].firsttime:
799 801 for ch in self.data.channels:
800 802 y = Y[ch]
801 803 self.axes[0].plot(x, y, lw=1, label='Ch{}'.format(ch))
802 804 plt.legend()
803 805 else:
804 806 for ch in self.data.channels:
805 807 y = Y[ch]
806 808 self.axes[0].lines[ch].set_data(x, y)
807 809
808 810 self.ymin = numpy.nanmin(Y) - 5
809 811 self.ymax = numpy.nanmax(Y) + 5
810 812 self.saveTime = self.min_time
811 813
812 814
813 815 class PlotSNRData(PlotRTIData):
814 816 '''
815 817 Plot for SNR Data
816 818 '''
817 819
818 820 CODE = 'snr'
819 821 colormap = 'jet'
820 822
821 823
822 824 class PlotDOPData(PlotRTIData):
823 825 '''
824 826 Plot for DOPPLER Data
825 827 '''
826 828
827 829 CODE = 'dop'
828 830 colormap = 'jet'
829 831
830 832
831 833 class PlotSkyMapData(PlotData):
832 834 '''
833 835 Plot for meteors detection data
834 836 '''
835 837
836 838 CODE = 'param'
837 839
838 840 def setup(self):
839 841
840 842 self.ncols = 1
841 843 self.nrows = 1
842 844 self.width = 7.2
843 845 self.height = 7.2
844 846 self.nplots = 1
845 847 self.xlabel = 'Zonal Zenith Angle (deg)'
846 848 self.ylabel = 'Meridional Zenith Angle (deg)'
847 849 self.polar = True
848 850 self.ymin = -180
849 851 self.ymax = 180
850 852 self.colorbar = False
851 853
852 854 def plot(self):
853 855
854 856 arrayParameters = numpy.concatenate(self.data['param'])
855 857 error = arrayParameters[:, -1]
856 858 indValid = numpy.where(error == 0)[0]
857 859 finalMeteor = arrayParameters[indValid, :]
858 860 finalAzimuth = finalMeteor[:, 3]
859 861 finalZenith = finalMeteor[:, 4]
860 862
861 863 x = finalAzimuth * numpy.pi / 180
862 864 y = finalZenith
863 865
864 866 ax = self.axes[0]
865 867
866 868 if ax.firsttime:
867 869 ax.plot = ax.plot(x, y, 'bo', markersize=5)[0]
868 870 else:
869 871 ax.plot.set_data(x, y)
870 872
871 873 dt1 = self.getDateTime(self.min_time).strftime('%y/%m/%d %H:%M:%S')
872 874 dt2 = self.getDateTime(self.max_time).strftime('%y/%m/%d %H:%M:%S')
873 875 title = 'Meteor Detection Sky Map\n %s - %s \n Number of events: %5.0f\n' % (dt1,
874 876 dt2,
875 877 len(x))
876 878 self.titles[0] = title
877 879 self.saveTime = self.max_time
878 880
879 881
880 882 class PlotParamData(PlotRTIData):
881 883 '''
882 884 Plot for data_param object
883 885 '''
884 886
885 887 CODE = 'param'
886 888 colormap = 'seismic'
887 889
888 890 def setup(self):
889 891 self.xaxis = 'time'
890 892 self.ncols = 1
891 893 self.nrows = self.data.shape(self.CODE)[0]
892 894 self.nplots = self.nrows
893 895 if self.showSNR:
894 896 self.nrows += 1
895 897 self.nplots += 1
896 898
897 899 self.ylabel = 'Height [Km]'
898 self.titles = self.data.parameters \
899 if self.data.parameters else ['Param {}'.format(x) for x in xrange(self.nrows)]
900 if self.showSNR:
901 self.titles.append('SNR')
900 if not self.titles:
901 self.titles = self.data.parameters \
902 if self.data.parameters else ['Param {}'.format(x) for x in xrange(self.nrows)]
903 if self.showSNR:
904 self.titles.append('SNR')
902 905
903 906 def plot(self):
904 907 self.data.normalize_heights()
905 908 self.x = self.times
906 909 self.y = self.data.heights
907 910 if self.showSNR:
908 911 self.z = numpy.concatenate(
909 912 (self.data[self.CODE], self.data['snr'])
910 913 )
911 914 else:
912 915 self.z = self.data[self.CODE]
913 916
914 917 self.z = numpy.ma.masked_invalid(self.z)
915 918
916 919 for n, ax in enumerate(self.axes):
917 920
918 921 x, y, z = self.fill_gaps(*self.decimate())
919 922 self.zmax = self.zmax if self.zmax is not None else numpy.max(
920 923 self.z[n])
921 924 self.zmin = self.zmin if self.zmin is not None else numpy.min(
922 925 self.z[n])
923 926
924 927 if ax.firsttime:
925 928 if self.zlimits is not None:
926 929 self.zmin, self.zmax = self.zlimits[n]
927 930
928 931 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
929 932 vmin=self.zmin,
930 933 vmax=self.zmax,
931 934 cmap=self.cmaps[n]
932 935 )
933 936 else:
934 937 if self.zlimits is not None:
935 938 self.zmin, self.zmax = self.zlimits[n]
936 939 ax.collections.remove(ax.collections[0])
937 940 ax.plt = ax.pcolormesh(x, y, z[n].T * self.factors[n],
938 941 vmin=self.zmin,
939 942 vmax=self.zmax,
940 943 cmap=self.cmaps[n]
941 944 )
942 945
943 946 self.saveTime = self.min_time
944 947
945 948
946 949 class PlotOutputData(PlotParamData):
947 950 '''
948 951 Plot data_output object
949 952 '''
950 953
951 954 CODE = 'output'
952 955 colormap = 'seismic'
@@ -1,20 +1,21
1 1 '''
2 2
3 3 $Author: murco $
4 4 $Id: JRODataIO.py 169 2012-11-19 21:57:03Z murco $
5 5 '''
6 6
7 7 from jroIO_voltage import *
8 8 from jroIO_spectra import *
9 9 from jroIO_heispectra import *
10 10 from jroIO_usrp import *
11 11 from jroIO_digitalRF import *
12 12 from jroIO_kamisr import *
13 13 from jroIO_param import *
14 14 from jroIO_hf import *
15 15
16 16 from jroIO_madrigal import *
17 17
18 18 from bltrIO_param import *
19 19 from jroIO_bltr import *
20 20 from jroIO_mira35c import *
21 from julIO_param import * No newline at end of file
@@ -1,636 +1,635
1 1 '''
2 2 @author: Juan C. Espinoza
3 3 '''
4 4
5 5 import time
6 6 import json
7 7 import numpy
8 8 import paho.mqtt.client as mqtt
9 9 import zmq
10 10 import datetime
11 11 from zmq.utils.monitor import recv_monitor_message
12 12 from functools import wraps
13 13 from threading import Thread
14 14 from multiprocessing import Process
15 15
16 16 from schainpy.model.proc.jroproc_base import Operation, ProcessingUnit
17 17 from schainpy.model.data.jrodata import JROData
18 18 from schainpy.utils import log
19 19
20 20 MAXNUMX = 100
21 21 MAXNUMY = 100
22 22
23 23 class PrettyFloat(float):
24 24 def __repr__(self):
25 25 return '%.2f' % self
26 26
27 27 def roundFloats(obj):
28 28 if isinstance(obj, list):
29 29 return map(roundFloats, obj)
30 30 elif isinstance(obj, float):
31 31 return round(obj, 2)
32 32
33 33 def decimate(z, MAXNUMY):
34 34 dy = int(len(z[0])/MAXNUMY) + 1
35 35
36 36 return z[::, ::dy]
37 37
38 38 class throttle(object):
39 39 '''
40 40 Decorator that prevents a function from being called more than once every
41 41 time period.
42 42 To create a function that cannot be called more than once a minute, but
43 43 will sleep until it can be called:
44 44 @throttle(minutes=1)
45 45 def foo():
46 46 pass
47 47
48 48 for i in range(10):
49 49 foo()
50 50 print "This function has run %s times." % i
51 51 '''
52 52
53 53 def __init__(self, seconds=0, minutes=0, hours=0):
54 54 self.throttle_period = datetime.timedelta(
55 55 seconds=seconds, minutes=minutes, hours=hours
56 56 )
57 57
58 58 self.time_of_last_call = datetime.datetime.min
59 59
60 60 def __call__(self, fn):
61 61 @wraps(fn)
62 62 def wrapper(*args, **kwargs):
63 63 coerce = kwargs.pop('coerce', None)
64 64 if coerce:
65 65 self.time_of_last_call = datetime.datetime.now()
66 66 return fn(*args, **kwargs)
67 67 else:
68 68 now = datetime.datetime.now()
69 69 time_since_last_call = now - self.time_of_last_call
70 70 time_left = self.throttle_period - time_since_last_call
71 71
72 72 if time_left > datetime.timedelta(seconds=0):
73 73 return
74 74
75 75 self.time_of_last_call = datetime.datetime.now()
76 76 return fn(*args, **kwargs)
77 77
78 78 return wrapper
79 79
80 80 class Data(object):
81 81 '''
82 82 Object to hold data to be plotted
83 83 '''
84 84
85 85 def __init__(self, plottypes, throttle_value):
86 86 self.plottypes = plottypes
87 87 self.throttle = throttle_value
88 88 self.ended = False
89 89 self.localtime = False
90 90 self.__times = []
91 91 self.__heights = []
92 92
93 93 def __str__(self):
94 94 dum = ['{}{}'.format(key, self.shape(key)) for key in self.data]
95 95 return 'Data[{}][{}]'.format(';'.join(dum), len(self.__times))
96 96
97 97 def __len__(self):
98 98 return len(self.__times)
99 99
100 100 def __getitem__(self, key):
101 101 if key not in self.data:
102 102 raise KeyError(log.error('Missing key: {}'.format(key)))
103 103
104 104 if 'spc' in key:
105 105 ret = self.data[key]
106 106 else:
107 107 ret = numpy.array([self.data[key][x] for x in self.times])
108 108 if ret.ndim > 1:
109 109 ret = numpy.swapaxes(ret, 0, 1)
110 110 return ret
111 111
112 112 def __contains__(self, key):
113 113 return key in self.data
114 114
115 115 def setup(self):
116 116 '''
117 117 Configure object
118 118 '''
119 119
120 120 self.ended = False
121 121 self.data = {}
122 122 self.__times = []
123 123 self.__heights = []
124 124 self.__all_heights = set()
125 125 for plot in self.plottypes:
126 126 if 'snr' in plot:
127 127 plot = 'snr'
128 128 self.data[plot] = {}
129 129
130 130 def shape(self, key):
131 131 '''
132 132 Get the shape of the one-element data for the given key
133 133 '''
134 134
135 135 if len(self.data[key]):
136 136 if 'spc' in key:
137 137 return self.data[key].shape
138 138 return self.data[key][self.__times[0]].shape
139 139 return (0,)
140 140
141 def update(self, dataOut):
141 def update(self, dataOut, tm):
142 142 '''
143 143 Update data object with new dataOut
144 144 '''
145
146 tm = dataOut.utctime
145
147 146 if tm in self.__times:
148 147 return
149 148
150 149 self.parameters = getattr(dataOut, 'parameters', [])
151 150 self.pairs = dataOut.pairsList
152 151 self.channels = dataOut.channelList
153 152 self.interval = dataOut.getTimeInterval()
154 153 self.localtime = dataOut.useLocalTime
155 154 if 'spc' in self.plottypes or 'cspc' in self.plottypes:
156 155 self.xrange = (dataOut.getFreqRange(1)/1000., dataOut.getAcfRange(1), dataOut.getVelRange(1))
157 156 self.__heights.append(dataOut.heightList)
158 157 self.__all_heights.update(dataOut.heightList)
159 158 self.__times.append(tm)
160 159
161 160 for plot in self.plottypes:
162 161 if plot == 'spc':
163 162 z = dataOut.data_spc/dataOut.normFactor
164 163 self.data[plot] = 10*numpy.log10(z)
165 164 if plot == 'cspc':
166 165 self.data[plot] = dataOut.data_cspc
167 166 if plot == 'noise':
168 167 self.data[plot][tm] = 10*numpy.log10(dataOut.getNoise()/dataOut.normFactor)
169 168 if plot == 'rti':
170 169 self.data[plot][tm] = dataOut.getPower()
171 170 if plot == 'snr_db':
172 171 self.data['snr'][tm] = dataOut.data_SNR
173 172 if plot == 'snr':
174 173 self.data[plot][tm] = 10*numpy.log10(dataOut.data_SNR)
175 174 if plot == 'dop':
176 175 self.data[plot][tm] = 10*numpy.log10(dataOut.data_DOP)
177 176 if plot == 'mean':
178 177 self.data[plot][tm] = dataOut.data_MEAN
179 178 if plot == 'std':
180 179 self.data[plot][tm] = dataOut.data_STD
181 180 if plot == 'coh':
182 181 self.data[plot][tm] = dataOut.getCoherence()
183 182 if plot == 'phase':
184 183 self.data[plot][tm] = dataOut.getCoherence(phase=True)
185 184 if plot == 'output':
186 185 self.data[plot][tm] = dataOut.data_output
187 186 if plot == 'param':
188 187 self.data[plot][tm] = dataOut.data_param
189 188
190 189 def normalize_heights(self):
191 190 '''
192 191 Ensure same-dimension of the data for different heighList
193 192 '''
194 193
195 194 H = numpy.array(list(self.__all_heights))
196 195 H.sort()
197 196 for key in self.data:
198 197 shape = self.shape(key)[:-1] + H.shape
199 198 for tm, obj in self.data[key].items():
200 199 h = self.__heights[self.__times.index(tm)]
201 200 if H.size == h.size:
202 201 continue
203 202 index = numpy.where(numpy.in1d(H, h))[0]
204 203 dummy = numpy.zeros(shape) + numpy.nan
205 204 if len(shape) == 2:
206 205 dummy[:, index] = obj
207 206 else:
208 207 dummy[index] = obj
209 208 self.data[key][tm] = dummy
210 209
211 210 self.__heights = [H for tm in self.__times]
212 211
213 212 def jsonify(self, decimate=False):
214 213 '''
215 214 Convert data to json
216 215 '''
217 216
218 217 ret = {}
219 218 tm = self.times[-1]
220 219
221 220 for key, value in self.data:
222 221 if key in ('spc', 'cspc'):
223 222 ret[key] = roundFloats(self.data[key].to_list())
224 223 else:
225 224 ret[key] = roundFloats(self.data[key][tm].to_list())
226 225
227 226 ret['timestamp'] = tm
228 227 ret['interval'] = self.interval
229 228
230 229 @property
231 230 def times(self):
232 231 '''
233 232 Return the list of times of the current data
234 233 '''
235 234
236 235 ret = numpy.array(self.__times)
237 236 ret.sort()
238 237 return ret
239 238
240 239 @property
241 240 def heights(self):
242 241 '''
243 242 Return the list of heights of the current data
244 243 '''
245 244
246 245 return numpy.array(self.__heights[-1])
247 246
248 247 class PublishData(Operation):
249 248 '''
250 249 Operation to send data over zmq.
251 250 '''
252 251
253 252 __attrs__ = ['host', 'port', 'delay', 'zeromq', 'mqtt', 'verbose']
254 253
255 254 def __init__(self, **kwargs):
256 255 """Inicio."""
257 256 Operation.__init__(self, **kwargs)
258 257 self.isConfig = False
259 258 self.client = None
260 259 self.zeromq = None
261 260 self.mqtt = None
262 261
263 262 def on_disconnect(self, client, userdata, rc):
264 263 if rc != 0:
265 264 log.warning('Unexpected disconnection.')
266 265 self.connect()
267 266
268 267 def connect(self):
269 268 log.warning('trying to connect')
270 269 try:
271 270 self.client.connect(
272 271 host=self.host,
273 272 port=self.port,
274 273 keepalive=60*10,
275 274 bind_address='')
276 275 self.client.loop_start()
277 276 # self.client.publish(
278 277 # self.topic + 'SETUP',
279 278 # json.dumps(setup),
280 279 # retain=True
281 280 # )
282 281 except:
283 282 log.error('MQTT Conection error.')
284 283 self.client = False
285 284
286 285 def setup(self, port=1883, username=None, password=None, clientId="user", zeromq=1, verbose=True, **kwargs):
287 286 self.counter = 0
288 287 self.topic = kwargs.get('topic', 'schain')
289 288 self.delay = kwargs.get('delay', 0)
290 289 self.plottype = kwargs.get('plottype', 'spectra')
291 290 self.host = kwargs.get('host', "10.10.10.82")
292 291 self.port = kwargs.get('port', 3000)
293 292 self.clientId = clientId
294 293 self.cnt = 0
295 294 self.zeromq = zeromq
296 295 self.mqtt = kwargs.get('plottype', 0)
297 296 self.client = None
298 297 self.verbose = verbose
299 298 setup = []
300 299 if mqtt is 1:
301 300 self.client = mqtt.Client(
302 301 client_id=self.clientId + self.topic + 'SCHAIN',
303 302 clean_session=True)
304 303 self.client.on_disconnect = self.on_disconnect
305 304 self.connect()
306 305 for plot in self.plottype:
307 306 setup.append({
308 307 'plot': plot,
309 308 'topic': self.topic + plot,
310 309 'title': getattr(self, plot + '_' + 'title', False),
311 310 'xlabel': getattr(self, plot + '_' + 'xlabel', False),
312 311 'ylabel': getattr(self, plot + '_' + 'ylabel', False),
313 312 'xrange': getattr(self, plot + '_' + 'xrange', False),
314 313 'yrange': getattr(self, plot + '_' + 'yrange', False),
315 314 'zrange': getattr(self, plot + '_' + 'zrange', False),
316 315 })
317 316 if zeromq is 1:
318 317 context = zmq.Context()
319 318 self.zmq_socket = context.socket(zmq.PUSH)
320 319 server = kwargs.get('server', 'zmq.pipe')
321 320
322 321 if 'tcp://' in server:
323 322 address = server
324 323 else:
325 324 address = 'ipc:///tmp/%s' % server
326 325
327 326 self.zmq_socket.connect(address)
328 327 time.sleep(1)
329 328
330 329
331 330 def publish_data(self):
332 331 self.dataOut.finished = False
333 332 if self.mqtt is 1:
334 333 yData = self.dataOut.heightList[:2].tolist()
335 334 if self.plottype == 'spectra':
336 335 data = getattr(self.dataOut, 'data_spc')
337 336 z = data/self.dataOut.normFactor
338 337 zdB = 10*numpy.log10(z)
339 338 xlen, ylen = zdB[0].shape
340 339 dx = int(xlen/MAXNUMX) + 1
341 340 dy = int(ylen/MAXNUMY) + 1
342 341 Z = [0 for i in self.dataOut.channelList]
343 342 for i in self.dataOut.channelList:
344 343 Z[i] = zdB[i][::dx, ::dy].tolist()
345 344 payload = {
346 345 'timestamp': self.dataOut.utctime,
347 346 'data': roundFloats(Z),
348 347 'channels': ['Ch %s' % ch for ch in self.dataOut.channelList],
349 348 'interval': self.dataOut.getTimeInterval(),
350 349 'type': self.plottype,
351 350 'yData': yData
352 351 }
353 352
354 353 elif self.plottype in ('rti', 'power'):
355 354 data = getattr(self.dataOut, 'data_spc')
356 355 z = data/self.dataOut.normFactor
357 356 avg = numpy.average(z, axis=1)
358 357 avgdB = 10*numpy.log10(avg)
359 358 xlen, ylen = z[0].shape
360 359 dy = numpy.floor(ylen/self.__MAXNUMY) + 1
361 360 AVG = [0 for i in self.dataOut.channelList]
362 361 for i in self.dataOut.channelList:
363 362 AVG[i] = avgdB[i][::dy].tolist()
364 363 payload = {
365 364 'timestamp': self.dataOut.utctime,
366 365 'data': roundFloats(AVG),
367 366 'channels': ['Ch %s' % ch for ch in self.dataOut.channelList],
368 367 'interval': self.dataOut.getTimeInterval(),
369 368 'type': self.plottype,
370 369 'yData': yData
371 370 }
372 371 elif self.plottype == 'noise':
373 372 noise = self.dataOut.getNoise()/self.dataOut.normFactor
374 373 noisedB = 10*numpy.log10(noise)
375 374 payload = {
376 375 'timestamp': self.dataOut.utctime,
377 376 'data': roundFloats(noisedB.reshape(-1, 1).tolist()),
378 377 'channels': ['Ch %s' % ch for ch in self.dataOut.channelList],
379 378 'interval': self.dataOut.getTimeInterval(),
380 379 'type': self.plottype,
381 380 'yData': yData
382 381 }
383 382 elif self.plottype == 'snr':
384 383 data = getattr(self.dataOut, 'data_SNR')
385 384 avgdB = 10*numpy.log10(data)
386 385
387 386 ylen = data[0].size
388 387 dy = numpy.floor(ylen/self.__MAXNUMY) + 1
389 388 AVG = [0 for i in self.dataOut.channelList]
390 389 for i in self.dataOut.channelList:
391 390 AVG[i] = avgdB[i][::dy].tolist()
392 391 payload = {
393 392 'timestamp': self.dataOut.utctime,
394 393 'data': roundFloats(AVG),
395 394 'channels': ['Ch %s' % ch for ch in self.dataOut.channelList],
396 395 'type': self.plottype,
397 396 'yData': yData
398 397 }
399 398 else:
400 399 print "Tipo de grafico invalido"
401 400 payload = {
402 401 'data': 'None',
403 402 'timestamp': 'None',
404 403 'type': None
405 404 }
406 405
407 406 self.client.publish(self.topic + self.plottype, json.dumps(payload), qos=0)
408 407
409 408 if self.zeromq is 1:
410 409 if self.verbose:
411 410 log.log(
412 411 'Sending {} - {}'.format(self.dataOut.type, self.dataOut.datatime),
413 412 self.name
414 413 )
415 414 self.zmq_socket.send_pyobj(self.dataOut)
416 415
417 416 def run(self, dataOut, **kwargs):
418 417 self.dataOut = dataOut
419 418 if not self.isConfig:
420 419 self.setup(**kwargs)
421 420 self.isConfig = True
422 421
423 422 self.publish_data()
424 423 time.sleep(self.delay)
425 424
426 425 def close(self):
427 426 if self.zeromq is 1:
428 427 self.dataOut.finished = True
429 428 self.zmq_socket.send_pyobj(self.dataOut)
430 429 time.sleep(0.1)
431 430 self.zmq_socket.close()
432 431 if self.client:
433 432 self.client.loop_stop()
434 433 self.client.disconnect()
435 434
436 435
437 436 class ReceiverData(ProcessingUnit):
438 437
439 438 __attrs__ = ['server']
440 439
441 440 def __init__(self, **kwargs):
442 441
443 442 ProcessingUnit.__init__(self, **kwargs)
444 443
445 444 self.isConfig = False
446 445 server = kwargs.get('server', 'zmq.pipe')
447 446 if 'tcp://' in server:
448 447 address = server
449 448 else:
450 449 address = 'ipc:///tmp/%s' % server
451 450
452 451 self.address = address
453 452 self.dataOut = JROData()
454 453
455 454 def setup(self):
456 455
457 456 self.context = zmq.Context()
458 457 self.receiver = self.context.socket(zmq.PULL)
459 458 self.receiver.bind(self.address)
460 459 time.sleep(0.5)
461 460 log.success('ReceiverData from {}'.format(self.address))
462 461
463 462
464 463 def run(self):
465 464
466 465 if not self.isConfig:
467 466 self.setup()
468 467 self.isConfig = True
469 468
470 469 self.dataOut = self.receiver.recv_pyobj()
471 470 log.log('{} - {}'.format(self.dataOut.type,
472 471 self.dataOut.datatime.ctime(),),
473 472 'Receiving')
474 473
475 474
476 475 class PlotterReceiver(ProcessingUnit, Process):
477 476
478 477 throttle_value = 5
479 478 __attrs__ = ['server', 'plottypes', 'realtime', 'localtime', 'throttle']
480 479
481 480 def __init__(self, **kwargs):
482 481
483 482 ProcessingUnit.__init__(self, **kwargs)
484 483 Process.__init__(self)
485 484 self.mp = False
486 485 self.isConfig = False
487 486 self.isWebConfig = False
488 487 self.connections = 0
489 488 server = kwargs.get('server', 'zmq.pipe')
490 489 plot_server = kwargs.get('plot_server', 'zmq.web')
491 490 if 'tcp://' in server:
492 491 address = server
493 492 else:
494 493 address = 'ipc:///tmp/%s' % server
495 494
496 495 if 'tcp://' in plot_server:
497 496 plot_address = plot_server
498 497 else:
499 498 plot_address = 'ipc:///tmp/%s' % plot_server
500 499
501 500 self.address = address
502 501 self.plot_address = plot_address
503 502 self.plottypes = [s.strip() for s in kwargs.get('plottypes', 'rti').split(',')]
504 503 self.realtime = kwargs.get('realtime', False)
505 504 self.localtime = kwargs.get('localtime', True)
506 505 self.throttle_value = kwargs.get('throttle', 5)
507 506 self.sendData = self.initThrottle(self.throttle_value)
508 507 self.dates = []
509 508 self.setup()
510 509
511 510 def setup(self):
512 511
513 512 self.data = Data(self.plottypes, self.throttle_value)
514 513 self.isConfig = True
515 514
516 515 def event_monitor(self, monitor):
517 516
518 517 events = {}
519 518
520 519 for name in dir(zmq):
521 520 if name.startswith('EVENT_'):
522 521 value = getattr(zmq, name)
523 522 events[value] = name
524 523
525 524 while monitor.poll():
526 525 evt = recv_monitor_message(monitor)
527 526 if evt['event'] == 32:
528 527 self.connections += 1
529 528 if evt['event'] == 512:
530 529 pass
531 530
532 531 evt.update({'description': events[evt['event']]})
533 532
534 533 if evt['event'] == zmq.EVENT_MONITOR_STOPPED:
535 534 break
536 535 monitor.close()
537 536 print('event monitor thread done!')
538 537
539 538 def initThrottle(self, throttle_value):
540 539
541 540 @throttle(seconds=throttle_value)
542 541 def sendDataThrottled(fn_sender, data):
543 542 fn_sender(data)
544 543
545 544 return sendDataThrottled
546 545
547 546 def send(self, data):
548 547 log.success('Sending {}'.format(data), self.name)
549 548 self.sender.send_pyobj(data)
550 549
551 550 def run(self):
552 551
553 552 log.success(
554 553 'Starting from {}'.format(self.address),
555 554 self.name
556 555 )
557 556
558 557 self.context = zmq.Context()
559 558 self.receiver = self.context.socket(zmq.PULL)
560 559 self.receiver.bind(self.address)
561 560 monitor = self.receiver.get_monitor_socket()
562 561 self.sender = self.context.socket(zmq.PUB)
563 562 if self.realtime:
564 563 self.sender_web = self.context.socket(zmq.PUB)
565 564 self.sender_web.connect(self.plot_address)
566 565 time.sleep(1)
567 566
568 567 if 'server' in self.kwargs:
569 568 self.sender.bind("ipc:///tmp/{}.plots".format(self.kwargs['server']))
570 569 else:
571 570 self.sender.bind("ipc:///tmp/zmq.plots")
572 571
573 572 time.sleep(2)
574 573
575 574 t = Thread(target=self.event_monitor, args=(monitor,))
576 575 t.start()
577 576
578 577 while True:
579 578 dataOut = self.receiver.recv_pyobj()
580 579 if not dataOut.flagNoData:
581 if dataOut.type == 'Parameters':
580 if dataOut.type == 'Parameters':
582 581 tm = dataOut.utctimeInit
583 582 else:
584 583 tm = dataOut.utctime
585 584 if dataOut.useLocalTime:
586 585 if not self.localtime:
587 586 tm += time.timezone
588 587 dt = datetime.datetime.fromtimestamp(tm).date()
589 588 else:
590 589 if self.localtime:
591 590 tm -= time.timezone
592 591 dt = datetime.datetime.utcfromtimestamp(tm).date()
593 592 coerce = False
594 593 if dt not in self.dates:
595 594 if self.data:
596 595 self.data.ended = True
597 596 self.send(self.data)
598 597 coerce = True
599 598 self.data.setup()
600 599 self.dates.append(dt)
601 600
602 self.data.update(dataOut)
601 self.data.update(dataOut, tm)
603 602
604 603 if dataOut.finished is True:
605 604 self.connections -= 1
606 605 if self.connections == 0 and dt in self.dates:
607 606 self.data.ended = True
608 607 self.send(self.data)
609 608 self.data.setup()
610 609 else:
611 610 if self.realtime:
612 611 self.send(self.data)
613 612 # self.sender_web.send_string(self.data.jsonify())
614 613 else:
615 614 self.sendData(self.send, self.data, coerce=coerce)
616 615 coerce = False
617 616
618 617 return
619 618
620 619 def sendToWeb(self):
621 620
622 621 if not self.isWebConfig:
623 622 context = zmq.Context()
624 623 sender_web_config = context.socket(zmq.PUB)
625 624 if 'tcp://' in self.plot_address:
626 625 dum, address, port = self.plot_address.split(':')
627 626 conf_address = '{}:{}:{}'.format(dum, address, int(port)+1)
628 627 else:
629 628 conf_address = self.plot_address + '.config'
630 629 sender_web_config.bind(conf_address)
631 630 time.sleep(1)
632 631 for kwargs in self.operationKwargs.values():
633 632 if 'plot' in kwargs:
634 633 log.success('[Sending] Config data to web for {}'.format(kwargs['code'].upper()))
635 634 sender_web_config.send_string(json.dumps(kwargs))
636 635 self.isWebConfig = True
General Comments 0
You need to be logged in to leave comments. Login now