Sguaba: Hard-to-misuse rigid body transforms for engineers

Sguaba: hard-to-misuse rigid body transforms for engineers with other things to worry about than linear algebra
4 min read·May 23, 2025--
There are a wide variety of ways to describe the locations of objects in space. To name a few, there is: WGS84, the well-known Earth-bound system of latitude and longitude; ECEF (“Earth-centered, Earth-fixed”), a Cartesian coordinate system fixed to Earth’s axes; NED (“North, East, Down”), a coordinate system that describes where in compass directions a given object is relative to an observer; or FRD (“Front, Right, Down”), which describes where a given object is relative to the facing direction of an observer. And to make matters worse, points in all of these systems except WGS84 can be expressed in Cartesian coordinates (ie, X, Y, and Z), spherical coordinates (ie, angle to pole, angle on XY plane, and radius), or horizontal coordinates (ie, azimuth, elevation, and distance).
When we write software for systems that have to handle real-world coordinates, the question of what coordinate system those coordinates are in, and how they’re represented, comes up a lot. And getting it wrong in even a single place opens the door to disastrous consequences. To that end, we’ve written Sguaba (named after the self-navigating boat of the Celtic deity Manannán mac Lir that is navigated solely by the thoughts of its pilot), which we are now excited to open-source. Sguaba is a Rust crate that strongly types coordinates and vectors with their respective coordinate system, and implements conversions between them. An important design goal is footgun-avoidance — making it very difficult to accidentally mix up different coordinate systems or conventions by leveraging the Rust type system. Additionally, Sguaba comes with extensive documentation explaining the theory and practice involved.
We wrote Sguaba for engineers, not for mathematicians — while it uses quaternions under the hood (via the nalgebra crate), it exposes easy to reason about types like Coordinate
, Vector
, Orientation
, and Pose
(coordinate + orientation). Transformations between these happen through the RigidBodyTransform type, which has type parameters dictating the coordinate system it converts from and to.
To see how this plays out in code, assume that a pilot of a plane observes something out of their window at a given bearing and elevation angles (ie, measured in the plane’s FRD) and wants to know the location of that thing in terms of Latitude, Longitude, and Elevation (ie, WGS84) coordinates. This is what that would look like in Sguaba:
// First, construct a coordinate system for the plane's FRD,// which is what the pilot observes.system!(struct PlaneFrd using FRD);
// Second, construct a coordinate system for compass directions// around the plane, which is what the the pilot's instruments// indicate the plane's orientation in.system!(struct PlaneNed using NED);
// Now, we can name the pilot's observation,// expressed in terms of bearing and elevation// (ie, horizontal coordinates), and a range.let observation = Coordinate::::from_bearing( Bearing::new( Angle::new::(20.), // clockwise from forward Angle::new::(10.), // upwards from straight-ahead ) .expect("elevation is in [-90º, 90º]"), Length::new::(400.), // at this range );// The GPS system also tells us where the plane is located.let wgs84 = Wgs84::new( Angle::new::(12.), Angle::new::(30.), Length::new::(1000.), ).expect("latitude is in [-90º, 90º]");// And the pilot's instruments tell us what direction the// plane is facing, expressed as yaw, pitch, roll.let orientation_in_ned = Orientation::::from_tait_bryan_angles( Angle::new::(8.), // yaw Angle::new::(45.), // pitch Angle::new::(0.), // roll );
We now have all the input information, but every data point uses a different frame of reference. So, we must convert. Conversion requires constructing an appropriate transform, and then applying it to the observations. Note that constructing a transform requires unsafe
(more on that in the documentation) to assert that the transform is appropriate for the coordinate systems involved, but applying it does not.
// To convert between NED and ECEF, we need a transform.// This transform depends on where on the globe you are,// so it takes the WGS84 position.// SAFETY: We're claiming that `wgs84` is the location of// `PlaneNed`'s origin.let ecef_to_plane_ned = unsafe { RigidBodyTransform::ecef_to_ned_at(&wgs84)};
// To convert between FRD (which the observation was made in) and NED,// we need the plane's orientation, which we have from the instruments!// Note that this declaration says that // SAFETY: We're claiming that the given NED orientation makes up the// axes of `PlaneFrd`.let plane_ned_to_plane_frd = unsafe { orientation_in_ned.map_as_zero_in::() };
// These transformations can be chained to go from ECEF to NED.// This chaining would fail to compile if you got the arguments wrong!let ecef_to_plane_frd = ecef_to_plane_ned.and_then(plane_ned_to_plane_frd);
// This transform lets you go from ECEF to FRD, but transforms work both ways.// So, we can apply it in inverse to take our `Coordinate` // and produce a `Coordinate`: let observation_in_ecef = ecef_to_plane_frd.inverse_transform(observation);
// we can then turn that into WGS84 lat/lon/altitude!println!("{:?}", observation_in_ecef.to_wgs84());
You can find this code in examples/pilot-as-engineer.rs
in the source code. There is also a version for the more mathematically minded among you.
We are already using Sguaba and have not found it to have major shortcomings, but with that said there are always opportunities for improvements. For example, Sguaba is currently missing the ENU (“East, North, Up”) coordinate system commonly used as a frame of reference for ground-based vehicles, and the ECI (“Earth-centered inertial”) coordinate system used for near-Earth positioning that is independent of the Earth’s rotation. Similarly, a library like this could always use more in-depth documentation (including graphics) and tests to reduce the chance of misuse or regression. If you’d like to try your hand at contributing, head on over to the project’s GitHub repository. We’d love pull requests and issue reports, or just general experience reports if you end up adopting Sguaba!
So far, we’ve found Sguaba to be immensely useful at Helsing in eliminating errors in coordinate handling, and hope that by making it available to the community, it will help others to do so as well.
Author: jonhoo
What's Your Reaction?






