awips2/cave/com.raytheon.viz.avnconfig/localization/aviation/python/AvnLib.py
Zhidong.Hao 81b94c87c4 ASM #17540 AvnFPS: Unable to set TAF default issuance time
Change-Id: I70cf4675c19575320b1763aa0e5c35f6336bea3b

Former-commit-id: 5141a592712ec56b95d3e954507624b5f2081f37
2015-08-28 11:27:27 -04:00

1096 lines
35 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:
# AvnLib.py
# GFS1-NHD:A6626.0000-SCRIPT;1.45
#
# Status:
# DELIVERED
#
# History:
# Revision 1.45 (DELIVERED)
# Created: 05-NOV-2009 20:42:46 OBERFIEL
# makeTempo() function 'promotes' all items to
# prevailing conditions in dictionary.
#
# Revision 1.44 (DELIVERED)
# Created: 25-SEP-2009 14:30:06 OBERFIEL
# Change to support hacked code.
#
# Revision 1.43 (DELIVERED)
# Created: 24-AUG-2009 14:35:19 OBERFIEL
# Added code to support "CB not in TAF" when evaluating CCFP
# guidance.
#
# Revision 1.42 (DELIVERED)
# Created: 20-JUL-2009 10:02:28 GILMOREDM
# Fixed error that gave wrong flight cat when vsby or cig was
# missing
#
# Revision 1.41 (DELIVERED)
# Created: 22-APR-2009 19:30:15 OBERFIEL
# Added exception handling when BUFR model data is being
# processed.
#
# Revision 1.40 (REVIEW)
# Created: 20-MAR-2009 18:22:14 OBERFIEL
# Removed code cruft. ETA changed to NAM. NGMMOS removed.
#
# Revision 1.39 (UNDER WORK)
# Created: 17-MAR-2009 13:33:51 GILMOREDM
# Code now properly checks TEMPO wx
#
# Revision 1.38 (DELIVERED)
# Created: 31-DEC-2008 10:14:27 OBERFIEL
# Changes to support amending TAFs prior to valid period.
#
# Revision 1.37 (DELIVERED)
# Created: 31-OCT-2008 10:35:15 GILMOREDM
# fixed issue in flightCategory
#
# Revision 1.36 (DELIVERED)
# Created: 18-SEP-2008 13:34:09 OBERFIEL
# Expanded grace period of the transmission window slightly
#
# Revision 1.35 (DELIVERED)
# Created: 17-SEP-2008 20:33:32 OBERFIEL
# Lengthened the grace period for regularly issued TAF a tiny
# bit.
#
# Revision 1.34 (DELIVERED)
# Created: 02-SEP-2008 13:08:50 OBERFIEL
# Updated rule to account for 30-h length TAF and allow COR
# on the first line
#
# Revision 1.33 (DELIVERED)
# Created: 01-AUG-2008 15:44:46 OBERFIEL
# Synch'd up with changes in OB8.3.1
#
# Revision 1.32 (DELIVERED)
# Created: 19-JUN-2008 14:18:09 OBERFIEL
# Fixed getFmtValidTime to allow for 30-h header.
#
# Revision 1.31 (INITIALIZE)
# Created: 10-JUN-2008 12:34:27 OBERFIEL
# Fixed getFmtHeaderTime() routine and removed references to
# TWEBs
#
# Revision 1.30 (DELIVERED)
# Created: 14-MAR-2008 10:10:45 OBERFIEL
# Adjusted visibility thresholds downward slightly in LIFR to
# VLIFR categories
#
# Revision 1.29 (DELIVERED)
# Created: 26-FEB-2008 14:25:35 OBERFIEL
# Fixed _talk notification (unimplemented) and DTG for Nov
# 2008
#
# Revision 1.28 (DELIVERED)
# Created: 25-MAY-2007 14:27:09 OBERFIEL
# Update to support additional information in remarks
#
# Revision 1.27 (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 1.26 (DELIVERED)
# Created: 02-JUN-2006 10:28:08 TROJAN
# spr 7160: changed regular expression for visibility
#
# Revision 1.25 (DELIVERED)
# Created: 02-JUN-2006 09:04:21 TROJAN
# spr 7159: changed regular expression for visibility
#
# Revision 1.24 (DELIVERED)
# Created: 16-MAY-2006 10:36:40 TROJAN
# spr 7148: fixes TEMPO without sky/visibility
#
# Revision 1.23 (DELIVERED)
# Created: 09-MAY-2006 16:04:35 TROJAN
# SPR 7145: fixes TEMPO without sky/visibility
#
# Revision 1.22 (DELIVERED)
# Created: 09-MAR-2006 13:42:58 TROJAN
# spr 7107: TEMPO elements fix
#
# Revision 1.21 (DELIVERED)
# Created: 09-MAR-2006 12:48:49 TROJAN
# fix in findIndex() for missing elements
#
# Revision 1.20 (DELIVERED)
# Created: 13-FEB-2006 10:09:20 TROJAN
# fix wrong indentantion of PROB groups
#
# Revision 1.19 (APPROVED)
# Created: 23-JAN-2006 08:23:10 TROJAN
# stdr 956
#
# Revision 1.18 (APPROVED)
# Created: 12-OCT-2005 18:27:31 TROJAN
# spr 7039
#
# Revision 1.17 (DELIVERED)
# Created: 09-SEP-2005 13:53:20 TROJAN
# spr 7011
#
# Revision 1.16 (DELIVERED)
# Created: 16-AUG-2005 13:53:04 TROJAN
# spr 6988
#
# Revision 1.15 (APPROVED)
# Created: 09-AUG-2005 15:02:46 TROJAN
# spr 6975
#
# Revision 1.14 (DELIVERED)
# Created: 29-JUL-2005 18:55:09 TROJAN
# spr 6956
#
# Revision 1.13 (APPROVED)
# Created: 06-JUL-2005 21:01:41 TROJAN
# spr 6909
#
# Revision 1.12 (DELIVERED)
# Created: 12-MAY-2005 14:05:53 TROJAN
# spr 6833
#
# Revision 1.11 (REVIEW)
# Created: 07-MAY-2005 11:52:17 OBERFIEL
# Added Item Header Block
#
# Revision 1.10 (DELIVERED)
# Created: 04-APR-2005 15:20:32 TROJAN
# spr 6780
#
# Revision 1.9 (APPROVED)
# Created: 02-APR-2005 17:02:15 TROJAN
# spr 6763
#
# Revision 1.8 (DELIVERED)
# Created: 14-FEB-2005 20:54:46 TROJAN
# spr 6649
#
# Revision 1.7 (APPROVED)
# Created: 23-JAN-2005 19:05:30 TROJAN
# spr 6586
#
# Revision 1.6 (APPROVED)
# Created: 07-DEC-2004 14:13:59 TROJAN
# spr 6521
#
# Revision 1.5 (APPROVED)
# Created: 16-NOV-2004 20:12:09 PCMS
# Restoring history
#
# Revision 1.4 (DELIVERED)
# Created: 19-MAR-2004 18:32:39 TROJAN
# spr 5922
#
# Revision 1.3 (DELIVERED)
# Created: 15-JAN-2004 22:30:10 PCMS
# Fixed AdjustTimes tool which didn't work in second half of
# the month
#
# Revision 1.2 (DELIVERED)
# Created: 08-JAN-2004 21:39:50 PCMS
# Updating for code cleanup
#
# Revision 1.1 (APPROVED)
# Created: 06-NOV-2003 16:45:19 OBERFIEL
# date and time created -2147483647/-2147483648/-2147481748
# -2147483648:-2147483648:-2147483648 by oberfiel
#
# Change Document History:
# 1:
# Change Document: GFS1-NHD_SPR_7432
# Action Date: 06-NOV-2009 08:30:19
# Relationship Type: In Response to
# Status: NEXTRELEASE
# Title: OB9.2 AvnFPS - TPO/FuelAlternate Rule Doesn't work
#
# Date Ticket# Engineer Description
# ---------- ---------- ----------- --------------------------
# 08/03/2015 17540 zhao Modified to make default issue time configurable
#
import itertools, os, time
import Avn, Globals
import ConfigParser
# transmission times
_Fcst_Times = (6*3600, 12*3600, 18*3600, 24*3600)
_Xmit_Windows = (2400, 1200)
###############################################################################
# common functions used by forecast editors
###############################################################################
def _startHour(bbb, t):
if bbb and bbb[0] != ' ': # 1731Z -> 1818
t += 1800.0
tsec = t % 86400.0
for i in range(len(_Fcst_Times)):
if tsec < _Fcst_Times[i]:
break
else:
i = 0
if bbb and bbb[0] != ' ': # not regular: previous period
i -= 1
return (_Fcst_Times[i] // 3600) % 24
##############################################################################
def getFmtHeaderTime(kind, bbb, t=None):
# Returns header time string: one hour less than start of valid time
if t is None:
t = time.time()
tms = list(time.gmtime(t))
try:
if bbb[0] != ' ':
tms[4:6] = 0, 0
else:
raise IndexError
except IndexError:
hour = (_startHour(bbb, t) - 1) % 24
thour = tms[3]
if thour < hour - 12: # previous day
tms = list(time.gmtime(t-86400.0))
elif thour > hour + 12: # next day
tms = list(time.gmtime(t+86400.0))
tms[3:6] = hour, 0, 0
return time.strftime('%d%H00', tuple(tms))
def getValidTime(kind, bbb, t=None):
# returns start of valid time
if t is None:
t = time.time()
starthour = _startHour(bbb, t)
if bbb and bbb[0] != ' ':
# amendments and delayed forecasts: use closest hour
starthour = int(((t+1800.0)//3600))%24
if starthour < time.gmtime(t)[3]:
t += 86400.0
tms = list(time.gmtime(t))
tms[3:6] = starthour, 0, 0
return Avn.mkgmtime(tms)
def getFmtValidTime(kind, bbb, t=None, tafDuration=24, evtime=None):
# Returns valid time as yymmddHHhh for 'bbb'
if t is None:
t = time.time()
#
# Get the ending day and hour first
tms = list(time.gmtime(t))
tms[3:6] = _startHour(bbb, t), 0, 0
if tms[3] == 0:
try:
if bbb[0] == ' ':
tms[2] += 1
except IndexError:
tms[2] += 1
tms = list(time.gmtime(Avn.mkgmtime(tms) + tafDuration*3600))
endday,endhour = tms[2:4]
if tms[3] == 0:
tms[3] = -1
endday,endhour = time.gmtime(Avn.mkgmtime(tms))[2],24
#
# for amendments and delayed forecasts: use closest hour
if bbb and bbb[0] != ' ':
starthour = int(((t+1800.0)//3600))%24
#
# If amendment needed before valid period starts (up to 40 minutes
# prior (2400s)), preserve previous ending valid time
#
if evtime and (_Fcst_Times[0] - t%_Fcst_Times[0]) < 2400:
endday,endhour = int(evtime[:2]),int(evtime[2:])
#
# Otherwise, find the next regular issuance time
else:
starthour = _startHour(bbb,t)
tms = list(time.gmtime(t))
if starthour < tms[3]:
t += 86400.0
tms = list(time.gmtime(t))
tms[3:6] = starthour, 0, 0
year, month, startday = tms[:3]
return '%02d%02d%02d%02d/%02d%02d' % (year-2000, month, startday, starthour, endday, endhour)
def getIssueTime(kind, bbb, t=None):
if t is None:
t = time.time()
if not bbb or bbb[0] == ' ': # regular issue forecast
itime = Avn.string2time('%s00' % getFmtValidTime(kind, bbb, t)[:8])
minutesBeforeForecastTime = getMinutesBeforeForecastTime()
if minutesBeforeForecastTime != None:
itime -= 60*int(minutesBeforeForecastTime)
else:
itime -= _Xmit_Windows[0]
if itime > t:
return itime
return t
def getMinutesBeforeForecastTime():
try:
fname = os.path.join(Avn.ConfigDir, 'default_issue_time.cfg')
f = Avn.PATH_MGR.getStaticFile(fname)
fname = f.getPath()
if not (os.path.exists(fname)):
return None
cp = ConfigParser.RawConfigParser()
cp.read(fname)
d = cp.get('minutesBeforeForecastTime', 'minutes')
return d
except Exception:
raise
def getFmtIssueTime(kind, bbb, t=None):
if t is None:
t = time.time()
if kind == 'taf':
return time.strftime('%d%H%MZ', \
time.gmtime(getIssueTime(kind, bbb, t)))
else:
raise Avn.AvnError, 'Programming bug'
# the next 2 functions use current time
def getXmitTime(kind, headertime):
t = Avn.string2time('%s%s' % (Avn.time2string()[:6], headertime[2:])) \
+ 3600.0 - _Xmit_Windows[0]
now = time.time()
if t < now - 43200.0:
t += 86400.0
return max(t, now)
def getBBB(kind):
# Returns either '' or 'RRX'
eow = _Xmit_Windows[1]
now = time.time() % 86400.0
#
# Allow some leeway here
for t in _Fcst_Times:
if 0 < t - now < (eow-59):
return 'RRX'
else:
return ''
def printForecast(filelist):
content = ['Printed by %s on %s' % \
(Globals.Forecaster, time.strftime('%x %X')), '']
for f in filelist:
content.append(' '.join(os.path.basename(f).split('-')[2:]))
content.append(Globals.DRC.getFile(f))
(chldin, chldout) = os.popen4('lpr', -1)
chldin.write('\n'.join(content))
chldin.close()
return chldout.read()
###############################################################################
def configureFlightCatColors(text):
for tag in [Avn.LIFR, Avn.IFR, Avn.MVFR, Avn.VFR]:
text.tag_configure(tag, \
background=text.option_get('%sColor' % tag, ''))
###############################################################################
# functions used to produce forecasts
##############################################################################
def _split_line(line, indent=0):
# used by indentTaf()
maxlen = 68 - indent
if indent > 0:
lst1 = [' '*indent]
else:
lst1 = []
lst2 = [' '*5]
total = 0
for word in line.split():
if total + len(word) < maxlen:
lst1.append(word)
else:
lst2.append(word)
total += len(word)+1
return ' '.join(lst1), ' '.join(lst2).rstrip()
def indentTaf(lines):
taf = []
lines = filter(None, [x.strip() for x in lines])
if not lines:
return taf
k = 0
if lines[0][:3] == 'TAF':
taf.append(lines[0])
k += 1
l1, l2 = _split_line(lines[k])
taf.append(l1)
if l2:
taf.append(l2)
k += 1
for line in lines[k:]:
if line.startswith('FM'):
l1, l2 = _split_line(line, 4)
elif line.startswith('TEMPO'):
l1, l2 = _split_line(line, 5)
elif line.startswith('PROB'):
prevline = taf.pop()
if taf:
indent = 4
else:
indent = 0
l1, l2 = _split_line('%s %s' % (prevline, line), indent)
else:
l1, l2 = _split_line(line, 5)
taf.append(l1)
if l2:
taf.append(l2)
if not taf[-1].endswith('='):
taf[-1] += '='
return taf
def adjustTimes(bbb, taf):
# removes forecast periods that passed, adjust start of valid time
if not 'group' in taf:
# NIL TAF
return
stime = getValidTime('taf', bbb)
groups = [g for g in taf['group'] if g['prev']['time']['to'] > stime]
for g in groups:
if 'ocnl' in g and g['ocnl']['type'] == 'PROB' and \
g['ocnl']['time']['from'] < stime+32400:
del g['ocnl']
g = groups[0]
if g['prev']['time']['from'] < stime:
g['prev']['time']['from'] = stime
if 'ocnl' in g:
if g['ocnl']['time']['from'] < stime:
if g['ocnl']['time']['to'] - stime < 3600.0:
del g['ocnl']
else:
g['ocnl']['time']['from'] = stime
taf['group'] = groups
def formatRecForMAIN(rec, ident, bbb, itime=None, tafDuration=24, evtime=None):
# returns formated MAIN group as a list of strings
kind = 'taf'
lst = [ident, getFmtIssueTime(kind, bbb, itime), \
getFmtValidTime(kind, bbb, itime, tafDuration, evtime)[4:]]
if not rec :
# no data or too early for forecast
return lst
try:
if 'wind' in rec:
lst.append(rec['wind']['str'])
if 'vsby' in rec:
lst.append(rec['vsby']['str'])
if 'pcp' in rec:
lst.append(rec['pcp']['str'])
if 'obv' in rec:
lst.append(rec['obv']['str'])
if 'vcnty' in rec:
lst.append(rec['vcnty']['str'])
if 'sky' in rec:
lst.append(rec['sky']['str'])
if 'llws' in rec:
lst.append(rec['llws']['str'])
except KeyError:
pass
return lst
def formatRecForFM(rec):
# returns formatted FM group as a list of strings
if not rec:
return []
lst = [time.strftime('FM%d%H%M', time.gmtime(rec['time']['from']))]
try:
lst.extend([rec[k]['str'] for k in \
['wind', 'vsby', 'pcp', 'obv', 'vcnty', 'sky', 'llws'] if k in rec])
except KeyError:
pass
return lst
def formatRecForOCNL(rec):
# returns formated TEMPO group as a list of strings
# or an empty list if TEMPO not needed
if not rec or rec['type'] not in ['TEMPO', 'PROB']:
return []
start,end = time.gmtime(rec['time']['from']),\
list(time.gmtime(rec['time']['to']))
if end[3] == 0:
end[2] = start[2]
end[3] = 24
lst = []
typ = rec['type']
if typ == 'TEMPO':
lst = ['TEMPO']
elif typ == 'PROB':
lst = ['PROB30']
if len(lst):
lst.append('%02d%02d/%02d%02d' % (start[2],start[3],end[2],end[3]))
try:
lst.extend([rec[k]['str'] for k in \
['wind', 'vsby', 'pcp', 'obv', 'nsw', 'sky'] if k in rec])
except KeyError:
pass
if len(lst) > 1:
return lst
else:
return []
def _filterPeriods(bbb, periods, t, kind, tafDuration):
starttime = getValidTime(kind, bbb, t)
tms = list(time.gmtime(starttime))
tms[3] = _startHour(bbb, t)
endtime = Avn.mkgmtime(tms)
if endtime <= starttime:
endtime += tafDuration*3600.0
def _within(p):
return p['prev']['time']['to'] > starttime and \
p['prev']['time']['from'] < endtime
return itertools.ifilter(_within, periods)
def makeTafFromPeriods(ident, bbb, periods, t=None, tafDuration=24, evtime=None):
# returns formatted TAF given sequence of periods
if bbb is None:
bbb = 'RRA'
if t is None:
t = time.time()
if bbb.startswith('A'):
lines = ['TAF AMD']
else:
lines = ['TAF']
inperiods = _filterPeriods(bbb, periods, t, 'taf', tafDuration)
try:
p = inperiods.next()
if getValidTime('taf', bbb, t) < p['prev']['time']['from']-1800.0:
# start time of the first group in the future
tmp=formatRecForMAIN(None, ident, bbb, t, tafDuration, evtime)
lines.append(' '.join(' '.join(tmp).split()))
tmp = formatRecForFM(p['prev'])
else:
tmp = formatRecForMAIN(p['prev'], ident, bbb, t, tafDuration, evtime)
if not tmp:
raise StopIteration
lines.append(' '.join(' '.join(tmp).split()))
if 'ocnl' in p:
tmp = formatRecForOCNL(p['ocnl'])
if tmp:
lines.append(' '.join(' '.join(tmp).split()))
while 1:
p = inperiods.next()
tmp = formatRecForFM(p['prev'])
lines.append(' '.join(' '.join(tmp).split()))
if 'ocnl' in p:
tmp = formatRecForOCNL(p['ocnl'])
if tmp:
lines.append(' '.join(' '.join(tmp).split()))
except StopIteration:
pass
return lines
# default categories, used for eliminating unneccessary weather from TEMPO
_VsbyCat = [0.5, 1.0, 2.0, 3.0, 5.0]
_CigCat = [2, 6, 10, 20, 31]
def fixTafVsby(v):
# returns valid TAF visibility as a dictionary {val, str}
if v < 0.0:
return {}
elif v < 0.12:
return {'value': 0.0, 'str': '0SM'}
elif v < 0.37:
return {'value': 0.25, 'str': '1/4SM'}
elif v < 0.62:
return {'value': 0.5, 'str': '1/2SM'}
elif v < 0.87:
return {'value': 0.75, 'str': '3/4SM'}
elif v < 1.3:
return {'value': 1.0, 'str': '1SM'}
elif v < 1.8:
return {'value': 1.5, 'str': '1 1/2SM'}
elif v < 6.5:
iv = (v+0.49)//1.0
return {'value': iv, 'str': '%.0fSM' % iv}
else:
return {'value': 12.0, 'str': 'P6SM'}
def fixCldBase(h):
# returns valid TAF cloud base
if h < 30:
return h
elif h < 50:
return (h+2)//5 * 5
else:
return (h+5)//10 * 10
def fixTafSky(sky):
# returns valid TAF sky conditions as a tuple (cig, string)
# replaces CLR by SKC, rounds cloud bases
_Cover = {'FEW': 1, 'SCT': 2, 'BKN': 3, 'OVC': 4}
nsky = []
s = sky['str']
if s.find('CLR') != -1:
return {'cig': Avn.UNLIMITED, 'str': 'SKC'}
elif s.find('VV') != -1:
return sky
cover = 0
for c, h, cb in [(x[:3], int(x[3:6]), x[6:]) for x in s.split()]:
if cb == 'TCU':
cb = ''
cover = max(cover, _Cover.get(c, 0))
nsky.append('%s%03d%s' % (c, fixCldBase(h), cb))
return {'cig': fixCldBase(sky['cig']), 'cover': sky['cover'], \
'str': ' '.join(nsky)}
def updateTafWithMetar(tafgrp, mtr):
# replaces TAF group with METAR data
if 'vsby' in mtr:
tafgrp['vsby'] = fixTafVsby(mtr['vsby']['value'])
if 'sky' in mtr:
tafgrp['sky'] = fixTafSky(mtr['sky'])
for k in ['wind', 'pcp', 'obv']:
if k in mtr:
tafgrp[k] = mtr[k]
elif k in tafgrp:
del tafgrp[k]
def fixTafTempo(p, t):
# eliminates TAF TEMPO elements that match those in FM group
if not ('obv' in t or 'pcp' in t) and 'vsby' in t:
tcat = Avn.category(t['vsby']['val'], _VsbyCat)
pcat = Avn.category(p['vsby']['val'], _VsbyCat)
if tcat == pcat:
del t['vsby']
else:
t['obv'] = {'str': 'NSW'}
if 'wind' in t:
tdd, tff = t['wind']['dd'], t['wind']['ff']
pdd, pff = p['wind']['dd'], p['wind']['ff']
if Avn.VARIABLE in (tdd, pdd):
delta = 0
else:
delta = abs(pdd - tdd)
if delta > 180:
delta = 360 - delta
if abs(tff-pff) < 10 and (max(tff, pff) < 12 or delta < 30):
del t['wind']
if 'sky' in t:
tcat = Avn.category(t['sky']['cig'], _CigCat)
pcat = Avn.category(p['sky']['cig'], _CigCat)
if tcat == pcat:
del t['sky']
###############################################################################
# utility functions
##############################################################################
def getGroupIndex(g):
ixlist = [g[i]['index'] for i in g if 'index' in g[i]]
line = int(ixlist[0][0].split('.')[0])
fchar = min([int(x[0].split('.')[1]) for x in ixlist])
lchar = max([int(x[1].split('.')[1]) for x in ixlist])
return '%d.%d' % (line, fchar), '%d.%d' % (line, lchar)
def getTafPeriods(taf):
periods = []
indices = []
for p in taf['group'][:]:
pp = p['prev']
periods.append(pp)
indices.append(getGroupIndex(pp))
if 'ocnl' in p:
po = pp.copy()
po.update(p['ocnl'])
periods.append(po)
indices.append(getGroupIndex(p['ocnl']))
return zip(indices, periods)
def flightCategory(dcd):
# dcd: dictionary d = {'cig': (cig, ...), 'vsby': (vsby, ...)}
sky, vis = dcd.get('sky',{}), dcd.get('vsby',{})
cig, vsby = sky.get('cig',-1), vis.get('value',-1)
if cig < 0: #cig is missing, vsby may be valid
if vsby < 0: #vsby missing, default to VFR
return Avn.VFR
elif vsby < 1.0:
return Avn.LIFR
elif vsby < 3.0:
return Avn.IFR
elif vsby < 6.0:
return Avn.MVFR
return Avn.VFR
elif vsby < 0: #vsby is missing, cig is valid (took care of cig & vsby both missing already)
if cig < 500:
return Avn.LIFR
elif cig < 1000:
return Avn.IFR
elif cig < 3100:
return Avn.MVFR
return Avn.VFR
elif cig < 500 or vsby < 1.0:
return Avn.LIFR
elif cig < 1000 or vsby < 3.0:
return Avn.IFR
elif cig < 3100 or vsby < 6.0:
return Avn.MVFR
else:
return Avn.VFR
##############################################################################
# functions used by monitors
##############################################################################
def findIndex(e, dcd, hlen):
def _update(ix):
if hlen == 0:
return ix
l0, c0 = ix[0].split('.')
l1, c1 = ix[1].split('.')
return '%d.%s' % (int(l0)+hlen,c0), '%d.%s' % (int(l1)+hlen,c1)
if e == 'wx':
ix = []
if 'pcp' in dcd:
ix.append(_update(dcd['pcp']['index']))
if 'obv' in dcd:
ix.append(_update(dcd['obv']['index']))
return ix
elif e == 'cat':
ix = []
if 'vsby' in dcd:
ix.append(_update(dcd['vsby']['index']))
if 'sky' in dcd:
ix.append(_update(dcd['sky']['index']))
return ix
elif e in dcd:
return [_update(dcd[e]['index'])]
else:
return []
def _makeWx(g):
"""Returns wx string"""
try:
return ' '.join([g[k]['str'] for k in ['pcp', 'obv'] if k in g]) or None
except KeyError:
return None
def makeMetarData(metar):
d = {'time': metar['itime']['value']}
try:
tmp = metar['wind']
d['wind'] = {'dd': tmp['dd'], \
'ff': {'lo': tmp['ff'], 'hi': tmp.get('gg', tmp['ff'])}}
except KeyError:
pass
try:
d['vsby'] = {'vsby': metar['vsby']['value']}
except KeyError:
pass
s = _makeWx(metar)
if s:
d['wx'] = {'str': s}
try:
d['sky'] = {'cig': metar['sky']['cig']}
except KeyError:
pass
#
# Optional information from remarks section
try:
d['vvsby'] = {'lo': metar['vvsby']['lo'],
'hi': metar['vvsby']['hi']}
except KeyError:
pass
try:
d['vsky'] = {'cvr1': metar['vsky']['cvr1'],
'cvr2': metar['vsky']['cvr2'],
'cig': metar['vsky']['cig']}
except KeyError:
pass
try:
d['vcig'] = {'lo': metar['vcig']['lo'],
'hi': metar['vcig']['hi']}
except KeyError:
pass
return d
##############################################################################
# TAF structure used by monitors
##############################################################################
class TafData:
def _makeTafWind(g, items):
d = {}
try:
if 'p' in items and 'wind' in g['prev']:
tmp = g['prev']['wind']
d['dd'] = {'prev': tmp['dd']}
d['ff'] = {'prev': tmp.get('gg', tmp['ff'])}
if 'o' in items and g['ocnl'] and 'wind' in g['ocnl']:
tmp = g['ocnl']['wind']
if 'dd' in d:
d['dd']['ocnl'] = tmp['dd']
else:
d['dd'] = {'ocnl': tmp['dd']}
if 'ff' in d:
d['ff']['ocnl'] = tmp.get('gg', tmp['ff'])
else:
d['ff'] = {'ocnl': tmp.get('gg', tmp['ff'])}
lo = d['ff'].get('prev', 0)
hi = d['ff'].get('ocnl', lo)
if lo > hi:
lo, hi = hi, lo
d['ff']['lo'] = lo
d['ff']['hi'] = hi
except KeyError:
pass
return d
_makeTafWind = staticmethod(_makeTafWind)
def _makeTafVsby(g, items):
d = {}
try:
if 'p' in items and 'vsby' in g['prev']:
d['hi'] = g['prev']['vsby']['value']
d['prev'] = g['prev']['vsby']['value']
if 'o' in items and 'ocnl' in g:
if 'vsby' in g['ocnl']:
d['lo'] = g['ocnl']['vsby']['value']
d['ocnl'] = g['ocnl']['vsby']['value']
elif 'vsby' in g['prev']:
d['hi'] = g['prev']['vsby']['value']
d['prev'] = g['prev']['vsby']['value']
else:
return d
if 'hi' in d and not 'lo' in d:
d['lo'] = d['hi']
if 'lo' in d and not 'hi' in d:
d['hi'] = d['lo']
if d['lo'] > d['hi']:
d['lo'], d['hi'] = d['hi'], d['lo']
except KeyError:
pass
return d
_makeTafVsby = staticmethod(_makeTafVsby)
def _makeTafWx(g, items):
pstr, ostr = None, None
if 'p' in items:
pstr = _makeWx(g['prev'])
if 'o' in items and 'ocnl' in g:
g = g['ocnl']
if 'nsw' in g:
ostr = None
elif 'pcp' in g or 'obv' in g:
ostr = _makeWx(g)
elif pstr:
ostr = pstr
d = {}
if pstr:
d['pstr'] = pstr
if ostr:
d['ostr'] = ostr
return d
_makeTafWx = staticmethod(_makeTafWx)
def _makeTafSky(g, items):
d = {}
try:
if 'p' in items and 'sky' in g['prev']:
d['hi'] = g['prev']['sky']['cig']
d['prev'] = g['prev']['sky']['cig']
if 'o' in items:
if 'ocnl' in g and 'sky' in g['ocnl']:
d['lo'] = g['ocnl']['sky']['cig']
d['ocnl'] = g['ocnl']['sky']['cig']
elif 'sky' in g['prev']:
d['hi'] = g['prev']['sky']['cig']
d['prev'] = g['prev']['sky']['cig']
else:
return d
if 'hi' in d and not 'lo' in d:
d['lo'] = d['hi']
if 'lo' in d and not 'hi' in d:
d['hi'] = d['lo']
if d['lo'] > d['hi']:
d['lo'], d['hi'] = d['hi'], d['lo']
except KeyError:
pass
return d
_makeTafSky = staticmethod(_makeTafSky)
def _makeTafCover(g, items):
d = {}
try:
if 'p' in items and 'sky' in g['prev']:
d['hi'] = g['prev']['sky']['cover']
d['prev'] = g['prev']['sky']['cover']
if 'o' in items:
if 'ocnl' in g and 'sky' in g['ocnl']:
d['lo'] = g['ocnl']['sky']['cover']
d['ocnl'] = g['ocnl']['sky']['cover']
elif 'sky' in g['prev']:
d['hi'] = g['prev']['sky']['cover']
d['prev'] = g['prev']['sky']['cover']
else:
return d
if 'hi' in d and not 'lo' in d:
d['lo'] = d['hi']
if 'lo' in d and not 'hi' in d:
d['hi'] = d['lo']
if d['lo'] > d['hi']:
d['lo'], d['hi'] = d['hi'], d['lo']
except KeyError:
pass
return d
_makeTafCover = staticmethod(_makeTafCover)
def _makeTafTS(g, items):
d = {}
try:
if 'p' in items:
if 'pcp' in g['prev'] and 'TS' in g['prev']['pcp']['str']:
d['prev'] = 'Y'
elif 'vcnty' in g['prev'] and 'TS' in g['prev']['vcnty']['str']:
d['prev'] = 'VC'
if 'o' in items and 'ocnl' in g:
if 'pcp' in g['ocnl'] and 'TS' in g['ocnl']['pcp']['str']:
d['ocnl'] = g['ocnl']['type']
except KeyError:
pass
return d
_makeTafTS = staticmethod(_makeTafTS)
def _makeTafCB(g, items):
d = {}
try:
if 'p' in items and g['prev']['sky']['str'].find('CB') > -1:
d['prev']='Y'
raise KeyError
if 'o' in items and 'ocnl' in g and g['ocnl']['sky']['str'].find('CB') > -1:
d['prev']='Y'
except KeyError:
pass
return d
_makeTafCB = staticmethod(_makeTafCB)
def _makeTafLLWS(g, items):
d = {}
if 'p' in items:
if 'llws' in g['prev']:
d['prev'] = True
return d
_makeTafLLWS = staticmethod(_makeTafLLWS)
def _make(g, t1, t2, items):
d = {}
for key, f in [('wind', TafData._makeTafWind), \
('vsby', TafData._makeTafVsby), \
('wx', TafData._makeTafWx), \
('sky', TafData._makeTafSky), \
('cover', TafData._makeTafCover), \
('ts', TafData._makeTafTS), \
('cb', TafData._makeTafCB), \
('llws', TafData._makeTafLLWS)]:
tmp = f(g, items)
if tmp:
d[key] = tmp
d['from'], d['to'] = t1, t2
return d
_make = staticmethod(_make)
def makeTempo(grp):
try:
to1, to2 = grp['ocnl']['time']['from'], grp['ocnl']['time']['to']
tempo = TafData._make(grp, to1, to2, ['o'])
tempo['tempoCheck'] = True
#
# Promote TEMPO items to prevailing
try:
tempo['wx']['pstr'] = tempo['wx']['ostr']
del tempo['wx']['ostr']
except KeyError:
pass
for item in tempo.keys():
try:
tempo[item]['prev']=tempo[item]['ocnl']
del tempo[item]['ocnl']
except (TypeError,KeyError):
pass
return tempo
except KeyError:
return None
makeTempo = staticmethod(makeTempo)
def __init__(self, groups):
self._data = []
for g in groups:
tp1, tp2 = g['prev']['time']['from'], g['prev']['time']['to']
if 'ocnl' in g:
to1, to2 = g['ocnl']['time']['from'], g['ocnl']['time']['to']
if tp1 < to1:
d = TafData._make(g, tp1, to1, ['p'])
self._data.append(d)
tp1 = to1
d = TafData._make(g, tp1, to2, ['p', 'o'])
self._data.append(d)
if to2 < tp2:
d = TafData._make(g, to2, tp2, ['p'])
self._data.append(d)
else:
d = TafData._make(g, tp1, tp2, ['p'])
self._data.append(d)
def get(self, t):
if t < self._data[0]['from'] or t >= self._data[-1]['to']:
return None
for d in self._data:
if d['from'] <= t < d['to']:
return d
return None