543 lines
16 KiB
Python
543 lines
16 KiB
Python
##
|
|
# This software was developed and / or modified by Raytheon Company,
|
|
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
|
|
#
|
|
# U.S. EXPORT CONTROLLED TECHNICAL DATA
|
|
# This software product contains export-restricted data whose
|
|
# export/transfer/disclosure is restricted by U.S. law. Dissemination
|
|
# to non-U.S. persons whether in the United States or abroad requires
|
|
# an export license or other authorization.
|
|
#
|
|
# Contractor Name: Raytheon Company
|
|
# Contractor Address: 6825 Pine Street, Suite 340
|
|
# Mail Stop B8
|
|
# Omaha, NE 68106
|
|
# 402.291.0100
|
|
#
|
|
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
|
|
# further licensing information.
|
|
##
|
|
#
|
|
# Name:
|
|
# Avn.py
|
|
# GFS1-NHD:A4695.0000-SCRIPT;46
|
|
#
|
|
# Status:
|
|
# DELIVERED
|
|
#
|
|
# History:
|
|
# Revision 46 (DELIVERED)
|
|
# Created: 17-APR-2009 12:03:01 OBERFIEL
|
|
# Added new exception when unknown precipitation (UP)
|
|
# reported in observation.
|
|
#
|
|
# Revision 45 (REVIEW)
|
|
# Created: 20-MAR-2009 18:22:14 OBERFIEL
|
|
# Removed code cruft. ETA changed to NAM. NGMMOS removed.
|
|
#
|
|
# Revision 44 (DELIVERED)
|
|
# Created: 28-JUL-2008 13:56:48 OBERFIEL
|
|
# ICAO new TAF format switch now moved up 18 hours.
|
|
# DTG format will begin 00Z 05 November 2008
|
|
#
|
|
# Revision 43 (DELIVERED)
|
|
# Created: 22-APR-2008 12:08:20 OBERFIEL
|
|
# Aviation Services Branch has provided a specific date and
|
|
# time for the TAF format change.
|
|
#
|
|
# Revision 42 (DELIVERED)
|
|
# Created: 07-MAR-2008 10:48:55 OBERFIEL
|
|
# Changed assignment of global variables to have fallback
|
|
# values in case there aren't environment variables set.
|
|
#
|
|
# Revision 41 (DELIVERED)
|
|
# Created: 14-MAY-2007 10:04:48 OBERFIEL
|
|
# Removed references to the obsolete prototype XTF product.
|
|
# Allow decoder and encoder to format TAF in two different
|
|
# ways. New format will be triggered by day and time to be
|
|
# specified at a later date.
|
|
#
|
|
# Revision 40 (REVIEW)
|
|
# Created: 04-MAY-2007 15:38:13 OBERFIEL
|
|
# Added new function DTGImplementationSwitch()
|
|
#
|
|
# Revision 39 (DELIVERED)
|
|
# Created: 24-AUG-2006 15:42:36 OBERFIEL
|
|
# Reworded contact information
|
|
#
|
|
# Revision 38 (DELIVERED)
|
|
# Created: 16-AUG-2006 14:38:07 BLI
|
|
# Removed GT contact info from the Help->about panel
|
|
#
|
|
# Revision 37 (DELIVERED)
|
|
# Created: 06-JUN-2006 13:45:32 OBERFIEL
|
|
# Updated code to allow for wildcard expansion
|
|
#
|
|
# Revision 36 (DELIVERED)
|
|
# Created: 20-MAY-2006 09:33:54 OBERFIEL
|
|
# Added place holders in documentation
|
|
#
|
|
# Revision 35 (DELIVERED)
|
|
# Created: 04-APR-2006 07:57:36 OBERFIEL
|
|
# Corrected title bar
|
|
#
|
|
# Revision 34 (DELIVERED)
|
|
# Created: 23-MAR-2006 15:14:13 TROJAN
|
|
# spr 7109 - modified method to determine work PIL
|
|
#
|
|
# Revision 33 (DELIVERED)
|
|
# Created: 22-MAR-2006 13:05:15 TROJAN
|
|
# spr 7110. Revised TAF handling for consistency
|
|
#
|
|
# Revision 32 (DELIVERED)
|
|
# Created: 16-FEB-2006 14:15:25 TROJAN
|
|
# modified Version date
|
|
#
|
|
# Revision 31 (APPROVED)
|
|
# Created: 29-JAN-2006 14:23:42 TROJAN
|
|
# Added method to Bunch class
|
|
#
|
|
# Revision 30 (APPROVED)
|
|
# Created: 23-JAN-2006 08:23:11 TROJAN
|
|
# stdr 956
|
|
#
|
|
# Revision 29 (DELIVERED)
|
|
# Created: 07-SEP-2005 13:17:13 TROJAN
|
|
# spr 7010
|
|
#
|
|
# Revision 28 (DELIVERED)
|
|
# Created: 16-AUG-2005 13:03:16 TROJAN
|
|
# spr 6989
|
|
#
|
|
# Revision 27 (DELIVERED)
|
|
# Created: 08-AUG-2005 13:13:55 TROJAN
|
|
# spr 6971
|
|
#
|
|
# Revision 26 (DELIVERED)
|
|
# Created: 06-JUL-2005 18:16:34 TROJAN
|
|
# spr 6548
|
|
#
|
|
# Revision 25 (DELIVERED)
|
|
# Created: 07-MAY-2005 11:29:23 OBERFIEL
|
|
# Added Item Header Block
|
|
#
|
|
# Revision 24 (DELIVERED)
|
|
# Created: 04-APR-2005 15:51:03 TROJAN
|
|
# spr 6775
|
|
#
|
|
# Revision 23 (DELIVERED)
|
|
# Created: 14-FEB-2005 20:54:47 TROJAN
|
|
# spr 6649
|
|
#
|
|
# Revision 22 (APPROVED)
|
|
# Created: 24-JAN-2005 21:44:03 TROJAN
|
|
# stdr 855
|
|
#
|
|
# Revision 21 (APPROVED)
|
|
# Created: 16-NOV-2004 19:56:58 PCMS
|
|
# Restoring history
|
|
#
|
|
# Revision 20 (DELIVERED)
|
|
# Created: 08-JAN-2004 21:39:46 PCMS
|
|
# Updating for code cleanup
|
|
#
|
|
# Revision 19 (APPROVED)
|
|
# Created: 05-NOV-2003 19:04:33 OBERFIEL
|
|
# Initial version for 2.0
|
|
#
|
|
# Revision 18 (DELIVERED)
|
|
# Created: 24-APR-2003 14:54:42 TROJAN
|
|
# sprs 5055, 5056, 5057, 5070
|
|
#
|
|
# Revision 17 (DELIVERED)
|
|
# Updated: 10-APR-2003 15:26:53 TROJAN
|
|
# spr 4997
|
|
# Created: 16-MAR-2003 17:48:09 TROJAN
|
|
# spr 4931
|
|
#
|
|
# Revision 16 (BUILD_RELEASE)
|
|
# Created: 10-MAR-2003 13:38:59 TROJAN
|
|
# sprs 4904 - 4908
|
|
#
|
|
# Revision 15 (BUILD_RELEASE)
|
|
# Created: 28-FEB-2003 12:31:30 TROJAN
|
|
# spr 4821 4822
|
|
#
|
|
# Revision 14 (DELIVERED)
|
|
# Created: 14-NOV-2002 14:14:47 PCMS
|
|
# Fixing improper display of LDAD metars
|
|
#
|
|
# Revision 13 (DELIVERED)
|
|
# Created: 05-NOV-2002 17:58:17 PCMS
|
|
# Added definition for 'unique' attribute
|
|
#
|
|
# Revision 12 (DELIVERED)
|
|
# Created: 21-OCT-2002 21:52:52 PCMS
|
|
# Updating of rnew NWSI 10-813 migration
|
|
#
|
|
# Revision 11 (DELIVERED)
|
|
# Created: 10-SEP-2002 20:14:07 PCMS
|
|
# Fixing problem with one line TAF reported as NIL.
|
|
#
|
|
# Revision 10 (DELIVERED)
|
|
# Created: 06-AUG-2002 18:04:12 PCMS
|
|
# Fixed problem when loading from template/merge.
|
|
#
|
|
# Revision 9 (DELIVERED)
|
|
# Created: 17-JUL-2002 13:23:37 PCMS
|
|
# Fixed problem with monitoring old TAF.
|
|
#
|
|
# Revision 8 (DELIVERED)
|
|
# Created: 16-JUL-2002 20:41:20 PCMS
|
|
# Fixed monitoring of old TAF. (Failed 2nd time at NGIT)
|
|
#
|
|
# Revision 7 (DELIVERED)
|
|
# Created: 09-JUL-2002 21:04:29 PCMS
|
|
# Fixing problem monitoring old TAF
|
|
#
|
|
# Revision 6 (DELIVERED)
|
|
# Created: 25-JUN-2002 19:47:12 PCMS
|
|
# Fixed monitoring of old TAF.
|
|
#
|
|
# Revision 5 (DELIVERED)
|
|
# Created: 18-JUN-2002 19:28:32 PCMS
|
|
# Fixed Forecat Editor Display problem for TWBs.
|
|
#
|
|
# Revision 4 (BUILD_RELEASE)
|
|
# Created: 14-JUN-2002 15:11:04 PCMS
|
|
# Fixed time problems which affected which TAF is monitored.
|
|
#
|
|
# Revision 3 (DELIVERED)
|
|
# Created: 11-JUN-2002 18:25:52 PCMS
|
|
# Fixed Forecast Editor Text Display problems
|
|
#
|
|
# Revision 2 (DELIVERED)
|
|
# Created: 29-MAY-2002 22:27:50 PCMS
|
|
# Added capability to mark default product group to monitor
|
|
#
|
|
# Revision 1 (DELIVERED)
|
|
# Created: 13-MAY-2002 21:39:40 PCMS
|
|
# Initial version
|
|
#
|
|
# Change Document History:
|
|
# 1:
|
|
# Change Document: GFS1-NHD_SPR_7418
|
|
# Action Date: 06-OCT-2009 09:42:01
|
|
# Relationship Type: In Response to
|
|
# Status: CLOSED
|
|
# Title: AvnFPS: AvnFPS regression based lightning forecast to use LAMP
|
|
#
|
|
#
|
|
|
|
##
|
|
# This is a base file that is not intended to be overridden.
|
|
##
|
|
|
|
import itertools
|
|
import math
|
|
import os
|
|
import time
|
|
import logging
|
|
|
|
# Placement of special characters so that updates of AvnFPS version and
|
|
# release dates are automated
|
|
Name = 'AvnFPS %V'
|
|
Version = '%D'
|
|
Contact = """
|
|
For more information about this application consult the AvnFPS User's Guide
|
|
under AWIPS "One Stop Resource" Page under "Software" or from the World Wide Web at
|
|
http://www.nws.noaa.gov/mdl/pgb/AvnFPS/%V/AvnFPS%V.html"""
|
|
|
|
# relative to top level directory
|
|
WatchBitmap = '@etc/avnwatch.xbm'
|
|
SetupBitmap = '@etc/avnsetup.xbm'
|
|
|
|
# the next two values are set by the decoders
|
|
UNLIMITED = 99999 # SKC
|
|
CLEAR = 99998 # CLR, must be < UNLIMITED
|
|
VARIABLE = 999
|
|
|
|
# flight categories
|
|
LIFR = 'lifr'
|
|
IFR = 'ifr'
|
|
MVFR = 'mvfr'
|
|
VFR = 'vfr'
|
|
|
|
_TextDB = os.environ.get('FXA_HOME','/awips/fxa') + '/bin/textdb'
|
|
TAFWorkPIL = os.environ.get('DEFAULT_CCC','XXX') +'WRKTAF'
|
|
|
|
PLUGIN_NAME = 'com.raytheon.uf.common.aviation'
|
|
CATEGORY = 'AVNFPS'
|
|
|
|
_Logger = logging.getLogger(CATEGORY)
|
|
try:
|
|
import UFStatusHandler
|
|
_Logger.addHandler(UFStatusHandler.UFStatusHandler(PLUGIN_NAME, CATEGORY, level=logging.INFO))
|
|
except:
|
|
logging.basicConfig(filename='/tmp/avnfps.log',level=logging.INFO)
|
|
_Logger = logging.getLogger()
|
|
|
|
|
|
PATH_MGR = None
|
|
try:
|
|
from com.raytheon.uf.common.localization import PathManagerFactory
|
|
PATH_MGR = PathManagerFactory.getPathManager()
|
|
ConfigDir = os.path.join('aviation', 'config')
|
|
except:
|
|
#_Logger.exception("Error determining AvnFPS config directory")
|
|
pass
|
|
|
|
##############################################################################
|
|
# Determination of TAF format based on beginning valid time.
|
|
##############################################################################
|
|
def DTGImplementationSwitch(t=None):
|
|
if not t:
|
|
t = time.time()
|
|
dtg = mkgmtime(time.strptime('05 Nov 2008 00:00:00 GMT',
|
|
'%d %b %Y %H:%M:%S %Z'))
|
|
return t >= dtg
|
|
##############################################################################
|
|
# exceptions
|
|
##############################################################################
|
|
class AvnError(Exception):
|
|
pass
|
|
|
|
|
|
class AvnMissing(AvnError):
|
|
pass
|
|
|
|
|
|
class AvnUnknwnPcp(AvnError):
|
|
pass
|
|
|
|
|
|
##############################################################################
|
|
# stolen code
|
|
##############################################################################
|
|
# Python documentation
|
|
def any(seq, pred=bool):
|
|
"Returns True if pred(x) is True at least one element in the iterable"
|
|
return True in map(pred, seq)
|
|
|
|
|
|
def all(seq, pred=bool):
|
|
"Returns True if pred(x) is True for every element in the iterable"
|
|
return False not in map(pred, seq)
|
|
|
|
|
|
def flatten(listOfLists):
|
|
return list(itertools.chain(*listOfLists))
|
|
|
|
|
|
def pairs(seq):
|
|
is1 = itertools.islice(iter(seq), 0, None, 2)
|
|
is2 = itertools.islice(iter(seq), 1, None, 2)
|
|
return list(zip(is1, is2))
|
|
|
|
|
|
def window(seq, n=2):
|
|
"Returns a sliding window (of width n) over data from the iterable"
|
|
" s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... "
|
|
it = iter(seq)
|
|
result = tuple(itertools.islice(it, n))
|
|
if len(result) == n:
|
|
yield result
|
|
for elem in it:
|
|
result = result[1:] + (elem,)
|
|
yield result
|
|
|
|
|
|
def category(v, iterable):
|
|
"Returns smallest index n such that v is less than iterable[n]."
|
|
return sum(map(lambda x: x<=v, iterable))
|
|
|
|
|
|
def accumulate(iterable):
|
|
"'Integrates' iterable."
|
|
s = 0
|
|
for v in iterable:
|
|
s += v
|
|
yield s
|
|
|
|
|
|
def nth(iterable, n, m=1):
|
|
"Returns list of length m starting at position n"
|
|
return list(itertools.islice(iterable, n, n+m))
|
|
|
|
|
|
def wind_category(dd, ff, dd_threshold, ff_threshold):
|
|
if dd == 'VRB' or dd == 0:
|
|
return 0, 0
|
|
else:
|
|
numDDcat = len(dd_threshold)
|
|
dd_cat = (category(dd, dd_threshold)%numDDcat) + 1
|
|
ff_cat = category(ff, ff_threshold)
|
|
if ff_cat == 0:
|
|
dd_cat = 0
|
|
return dd_cat, ff_cat
|
|
|
|
|
|
def dd_avg(count, num_wind_dir=8):
|
|
delta = 360.0/num_wind_dir
|
|
x, y = 0.0, 0.0
|
|
for n in range(num_wind_dir):
|
|
r3 = math.pow(count[n+1], 3)
|
|
alpha = math.radians(delta*n)
|
|
x += r3*math.cos(alpha)
|
|
y += r3*math.sin(alpha)
|
|
return math.degrees(math.atan2(y, x))
|
|
|
|
|
|
# Python Cookbook
|
|
# Chapter 1.7
|
|
class Bunch(object):
|
|
def __init__(self, **kwds):
|
|
self.__dict__ = kwds
|
|
|
|
def values(self):
|
|
return list(self.__dict__.values())
|
|
|
|
|
|
# Chapter 1.15
|
|
def frange(start, end=None, inc=1.0):
|
|
if end == None:
|
|
end = start + 0.0
|
|
start = 0.0
|
|
assert inc
|
|
i = 0
|
|
while True:
|
|
next = start + i*inc
|
|
if inc > 0 and next >= end:
|
|
break
|
|
elif inc < 0 and next <= end:
|
|
break
|
|
yield next
|
|
i += 1
|
|
|
|
|
|
# Chapter 15.7
|
|
def curry(*args, **kwds):
|
|
def callit(*moreargs, **morekwds):
|
|
kw = kwds.copy()
|
|
kw.update(morekwds)
|
|
return args[0](*(args[1:]+moreargs), **kw)
|
|
return callit
|
|
|
|
|
|
# Chapter 14.4
|
|
def printExcPlus():
|
|
'''Print the usual traceback information, followed by a listing of all
|
|
the local variables in each frame'''
|
|
import sys, traceback
|
|
tb = sys.exc_info()[2]
|
|
while 1:
|
|
if not tb.tb_next:
|
|
break
|
|
tb = tb.tb_next
|
|
stack = []
|
|
f = tb.tb_frame
|
|
while f:
|
|
stack.append(f)
|
|
f = f.f_back
|
|
stack.reverse()
|
|
traceback.print_exc()
|
|
print('Locals by frame, innermost last')
|
|
for frame in stack:
|
|
print('\nFrame %s in %s at line %s' % (frame.f_code.co_name,
|
|
frame.f_code.co_filename, frame.f_lineno))
|
|
for key, value in frame.f_locals.items():
|
|
print('\t%20s = ' % key, end=' ')
|
|
try:
|
|
print(value)
|
|
except:
|
|
print('<ERROR WHILE PRINTING VALUE>')
|
|
|
|
###############################################################################
|
|
# utility functions
|
|
##############################################################################
|
|
def playCommand(audiofile):
|
|
if not audiofile:
|
|
return None
|
|
return '/usr/bin/play %s &' % audiofile
|
|
|
|
|
|
def string2time(s):
|
|
# Converts string %y%m%d%H%M to Unix time
|
|
return mkgmtime((int(s[:2])+2000, int(s[2:4]), int(s[4:6]),
|
|
int(s[6:8]), int(s[8:10]), 0, 0, 0, 0))
|
|
|
|
|
|
def time2string(t=None):
|
|
# Converts string Unix time to string %y%m%d%H%M
|
|
if t:
|
|
return time.strftime('%y%m%d%H%M', time.gmtime(t))
|
|
else:
|
|
return time.strftime('%y%m%d%H%M')
|
|
|
|
|
|
def tagToBBB(tag):
|
|
# used by several GUIs
|
|
return {'Amd': 'AAX', 'Rtd': 'RRX', 'Cor': 'CCX'}.get(tag, '')
|
|
|
|
|
|
def storeInTextDB(filename):
|
|
# stores content of a filename in text database
|
|
# returns output of 'textdb -w' command
|
|
cmd = '%s -w %s' % (_TextDB, os.path.basename(filename).split('.')[0])
|
|
chldin, chldout = os.popen4(cmd, -1)
|
|
with open(filename) as f:
|
|
chldin.write(f.read())
|
|
chldin.close()
|
|
return chldout.read()
|
|
|
|
|
|
def _getIds(taf):
|
|
n = taf.find('\n')+1
|
|
return taf[n:n+4]
|
|
|
|
|
|
def getTafPath(ident, name):
|
|
"""This searches for a file name first in the taf/ident local and base
|
|
directory then in the default tafs/XXXX directory.
|
|
Raises an AvnError when unable to find the file.
|
|
ident - site specific directory name
|
|
name - name of file to search for
|
|
returns fully qualified file name.
|
|
"""
|
|
relPath = os.path.join(ConfigDir, 'tafs', ident, name)
|
|
fname = ''
|
|
f = PATH_MGR.getStaticFile(relPath)
|
|
if f:
|
|
fname = f.getPath()
|
|
if len(fname) == 0 or not os.path.isfile(fname):
|
|
if len(fname) == 0 :
|
|
idErrName = relPath
|
|
else:
|
|
idErrName = fname
|
|
relPath = os.path.join(ConfigDir, 'tafs', 'XXXX', name)
|
|
f = PATH_MGR.getStaticFile(relPath)
|
|
if f:
|
|
fname = f.getPath()
|
|
if len(fname) == 0 or not os.path.isfile(fname):
|
|
if len(fname) == 0:
|
|
defErrName = relPath
|
|
else:
|
|
defErrName = fname
|
|
fname = None
|
|
raise AvnError('Unable to find file "%s" or file "%s"' % (idErrName, defErrName))
|
|
return fname
|
|
|
|
|
|
def mkgmtime(t):
|
|
"""This is the inverse function of time.gmtime(). It should be the same
|
|
as the calendar.timegm(); but no need to import calendar. This takes a
|
|
struct_time or 9-tuple which expresses the time in UTC not local time.
|
|
It returns a floating point number, for compatibility with time().
|
|
If the input value cannot be represented as a valid time, either OverflowError
|
|
or ValueError will be raised (which depends on whether the invalid value is
|
|
caught by Python or the underlying C libraries). The earliest date for which
|
|
it can generate a time is platform-dependent.
|
|
"""
|
|
if isinstance(t, list):
|
|
t = tuple(t)
|
|
return time.mktime(t) - time.timezone
|