diff --git a/src/whisker/gps.rs b/src/whisker/gps.rs
index d44f9ad4a8a376ab6e506a2ab5e680f0eaca5d2e..e0ee62fbc13928d92d452f9850290ee6ca0d5e4e 100644
--- a/src/whisker/gps.rs
+++ b/src/whisker/gps.rs
@@ -33,8 +33,14 @@ impl Gps {
         let longitude = longitude.clamp(-179.999, 179.999);
         let longitude = (longitude * ((1u32 << 31) as f64) / 180.0) as i32;
 
-        let heading = heading.clamp(0.0, 359.999);
-        let heading = (heading * 128.0 / 360.0) as u8;
+        let heading = if heading >= 0.0 {
+            heading % 360.0
+        } else {
+            // slightly hacky no-std floor
+            let factor = (-heading / 360.0) as u32 as f64;
+            360.0 * (1.0 + factor) + heading
+        };
+        let heading = round(heading * 128.0 / 180.0) as u8;
 
         Self {
             latitude,
@@ -55,7 +61,7 @@ impl Gps {
     }
 
     pub fn heading(&self) -> f64 {
-        self.heading as f64 / 128.0 * 360.0
+        self.heading as f64 / 128.0 * 180.0
     }
 
     pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> {
@@ -93,3 +99,40 @@ impl Gps {
         })
     }
 }
+
+// no-std and it's not worth bringing in a library for this
+fn round(v: f64) -> u32 {
+    let floor = v as u32;
+    let delta = v - floor as f64;
+    if delta <= 0.5 {
+        floor
+    } else {
+        floor + 1
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn heading_is_correct() {
+        let gps = Gps::new(0.0, 0.0, f16::from_f32(0.0), 0, 359.0, f16::from_f32(0.0));
+        assert_eq!(gps.heading, 255);
+
+        let gps = Gps::new(0.0, 0.0, f16::from_f32(0.0), 0, 0.0, f16::from_f32(0.0));
+        assert_eq!(gps.heading, 0);
+
+        let gps = Gps::new(0.0, 0.0, f16::from_f32(0.0), 0, -20.0, f16::from_f32(0.0));
+        assert_eq!(gps.heading, 242);
+
+        let gps = Gps::new(0.0, 0.0, f16::from_f32(0.0), 0, 719.0, f16::from_f32(0.0));
+        assert_eq!(gps.heading, 255);
+
+        let gps = Gps::new(0.0, 0.0, f16::from_f32(0.0), 0, 180.0, f16::from_f32(0.0));
+        assert_eq!(gps.heading, 128);
+
+        let gps = Gps::new(0.0, 0.0, f16::from_f32(0.0), 0, 540.0, f16::from_f32(0.0));
+        assert_eq!(gps.heading, 128);
+    }
+}
diff --git a/src/whisker/mod.rs b/src/whisker/mod.rs
index d08f9c380e98afba638a6c91e69cf70a3451fe46..6a63f96a4b2d37ff8a097597a64eff9820acd489 100644
--- a/src/whisker/mod.rs
+++ b/src/whisker/mod.rs
@@ -248,7 +248,7 @@ mod tests {
 
         assert_eq!(89.99899999704212, gps.latitude());
         assert_eq!(-179.9989999551326, gps.longitude());
-        assert_eq!(120.9375, gps.heading());
+        assert_eq!(123.75, gps.heading());
     }
 
     #[test]
@@ -258,11 +258,58 @@ mod tests {
             23.45,
             f16::from_f32(45.02),
             24,
-            360.0,
+            359.0,
             f16::from_f32(12.3),
         );
 
-        assert_eq!(357.1875, gps.heading());
+        assert_eq!(358.59375, gps.heading());
+
+        let gps = Gps::new(
+            4.0,
+            23.45,
+            f16::from_f32(45.02),
+            24,
+            957.47,
+            f16::from_f32(12.3),
+        );
+
+        assert_eq!(957.65625 - 360.0 * 2.0, gps.heading());
+    }
+
+    #[test]
+    fn gps_min_heading() {
+        let gps = Gps::new(
+            4.0,
+            23.45,
+            f16::from_f32(45.02),
+            24,
+            0.0,
+            f16::from_f32(12.3),
+        );
+
+        assert_eq!(0.0, gps.heading());
+
+        let gps = Gps::new(
+            4.0,
+            23.45,
+            f16::from_f32(45.02),
+            24,
+            -22.0,
+            f16::from_f32(12.3),
+        );
+
+        assert_eq!(337.5, gps.heading());
+
+        let gps = Gps::new(
+            4.0,
+            23.45,
+            f16::from_f32(45.02),
+            24,
+            -1206.0,
+            f16::from_f32(12.3),
+        );
+
+        assert_eq!(233.4375, gps.heading());
     }
 
     #[test]