diff --git a/sbp.cabal b/sbp.cabal
--- a/sbp.cabal
+++ b/sbp.cabal
@@ -1,5 +1,5 @@
 name:                  sbp
-version:               4.0.3
+version:               4.1.0
 synopsis:              SwiftNav's SBP Library
 homepage:              https://github.com/swift-nav/libsbp
 license:               MIT
diff --git a/src/SwiftNav/SBP/Msg.hs b/src/SwiftNav/SBP/Msg.hs
--- a/src/SwiftNav/SBP/Msg.hs
+++ b/src/SwiftNav/SBP/Msg.hs
@@ -246,6 +246,7 @@
    | SBPMsgUtcTime MsgUtcTime Msg
    | SBPMsgUtcTimeGnss MsgUtcTimeGnss Msg
    | SBPMsgVelBody MsgVelBody Msg
+   | SBPMsgVelCog MsgVelCog Msg
    | SBPMsgVelEcef MsgVelEcef Msg
    | SBPMsgVelEcefCov MsgVelEcefCov Msg
    | SBPMsgVelEcefCovGnss MsgVelEcefCovGnss Msg
@@ -461,6 +462,7 @@
           | _msgSBPType == msgUtcTime = SBPMsgUtcTime (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgUtcTimeGnss = SBPMsgUtcTimeGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgVelBody = SBPMsgVelBody (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgVelCog = SBPMsgVelCog (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgVelEcef = SBPMsgVelEcef (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgVelEcefCov = SBPMsgVelEcefCov (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgVelEcefCovGnss = SBPMsgVelEcefCovGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
@@ -668,6 +670,7 @@
       encoder (SBPMsgUtcTime _ m) = put m
       encoder (SBPMsgUtcTimeGnss _ m) = put m
       encoder (SBPMsgVelBody _ m) = put m
+      encoder (SBPMsgVelCog _ m) = put m
       encoder (SBPMsgVelEcef _ m) = put m
       encoder (SBPMsgVelEcefCov _ m) = put m
       encoder (SBPMsgVelEcefCovGnss _ m) = put m
@@ -879,6 +882,7 @@
         | msgType == msgUtcTime = SBPMsgUtcTime <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgUtcTimeGnss = SBPMsgUtcTimeGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgVelBody = SBPMsgVelBody <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgVelCog = SBPMsgVelCog <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgVelEcef = SBPMsgVelEcef <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgVelEcefCov = SBPMsgVelEcefCov <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgVelEcefCovGnss = SBPMsgVelEcefCovGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
@@ -1091,6 +1095,7 @@
   toJSON (SBPMsgUtcTime n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgUtcTimeGnss n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgVelBody n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgVelCog n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgVelEcef n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgVelEcefCov n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgVelEcefCovGnss n m) = toJSON n <<>> toJSON m
@@ -1297,6 +1302,7 @@
   msg f (SBPMsgUtcTime n m) = SBPMsgUtcTime n <$> f m
   msg f (SBPMsgUtcTimeGnss n m) = SBPMsgUtcTimeGnss n <$> f m
   msg f (SBPMsgVelBody n m) = SBPMsgVelBody n <$> f m
+  msg f (SBPMsgVelCog n m) = SBPMsgVelCog n <$> f m
   msg f (SBPMsgVelEcef n m) = SBPMsgVelEcef n <$> f m
   msg f (SBPMsgVelEcefCov n m) = SBPMsgVelEcefCov n <$> f m
   msg f (SBPMsgVelEcefCovGnss n m) = SBPMsgVelEcefCovGnss n <$> f m
diff --git a/src/SwiftNav/SBP/Navigation.hs b/src/SwiftNav/SBP/Navigation.hs
--- a/src/SwiftNav/SBP/Navigation.hs
+++ b/src/SwiftNav/SBP/Navigation.hs
@@ -1613,6 +1613,64 @@
 $(makeJSON "_msgVelBody_" ''MsgVelBody)
 $(makeLenses ''MsgVelBody)
 
+msgVelCog :: Word16
+msgVelCog = 0x021C
+
+-- | SBP class for message MSG_VEL_COG (0x021C).
+--
+-- This message reports the receiver course over ground (COG) and speed over
+-- ground (SOG) based on the horizontal (N-E) components of the NED velocity
+-- vector. It also includes the vertical velocity in the form of the
+-- D-component of the NED velocity vector. The NED coordinate system is
+-- defined as the local WGS84 tangent  plane centered at the current position.
+-- The full GPS time is given by the  preceding MSG_GPS_TIME with the matching
+-- time-of-week (tow). Note: course over ground represents the receiver's
+-- direction of travel,  but not necessarily the device heading.
+data MsgVelCog = MsgVelCog
+  { _msgVelCog_tow          :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelCog_cog          :: !Word32
+    -- ^ Course over ground relative to local north
+  , _msgVelCog_sog          :: !Word32
+    -- ^ Speed over ground
+  , _msgVelCog_vel_d        :: !Int32
+    -- ^ Velocity Down coordinate
+  , _msgVelCog_cog_accuracy :: !Word32
+    -- ^ Course over ground estimated standard deviation
+  , _msgVelCog_sog_accuracy :: !Word32
+    -- ^ Speed over ground estimated standard deviation
+  , _msgVelCog_vel_d_accuracy :: !Word32
+    -- ^ Vertical velocity estimated standard deviation
+  , _msgVelCog_flags        :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelCog where
+  get = do
+    _msgVelCog_tow <- getWord32le
+    _msgVelCog_cog <- getWord32le
+    _msgVelCog_sog <- getWord32le
+    _msgVelCog_vel_d <- (fromIntegral <$> getWord32le)
+    _msgVelCog_cog_accuracy <- getWord32le
+    _msgVelCog_sog_accuracy <- getWord32le
+    _msgVelCog_vel_d_accuracy <- getWord32le
+    _msgVelCog_flags <- getWord8
+    pure MsgVelCog {..}
+
+  put MsgVelCog {..} = do
+    putWord32le _msgVelCog_tow
+    putWord32le _msgVelCog_cog
+    putWord32le _msgVelCog_sog
+    (putWord32le . fromIntegral) _msgVelCog_vel_d
+    putWord32le _msgVelCog_cog_accuracy
+    putWord32le _msgVelCog_sog_accuracy
+    putWord32le _msgVelCog_vel_d_accuracy
+    putWord8 _msgVelCog_flags
+
+$(makeSBP 'msgVelCog ''MsgVelCog)
+$(makeJSON "_msgVelCog_" ''MsgVelCog)
+$(makeLenses ''MsgVelCog)
+
 msgAgeCorrections :: Word16
 msgAgeCorrections = 0x0210
 
