Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/libnmea_navsat_driver/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ def __init__(self):
self.fix_pub = self.create_publisher(NavSatFix, 'fix', 10)
self.vel_pub = self.create_publisher(TwistStamped, 'vel', 10)
self.heading_pub = self.create_publisher(QuaternionStamped, 'heading', 10)
# Orientation is relative to ENU (REP 103) in contrast with heading which
# is positive clockwise from North
self.orientation_pub = self.create_publisher(QuaternionStamped, 'orientation', 10)

self.time_ref_source = self.declare_parameter('time_ref_source', 'gps').value
self.use_RMC = self.declare_parameter('useRMC', False).value
Expand Down Expand Up @@ -276,6 +279,24 @@ def add_sentence(self, nmea_string, frame_id, timestamp=None):
current_heading.quaternion.z = q[2]
current_heading.quaternion.w = q[3]
self.heading_pub.publish(current_heading)
elif 'PASHR' in parsed_sentence:
data = parsed_sentence['PASHR']
if data['roll'] and data['pitch'] and data['heading']:
current_orientation = QuaternionStamped()
current_orientation.header.stamp = current_time
current_orientation.header.frame_id = frame_id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you note above, this orientation is in a different frame than the heading. Having conflicting orientation conventions published with the same frame will be confusing to clients and could be problematic if both are fed into the same localization filter.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it could cause confusion. Practically, if a device reports orientation using a $PASHR sentence, you shouldn't need to use a separate heading sentence. Maybe is should be documented that a user should use the heading or orientation output, but not both?
In my experience, devices that output a $PASHR sentence are high end integrated system that provide a navigation solution that does not need extre filtering so we don't use a localization filter.

# Convert from NED (Tait-Bryan angles) to ENU (quaternion)
q = quaternion_from_euler(
math.radians(90.0-data['heading']),
-math.radians(data['pitch']),
math.radians(data['roll']),
'rzyx'
)
current_orientation.quaternion.x = q[0]
current_orientation.quaternion.y = q[1]
current_orientation.quaternion.z = q[2]
current_orientation.quaternion.w = q[3]
self.orientation_pub.publish(current_orientation)
else:
return False
return True
Expand Down
11 changes: 10 additions & 1 deletion src/libnmea_navsat_driver/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,21 +199,30 @@ def convert_deg_to_rads(degs):
"VTG": [
("true_course", convert_deg_to_rads, 1),
("speed", convert_knots_to_mps, 5)
],
"PASHR": [
("utc_time", convert_time, 1),
("heading", safe_float, 2),
("roll", safe_float, 4),
("pitch", safe_float, 5),
Comment on lines +204 to +207
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to this source, there are several other fields in the PASHR sentence. Why not include those here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I quickly updated the driver while working on a robot of opportunity. I didn't have the time to add the other fields and make sure they were being paresed properly. Unfortunately, I didn't record raw NMEA data so I could implement and test those extra fields now. I decided to create this PR since partial support for a new sentence is better than none in my opinion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it stands, I don't think I can accept this partial implementation. I understand if you don't have the time or resources to complete this work. Hopefully someone else will be able to.

]
}


def parse_nmea_sentence(nmea_sentence):
# Check for a valid nmea sentence

if not re.match(r'(^\$GP|^\$GN|^\$GL|^\$IN).*\*[0-9A-Fa-f]{2}$', nmea_sentence):
if not re.match(r'(^\$GP|^\$GN|^\$GL|^\$IN|^\$P).*\*[0-9A-Fa-f]{2}$', nmea_sentence):
logger.debug("Regex didn't match, sentence not valid NMEA? Sentence was: %s"
% repr(nmea_sentence))
return False
fields = [field.strip(',') for field in nmea_sentence.split(',')]

# Ignore the $ and talker ID portions (e.g. GP)
sentence_type = fields[0][3:]
# Support proprietary sentences that start with P
if fields[0][0:2] == "$P":
sentence_type = fields[0][1:]

if sentence_type not in parse_maps:
logger.debug("Sentence type %s not in parse map, ignoring."
Expand Down