00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 import re
00034 import time
00035 import calendar
00036 import math
00037 import logging
00038 logger = logging.getLogger('rosout')
00039
00040
00041 def safe_float(field):
00042 try:
00043 return float(field)
00044 except ValueError:
00045 return float('NaN')
00046
00047
00048 def safe_int(field):
00049 try:
00050 return int(field)
00051 except ValueError:
00052 return 0
00053
00054
00055 def convert_latitude(field):
00056 return safe_float(field[0:2]) + safe_float(field[2:]) / 60.0
00057
00058
00059 def convert_longitude(field):
00060 return safe_float(field[0:3]) + safe_float(field[3:]) / 60.0
00061
00062
00063 def convert_time(nmea_utc):
00064
00065 utc_struct = time.gmtime()
00066 utc_list = list(utc_struct)
00067
00068 if not nmea_utc[0:2] or not nmea_utc[2:4] or not nmea_utc[4:6]:
00069 return float('NaN')
00070 else:
00071 hours = int(nmea_utc[0:2])
00072 minutes = int(nmea_utc[2:4])
00073 seconds = int(nmea_utc[4:6])
00074 utc_list[3] = hours
00075 utc_list[4] = minutes
00076 utc_list[5] = seconds
00077 unix_time = calendar.timegm(tuple(utc_list))
00078 return unix_time
00079
00080
00081 def convert_status_flag(status_flag):
00082 if status_flag == "A":
00083 return True
00084 elif status_flag == "V":
00085 return False
00086 else:
00087 return False
00088
00089
00090 def convert_knots_to_mps(knots):
00091 return safe_float(knots) * 0.514444444444
00092
00093
00094
00095 def convert_deg_to_rads(degs):
00096 return math.radians(safe_float(degs))
00097
00098 """Format for this is a sentence identifier (e.g. "GGA") as the key, with a
00099 tuple of tuples where each tuple is a field name, conversion function and index
00100 into the split sentence"""
00101 parse_maps = {
00102 "GGA": [
00103 ("fix_type", int, 6),
00104 ("latitude", convert_latitude, 2),
00105 ("latitude_direction", str, 3),
00106 ("longitude", convert_longitude, 4),
00107 ("longitude_direction", str, 5),
00108 ("altitude", safe_float, 9),
00109 ("mean_sea_level", safe_float, 11),
00110 ("hdop", safe_float, 8),
00111 ("num_satellites", safe_int, 7),
00112 ("utc_time", convert_time, 1),
00113 ],
00114 "RMC": [
00115 ("utc_time", convert_time, 1),
00116 ("fix_valid", convert_status_flag, 2),
00117 ("latitude", convert_latitude, 3),
00118 ("latitude_direction", str, 4),
00119 ("longitude", convert_longitude, 5),
00120 ("longitude_direction", str, 6),
00121 ("speed", convert_knots_to_mps, 7),
00122 ("true_course", convert_deg_to_rads, 8),
00123 ]
00124 }
00125
00126
00127 def parse_nmea_sentence(nmea_sentence):
00128
00129 if not re.match('(^\$GP|^\$GN|^\$GL).*\*[0-9A-Fa-f]{2}$', nmea_sentence):
00130 logger.debug("Regex didn't match, sentence not valid NMEA? Sentence was: %s"
00131 % repr(nmea_sentence))
00132 return False
00133 fields = [field.strip(',') for field in nmea_sentence.split(',')]
00134
00135
00136 sentence_type = fields[0][3:]
00137
00138 if not sentence_type in parse_maps:
00139 logger.debug("Sentence type %s not in parse map, ignoring."
00140 % repr(sentence_type))
00141 return False
00142
00143 parse_map = parse_maps[sentence_type]
00144
00145 parsed_sentence = {}
00146 for entry in parse_map:
00147 parsed_sentence[entry[0]] = entry[1](fields[entry[2]])
00148
00149 return {sentence_type: parsed_sentence}