33 """Provides a driver for NMEA GNSS devices.""" 39 from sensor_msgs.msg
import NavSatFix, NavSatStatus, TimeReference
40 from geometry_msgs.msg
import TwistStamped
47 """ROS driver for NMEA GNSS devices.""" 50 """Initialize the ROS NMEA driver. 52 Creates the following ROS publishers: 53 NavSatFix publisher on the 'fix' channel. 54 TwistStamped publisher on the 'vel' channel. 55 QuaternionStamped publisher on the 'heading' channel. 56 TimeReference publisher on the 'time_reference' channel. 58 Reads the following ROS parameters: 59 ~time_ref_source (str): The name of the source in published TimeReference messages. (default None) 60 ~useRMC (bool): If true, use RMC NMEA messages. If false, use GGA and VTG messages. (default False) 61 ~epe_quality0 (float): Value to use for default EPE quality for fix type 0. (default 1000000) 62 ~epe_quality1 (float): Value to use for default EPE quality for fix type 1. (default 4.0) 63 ~epe_quality2 (float): Value to use for default EPE quality for fix type 2. (default (0.1) 64 ~epe_quality4 (float): Value to use for default EPE quality for fix type 4. (default 0.02) 65 ~epe_quality5 (float): Value to use for default EPE quality for fix type 5. (default 4.0) 66 ~epe_quality9 (float): Value to use for default EPE quality for fix type 9. (default 3.0) 68 self.
fix_pub = rospy.Publisher(
'fix', NavSatFix, queue_size=1)
69 self.
vel_pub = rospy.Publisher(
'vel', TwistStamped, queue_size=1)
73 'time_reference', TimeReference, queue_size=1)
76 self.
use_RMC = rospy.get_param(
'~useRMC',
False)
80 """Public method to provide a new NMEA sentence to the driver. 83 nmea_string (str): NMEA sentence in string form. 84 frame_id (str): TF frame ID of the GPS receiver. 85 timestamp(rospy.Time, optional): Time the sentence was received. 86 If timestamp is not specified, the current time is used. 89 bool: True if the NMEA string is successfully processed, False if there is an error. 92 rospy.logwarn(
"Received a sentence with an invalid checksum. " +
93 "Sentence was: %s" % repr(nmea_string))
98 if not parsed_sentence:
100 "Failed to parse NMEA sentence. Sentence was: %s" %
105 current_time = timestamp
107 current_time = rospy.get_rostime()
108 current_fix = NavSatFix()
109 current_fix.header.stamp = current_time
110 current_fix.header.frame_id = frame_id
112 current_time_ref = TimeReference()
113 current_time_ref.header.stamp = current_time
114 current_time_ref.header.frame_id = frame_id
118 current_time_ref.source = frame_id
120 if not self.
use_RMC and 'GGA' in parsed_sentence:
121 data = parsed_sentence[
'GGA']
124 if math.isnan(data[
'utc_time'][0]):
125 rospy.logwarn(
"Time in the NMEA sentence is NOT valid")
127 current_fix.header.stamp = rospy.Time(data[
'utc_time'][0], data[
'utc_time'][1])
129 gps_qual = data[
'fix_type']
131 current_fix.status.status = NavSatStatus.STATUS_NO_FIX
133 current_fix.status.status = NavSatStatus.STATUS_FIX
135 current_fix.status.status = NavSatStatus.STATUS_SBAS_FIX
136 elif gps_qual
in (4, 5):
137 current_fix.status.status = NavSatStatus.STATUS_GBAS_FIX
141 current_fix.status.status = NavSatStatus.STATUS_SBAS_FIX
143 current_fix.status.status = NavSatStatus.STATUS_NO_FIX
150 current_fix.status.service = NavSatStatus.SERVICE_GPS
152 current_fix.header.stamp = current_time
154 latitude = data[
'latitude']
155 if data[
'latitude_direction'] ==
'S':
157 current_fix.latitude = latitude
159 longitude = data[
'longitude']
160 if data[
'longitude_direction'] ==
'W':
161 longitude = -longitude
162 current_fix.longitude = longitude
165 current_fix.position_covariance[0] = hdop ** 2
166 current_fix.position_covariance[4] = hdop ** 2
167 current_fix.position_covariance[8] = (2 * hdop) ** 2
168 current_fix.position_covariance_type = \
169 NavSatFix.COVARIANCE_TYPE_APPROXIMATED
172 altitude = data[
'altitude'] + data[
'mean_sea_level']
173 current_fix.altitude = altitude
175 self.
fix_pub.publish(current_fix)
177 if not (math.isnan(data[
'utc_time'][0])
or self.
use_GNSS_time):
178 current_time_ref.time_ref = rospy.Time(
179 data[
'utc_time'][0], data[
'utc_time'][1])
183 elif not self.
use_RMC and 'VTG' in parsed_sentence:
184 data = parsed_sentence[
'VTG']
189 current_vel = TwistStamped()
190 current_vel.header.stamp = current_time
191 current_vel.header.frame_id = frame_id
192 current_vel.twist.linear.x = data[
'speed'] * math.sin(data[
'true_course'])
193 current_vel.twist.linear.y = data[
'speed'] * math.cos(data[
'true_course'])
194 self.
vel_pub.publish(current_vel)
196 elif 'RMC' in parsed_sentence:
197 data = parsed_sentence[
'RMC']
200 if math.isnan(data[
'utc_time'][0]):
201 rospy.logwarn(
"Time in the NMEA sentence is NOT valid")
203 current_fix.header.stamp = rospy.Time(data[
'utc_time'][0], data[
'utc_time'][1])
207 if data[
'fix_valid']:
208 current_fix.status.status = NavSatStatus.STATUS_FIX
210 current_fix.status.status = NavSatStatus.STATUS_NO_FIX
212 current_fix.status.service = NavSatStatus.SERVICE_GPS
214 latitude = data[
'latitude']
215 if data[
'latitude_direction'] ==
'S':
217 current_fix.latitude = latitude
219 longitude = data[
'longitude']
220 if data[
'longitude_direction'] ==
'W':
221 longitude = -longitude
222 current_fix.longitude = longitude
224 current_fix.altitude = float(
'NaN')
225 current_fix.position_covariance_type = \
226 NavSatFix.COVARIANCE_TYPE_UNKNOWN
228 self.
fix_pub.publish(current_fix)
230 if not (math.isnan(data[
'utc_time'][0])
or self.
use_GNSS_time):
231 current_time_ref.time_ref = rospy.Time(
232 data[
'utc_time'][0], data[
'utc_time'][1])
237 if data[
'fix_valid']:
238 current_vel = TwistStamped()
239 current_vel.header.stamp = current_time
240 current_vel.header.frame_id = frame_id
241 current_vel.twist.linear.x = data[
'speed'] * \
242 math.sin(data[
'true_course'])
243 current_vel.twist.linear.y = data[
'speed'] * \
244 math.cos(data[
'true_course'])
245 self.
vel_pub.publish(current_vel)
251 """Get the TF frame_id. 253 Queries rosparam for the ~frame_id param. If a tf_prefix param is set, 254 the frame_id is prefixed with the prefix. 257 str: The fully-qualified TF frame ID. 259 frame_id = rospy.get_param(
'~frame_id',
'gps')
260 if frame_id[0] !=
"/":
263 prefix_param = rospy.search_param(
'tf_prefix')
265 prefix = rospy.get_param(prefix_param)
267 prefix =
"/%s" % prefix
268 return "%s/%s" % (prefix, frame_id)
def add_sentence(self, nmea_string, frame_id, timestamp=None)
def parse_nmea_sentence(nmea_sentence)
def check_nmea_checksum(nmea_sentence)