1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
#pylint: disable=invalid-name
import datetime
import re
# python 2.7 backport
if not hasattr(datetime, 'timezone'):
class UTC(datetime.tzinfo):
def utcoffset(self, dt):
return datetime.timedelta(0)
class timezone(object):
utc = UTC()
datetime.timezone = timezone
def valid(s):
return s == 'A'
def timestamp(s):
'''
Converts a timestamp given in "hhmmss[.ss]" ASCII text format to a
datetime.time object
'''
ms_s = s[6:]
ms = ms_s and int(float(ms_s) * 1000000) or 0
t = datetime.time(
hour=int(s[0:2]),
minute=int(s[2:4]),
second=int(s[4:6]),
microsecond=ms,
tzinfo=datetime.timezone.utc)
return t
def datestamp(s):
'''
Converts a datestamp given in "DDMMYY" ASCII text format to a
datetime.datetime object
'''
return datetime.datetime.strptime(s, '%d%m%y').date()
def dm_to_sd(dm):
'''
Converts a geographic co-ordinate given in "degrees/minutes" dddmm.mmmm
format (eg, "12319.943281" = 123 degrees, 19.943281 minutes) to a signed
decimal (python float) format
'''
# '12319.943281'
if not dm or dm == '0':
return 0.
r = re.match(r'^(\d+)(\d\d\.\d+)$', dm)
if not r:
raise ValueError("Geographic coordinate value '{}' is not valid DDDMM.MMM".format(dm))
d, m = r.groups()
return float(d) + float(m) / 60
class LatLonFix(object):
'''Mixin to add `latitude` and `longitude` properties as signed decimals
to NMEA sentences which have co-ordinates given as degrees/minutes (lat, lon)
and cardinal directions (lat_dir, lon_dir)'''
#pylint: disable=no-member
@property
def latitude(self):
'''Latitude in signed degrees (python float)'''
sd = dm_to_sd(self.lat)
if self.lat_dir == 'N':
return +sd
elif self.lat_dir == 'S':
return -sd
else:
return 0.
@property
def longitude(self):
'''Longitude in signed degrees (python float)'''
sd = dm_to_sd(self.lon)
if self.lon_dir == 'E':
return +sd
elif self.lon_dir == 'W':
return -sd
else:
return 0.
@staticmethod
def _minutes(x):
return abs(x * 60.) % 60.
@staticmethod
def _seconds(x):
return abs(x * 3600.) % 60.
@property
def latitude_minutes(self):
return self._minutes(self.latitude)
@property
def longitude_minutes(self):
return self._minutes(self.longitude)
@property
def latitude_seconds(self):
return self._seconds(self.latitude)
@property
def longitude_seconds(self):
return self._seconds(self.longitude)
class DatetimeFix(object):
#pylint: disable=no-member
@property
def datetime(self):
return datetime.datetime.combine(self.datestamp, self.timestamp)
class ValidStatusFix(object):
#pylint: disable=no-member
@property
def is_valid(self):
return self.status == 'A'
class ValidRMCStatusFix(ValidStatusFix):
#pylint: disable=no-member
@property
def is_valid(self):
status = super(ValidRMCStatusFix, self).is_valid
if self.name_to_idx["mode_indicator"] < len(self.data):
status &= self.mode_indicator in tuple('ADEFMPRS')
if self.name_to_idx["nav_status"] < len(self.data):
status &= self.nav_status in tuple('SCU')
return status
class ValidGSAFix(object):
#pylint: disable=no-member
@property
def is_valid(self):
return int(self.mode_fix_type) in [2, 3]
class ValidGGAFix(object):
#pylint: disable=no-member
@property
def is_valid(self):
return self.gps_qual in range(1,6)
class ValidVBWFix(object):
#pylint: disable=no-member
@property
def is_valid(self):
return self.data_validity_water_spd == self.data_validity_grnd_spd == 'A'
class TZInfo(datetime.tzinfo):
def __init__(self, hh, mm):
self.hh = hh
self.mm = mm
super(TZInfo, self).__init__()
def tzname(self, dt):
return ''
def dst(self, dt):
return datetime.timedelta(0)
def utcoffset(self, dt):
return datetime.timedelta(hours=self.hh, minutes=self.mm)
|