ROPO
Loading...
Searching...
No Matches
ropo_realtime.py
Go to the documentation of this file.
1#!/usr/bin/env python
2'''
3Copyright (C) 2011- Swedish Meteorological and Hydrological Institute (SMHI)
4
5This file is part of the bRopo extension to RAVE.
6
7RAVE is free software: you can redistribute it and/or modify
8it under the terms of the GNU Lesser General Public License as published by
9the Free Software Foundation, either version 3 of the License, or
10(at your option) any later version.
11
12RAVE is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15See the GNU Lesser General Public License for more details.
16
17You should have received a copy of the GNU Lesser General Public License
18along with RAVE. If not, see <http://www.gnu.org/licenses/>.
19'''
20
22
23
26
27from Proj import rd
28from rave_defines import UTF8
29import _fmiimage
30import _polarscan
31import _polarvolume
32import _raveio
33import _ropogenerator
34import odim_source
35import os
36import time
37import copy
38import xml.etree.ElementTree as ET
39from rave_quality_plugin import QUALITY_CONTROL_MODE_ANALYZE_AND_APPLY
40from rave_quality_plugin import QUALITY_CONTROL_MODE_ANALYZE
41
42
43CONFIG_FILE = os.path.join(os.path.join(os.path.split(os.path.split(_ropogenerator.__file__)[0])[0],
44 'config'), 'ropo_options.xml')
45
46# Guideline command-line arguments when creating this functionality
47# --parameters=DBZH --threshold=<see below> --restore-fill=True --restore-thresh=108
48# --softcut=5,170,180 --speckNormOld=-20,24,8 --emitter2=-10,3,2 --ship=20,8 --speck=-30,12
49
50
53THRESHOLDS = {"COLD" : (-6, -4, -2, 0, 2, 4, 6, 4, 2, 0, -2, -4),
54 "VERY_COLD" : (-12, -10, -6, -4, 0, 4, 6, 4, -4, -8, -10, -12),
55 "TEMPERATE" : (0, 2, 4, 6, 8, 10, 10, 8, 6, 4, 2, 0),
56 "FLAT-10" : (-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10),
57 "FLAT-24" : (-24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24, -24),
58 "FLAT-30" : (-30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, -30),
59 "NONE" : None
60 }
61THRESHOLDS["DEFAULT"] = THRESHOLDS["FLAT-24"]
62
63initialized = 0
64
65ARGS = {} # Empty dictionary to be filled with site-specific options/arguments
66
67
68def init():
69 global initialized
70 if initialized: return
71
72 C = ET.parse(CONFIG_FILE)
73 OPTIONS = C.getroot()
74
75 for site in list(OPTIONS):
76 opts = options()
77
78 for k in site.attrib.keys():
79 if k == "parameters": opts.params = site.attrib[k]
80 elif k == "threshold": opts.threshold = site.attrib[k]
81 elif k == "highest-elev": opts.elev = float(site.attrib[k])
82 elif k == "restore": opts.restore = eval(site.attrib[k])
83 elif k == "restore-fill": opts.restore2 = eval(site.attrib[k])
84 elif k == "restore-thresh": opts.restore_thresh = eval(site.attrib[k])
85 elif k == "softcut": opts.softcut = eval(site.attrib[k])
86 elif k == "speckNormOld": opts.speckNormOld = eval(site.attrib[k])
87 elif k == "emitter2": opts.emitter2 = eval(site.attrib[k])
88 elif k == "ship": opts.ship = eval(site.attrib[k])
89 elif k == "speck": opts.speck = eval(site.attrib[k])
90
91 ARGS[site.tag] = opts
92 initialized = 1
93
94
95
99class options:
100 def __init__(self):
101 self.params = None
102 self.threshold = None
103 self.elev = None
104 self.restore = None
105 self.restore2 = None
106 self.restore_thresh = None
107 self.softcut = None
108 self.speckNormOld = None
109 self.emitter2 = None
110 self.ship = None
111 self.speck = None
112
113
114
117def get_options(inobj):
118 odim_source.CheckSource(inobj)
119 S = odim_source.ODIM_Source(inobj.source)
120 try:
121 return copy.deepcopy(ARGS[S.nod])
122 except KeyError:
123 return copy.deepcopy(ARGS["default"])
124
125
126
130def copy_topwhat(ino, outo):
131 outo.beamwidth = ino.beamwidth
132 outo.date = ino.date
133 outo.time = ino.time
134 outo.height = ino.height
135 outo.latitude = ino.latitude
136 outo.longitude = ino.longitude
137 outo.source = ino.source
138
139
140
145def process_scan(scan, options, quality_control_mode=QUALITY_CONTROL_MODE_ANALYZE_AND_APPLY):
146 newscan, gates = PadNrays(scan, options)
147
148 image = _fmiimage.fromRave(newscan, options.params)
149 param = newscan.getParameter(options.params)
150 rg = _ropogenerator.new(image)
151 if options.threshold:
152 raw_thresh = int((options.threshold - image.offset) / image.gain)
153 rg.threshold(raw_thresh)
154 if options.speck:
155 a, b = options.speck
156 rg.speck(a, b)
157 if scan.elangle * rd < options.elev:
158 if options.speckNormOld:
159 a, b, c = options.speckNormOld
160 rg.speckNormOld(a, b, c)
161 if options.softcut:
162 a, b, c = options.softcut
163 rg.softcut(a, b, c)
164 if options.ship:
165 a, b = options.ship
166 rg.ship(a, b)
167 if options.emitter2:
168 a, b, c = options.emitter2
169 rg.emitter2(a, b, c)
170
171 classification = rg.classify().classification.toRaveField()
172 if options.restore:
173 restored = rg.restore(int(options.restore_thresh)).toPolarScan()
174 elif options.restore2:
175 restored = rg.restore2(int(options.restore_thresh)).toPolarScan()
176
177 restored, classification = UnpadNrays(restored, classification, gates)
178 dbzh = scan.getParameter("DBZH")
179 if quality_control_mode != QUALITY_CONTROL_MODE_ANALYZE:
180 dbzh.setData(restored.getParameter("DBZH").getData())
181 scan.addParameter(dbzh)
182 scan.addOrReplaceQualityField(classification)
183
184 return scan
185
186
187
191def process_pvol(pvol, options, quality_control_mode=QUALITY_CONTROL_MODE_ANALYZE_AND_APPLY):
192 import _polarvolume
193
194 out = _polarvolume.new()
195 copy_topwhat(pvol, out)
196
197 # Get month and use it to determine dBZ threshold, recalling that
198 # options.threshold contains the name of the look-up. This is
199 # over-written with the looked-up threshold itself.
200 month = int(pvol.date[4:6]) - 1
201 options.threshold = THRESHOLDS[options.threshold][month]
202
203 for a in pvol.getAttributeNames(): # Copy also 'how' attributes at top level of volume, if any
204 out.addAttribute(a, pvol.getAttribute(a))
205
206 for s in range(pvol.getNumberOfScans()):
207 scan = pvol.getScan(s)
208 scan = process_scan(scan, options, quality_control_mode)
209 out.addScan(scan)
210
211 return out
212
213
214
218def generate(inobj, reprocess_quality_flag=True, quality_control_mode=QUALITY_CONTROL_MODE_ANALYZE_AND_APPLY):
219 if _polarscan.isPolarScan(inobj) == False and _polarvolume.isPolarVolume(inobj) == False:
220 raise IOError("Input file must be either polar scan or volume.")
221
222 if reprocess_quality_flag == False:
223 if _polarscan.isPolarScan(inobj) and inobj.findQualityFieldByHowTask("fi.fmi.ropo.detector.classification"):
224 return inobj
225 elif _polarvolume.isPolarVolume(inobj):
226 allprocessed = True
227 for i in range(inobj.getNumberOfScans()):
228 scan = inobj.getScan(i)
229 if not scan.findQualityFieldByHowTask("fi.fmi.ropo.detector.classification"):
230 allprocessed = False
231 break
232 if allprocessed:
233 return inobj
234
235 options = get_options(inobj) # Gets options/arguments for this radar. Fixes /what/source if required.
236 if _polarvolume.isPolarVolume(inobj):
237 ret = process_pvol(inobj, options, quality_control_mode)
238 elif _polarscan.isPolarScan(inobj):
239 month = int(inobj.date[4:6]) - 1
240 options.threshold = THRESHOLDS[options.threshold][month]
241 ret = process_scan(inobj, options, quality_control_mode)
242 copy_topwhat(inobj, ret)
243
244 return ret
245
246
247
254def PadNrays(scan, options):
255 from numpy import vstack
256
257 width = float(options.emitter2[2])
258 gatew = 360.0 / scan.nrays
259 gates = (width / gatew) # / 2 # May as well pad with good margin
260 if (gates - 1) > 0.0:
261 gates = int(gates + 1)
262 else:
263 gates = int(gates)
264
265 newscan = _polarscan.new()
266
267 dbzh = scan.getParameter("DBZH").clone()
268 data = dbzh.getData()
269 toprays = data[0:gates, ]
270 botrays = data[scan.nrays - gates:, ]
271 data = vstack((botrays, data, toprays))
272 dbzh.setData(data)
273 dbzh.quantity = "DBZH"
274 newscan.addParameter(dbzh)
275 newscan.elangle = scan.elangle
276 return newscan, gates
277
278
279
284def UnpadNrays(scan, classification, gates):
285 dbzh = scan.getParameter("DBZH")
286 data = dbzh.getData()
287 data = data[gates:scan.nrays - gates, ]
288 dbzh.setData(data)
289
290 qdata = classification.getData()
291 qdata = qdata[gates:scan.nrays - gates, ]
292 classification.setData(qdata)
293
294 scan.removeParameter("DBZH")
295 scan.addParameter(dbzh)
296 return scan, classification
297
298
299
300init()
301
302
303if __name__ == "__main__":
304 pass
Class used to organize options and argument values to ropo.
process_scan(scan, options, quality_control_mode=QUALITY_CONTROL_MODE_ANALYZE_AND_APPLY)
TODO: activate parameters list.
UnpadNrays(scan, classification, gates)
Internal function to unwrap a scan from overlapping rays.
get_options(inobj)
Based on the /what/source attribute, find site-specific options/arguments.
init()
Initializes the ARGS dictionary by reading content from XML file.
process_pvol(pvol, options, quality_control_mode=QUALITY_CONTROL_MODE_ANALYZE_AND_APPLY)
Loops through a volume and processes scans using process_scan.
generate(inobj, reprocess_quality_flag=True, quality_control_mode=QUALITY_CONTROL_MODE_ANALYZE_AND_APPLY)
Generate - does the real work.
PadNrays(scan, options)
Internal function to wrap rays near 360-0 degrees.
copy_topwhat(ino, outo)
Copies the top-level 'what' attributes from one object to another.