diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -10,13 +10,15 @@
 
 Available on [Hackage as `sbp`](http://hackage.haskell.org/package/sbp).
 
-The library supports building against Stackage LTS-10. To
-install from Hackage using `stack`:
+The library supports building against Stackage LTS-10. To install from Hackage using `stack`:
 
     $ stack install --resolver lts-10.10 sbp # (LTS-10)
-
-Note that we explicitly specify the resolvers to use, as installing `libsbp` may
-fail to build with more recent resolvers.
+    
+Note that we explicitly specify the resolvers to use, as installing `libsbp` may fail to build with more recent resolvers.    
+    
+Next, install the latest version of sbp available in the [snapshots](https://www.stackage.org/package/sbp/snapshots). For example, if the latest version listed in the snapshots is v2.6.3, run:
+    
+    $ stack install sbp-2.6.3
 
 Building with cabal is possible but not supported and may fail to build.
 
diff --git a/main/JSON2JSON.hs b/main/JSON2JSON.hs
--- a/main/JSON2JSON.hs
+++ b/main/JSON2JSON.hs
@@ -6,7 +6,7 @@
 -- Module:      JSON2JSON
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
 -- License:     LGPL-3
--- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
diff --git a/main/JSON2SBP.hs b/main/JSON2SBP.hs
--- a/main/JSON2SBP.hs
+++ b/main/JSON2SBP.hs
@@ -5,7 +5,7 @@
 -- Module:      JSON2SBP
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
 -- License:     LGPL-3
--- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
diff --git a/main/SBP2JSON.hs b/main/SBP2JSON.hs
--- a/main/SBP2JSON.hs
+++ b/main/SBP2JSON.hs
@@ -5,7 +5,7 @@
 -- Module:      SBP2JSON
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
 -- License:     LGPL-3
--- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
@@ -28,7 +28,9 @@
 encodeLine v = toStrict $ encode v <> "\n"
 
 main :: IO ()
-main =
+main = do
+  hSetBuffering stdin NoBuffering
+  hSetBuffering stdout NoBuffering
   runResourceT $
     sourceHandle stdin
       =$= conduitDecode
diff --git a/main/SBP2NMEA.hs b/main/SBP2NMEA.hs
new file mode 100644
--- /dev/null
+++ b/main/SBP2NMEA.hs
@@ -0,0 +1,137 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+-- |
+-- Module:      SBP2JNMEA
+-- Copyright:   Copyright (C) 2020 Swift Navigation, Inc.
+-- License:     LGPL-3
+-- Contact:     https://support.swiftnav.com
+-- Stability:   experimental
+-- Portability: portable
+--
+-- SBP to NMEA tool - reads SBP binary from stdin and sends NMEA
+-- to stdout.
+
+import           BasicPrelude                      hiding (map)
+import           Control.Lens
+import           Control.Monad.Trans.Resource
+import           Data.Bits
+import qualified Data.ByteString                   as BS
+import qualified Data.ByteString.Char8             as BS8
+import           Data.Conduit
+import           Data.Conduit.Binary
+import           Data.Conduit.List
+import           Data.Conduit.Serialization.Binary
+import           Data.Time
+import           Data.Time.Clock.POSIX
+import           Data.Word
+import           SwiftNav.SBP
+import           System.IO
+import           Text.Printf
+
+-- | NMEA time.
+--
+timestamp :: MsgPosLlh -> ByteString
+timestamp posLlh = BS8.pack $ printf "%s.%03u" times mods
+  where
+    epochs       = 315964800
+    offsets      = 18
+    (tows, mods) = divMod (posLlh ^. msgPosLlh_tow) 1000
+    seconds      = epochs - offsets + tows
+    time         = posixSecondsToUTCTime $ fromIntegral seconds
+    times        = formatTime defaultTimeLocale "%H%M%S" time
+
+-- | NMEA latitude.
+--
+lat :: MsgPosLlh -> ByteString
+lat posLlh = BS8.pack $ printf "%02u%010.7f" i (60 * f)
+  where
+    (i, f) = properFraction (abs $ posLlh ^. msgPosLlh_lat) :: (Word16, Double)
+
+-- | NMEA latitude direction.
+--
+latDir :: MsgPosLlh -> ByteString
+latDir posLlh = bool "S" "N" $ posLlh ^. msgPosLlh_lat > 0
+
+-- | NMEA longitude.
+--
+lon :: MsgPosLlh -> ByteString
+lon posLlh = BS8.pack $ printf "%03u%010.7f" i (60 * f)
+  where
+    (i, f) = properFraction (abs $ posLlh ^. msgPosLlh_lon) :: (Word16, Double)
+
+-- | NMEA longitude direction.
+--
+lonDir :: MsgPosLlh -> ByteString
+lonDir posLlh = bool "W" "E" $ posLlh ^. msgPosLlh_lon > 0
+
+-- | NMEA quality.
+--
+quality :: MsgPosLlh -> ByteString
+quality posLlh = case posLlh ^. msgPosLlh_flags .&. 0x7 of
+  0 -> "0"
+  1 -> "1"
+  2 -> "2"
+  3 -> "5"
+  4 -> "4"
+  _ -> "0"
+
+-- | NMEA number of satellites.
+--
+satellites :: MsgPosLlh -> ByteString
+satellites posLlh = BS8.pack $ printf "%02u" $ posLlh ^. msgPosLlh_n_sats
+
+-- | NMEA altitude.
+--
+height :: MsgPosLlh -> ByteString
+height posLlh = BS8.pack $ printf "%.2f" $ posLlh ^. msgPosLlh_height
+
+-- | NMEA sentence for GGA for GPS.
+--
+sentence :: MsgPosLlh -> ByteString
+sentence posLlh =
+  BS8.intercalate ","
+    [ "GPGGA"
+    , timestamp posLlh
+    , lat posLlh
+    , latDir posLlh
+    , lon posLlh
+    , lonDir posLlh
+    , quality posLlh
+    , satellites posLlh
+    , "0.0"
+    , height posLlh
+    , "M"
+    , "0.0"
+    , "M"
+    , mempty
+    , mempty
+    ]
+
+-- | NMEA checksum.
+--
+checksum :: ByteString -> ByteString
+checksum s = BS8.pack $ printf "%02x" $ BS.foldl' xor 0 s
+
+-- | NMEA GGA for GPS.
+--
+gpgga :: MsgPosLlh -> ByteString
+gpgga posLlh =  "$" <> s <> "*" <> c <> "\r\n"
+ where
+    s = sentence posLlh
+    c = checksum s
+
+-- | Encode a SBPMsg to a line of JSON.
+encodeLine :: SBPMsg -> ByteString
+encodeLine (SBPMsgPosLlh posLlh _msg) = gpgga posLlh
+encodeLine _                          = mempty
+
+main :: IO ()
+main = do
+  hSetBuffering stdin NoBuffering
+  hSetBuffering stdout NoBuffering
+  runResourceT $
+    sourceHandle stdin
+      =$= conduitDecode
+      =$= map encodeLine
+      $$  sinkHandle stdout
diff --git a/main/SBP2YAML.hs b/main/SBP2YAML.hs
--- a/main/SBP2YAML.hs
+++ b/main/SBP2YAML.hs
@@ -4,7 +4,7 @@
 -- Module:      SBP2YAML
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
 -- License:     LGPL-3
--- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
diff --git a/sbp.cabal b/sbp.cabal
--- a/sbp.cabal
+++ b/sbp.cabal
@@ -1,167 +1,173 @@
-cabal-version: >=1.22
-name: sbp
-version: 2.6.3
-license: LGPL-3
-copyright: Copyright (C) 2015-2018 Swift Navigation, Inc.
-maintainer: Swift Navigation <dev@swiftnav.com>
-author: Swift Navigation Inc.
-homepage: https://github.com/swift-nav/libsbp
-synopsis: SwiftNav's SBP Library
+name:                  sbp
+version:               3.4.11
+synopsis:              SwiftNav's SBP Library
+homepage:              https://github.com/swift-nav/libsbp
+license:               MIT
+author:                Swift Navigation Inc.
+maintainer:            Swift Navigation <dev@swiftnav.com>
+copyright:             Copyright (C) 2015-2018 Swift Navigation, Inc.
+category:              Network
+build-type:            Simple
+cabal-version:         >= 1.22
+extra-source-files:    README.md
 description:
-    Haskell bindings for Swift Navigation Binary Protocol (SBP), a fast,
-    simple, and minimal binary protocol for communicating with Swift
-    devices. It is the native binary protocol used by the Piksi GPS
-    receiver to transmit solutions, observations, status and debugging
-    messages, as well as receive messages from the host operating
-    system, such as differential corrections and the almanac.
-category: Network
-build-type: Simple
-extra-source-files:
-    README.md
+  Haskell bindings for Swift Navigation Binary Protocol (SBP), a fast,
+  simple, and minimal binary protocol for communicating with Swift
+  devices. It is the native binary protocol used by the Piksi GPS
+  receiver to transmit solutions, observations, status and debugging
+  messages, as well as receive messages from the host operating
+  system, such as differential corrections and the almanac.
 
 source-repository head
-    type: git
-    location: git@github.com:swift-nav/libsbp.git
+  type:                git
+  location:            git@github.com:swift-nav/libsbp.git
 
 library
-    exposed-modules:
-        SwiftNav.CRC16
-        SwiftNav.SBP
-        SwiftNav.SBP.Acquisition
-        SwiftNav.SBP.Bootload
-        SwiftNav.SBP.ExtEvents
-        SwiftNav.SBP.FileIo
-        SwiftNav.SBP.Flash
-        SwiftNav.SBP.Gnss
-        SwiftNav.SBP.Imu
-        SwiftNav.SBP.Linux
-        SwiftNav.SBP.Logging
-        SwiftNav.SBP.Mag
-        SwiftNav.SBP.Navigation
-        SwiftNav.SBP.Ndb
-        SwiftNav.SBP.Observation
-        SwiftNav.SBP.Orientation
-        SwiftNav.SBP.Piksi
-        SwiftNav.SBP.Sbas
-        SwiftNav.SBP.Settings
-        SwiftNav.SBP.Ssr
-        SwiftNav.SBP.System
-        SwiftNav.SBP.Tracking
-        SwiftNav.SBP.User
-        SwiftNav.SBP.Vehicle
-        SwiftNav.SBP.Types
-    hs-source-dirs: src
-    other-modules:
-        SwiftNav.SBP.Msg
-        SwiftNav.SBP.TH
-    default-language: Haskell2010
-    ghc-options: -Wall
-    build-depends:
-        aeson >=1.2.4.0,
-        array >=0.5.2.0,
-        base >=4.8 && <5,
-        base64-bytestring >=1.0.0.1,
-        basic-prelude >=0.7.0,
-        binary >=0.8.5.1,
-        bytestring >=0.10.8.2,
-        data-binary-ieee754 >=0.4.4,
-        lens >=4.15.4,
-        lens-aeson >=1.0.2,
-        monad-loops >=0.4.3,
-        template-haskell >=2.12.0.0,
-        text >=1.2.2.2
+  exposed-modules:     SwiftNav.CRC16
+                     , SwiftNav.SBP
+                     , SwiftNav.SBP.Acquisition
+                     , SwiftNav.SBP.Bootload
+                     , SwiftNav.SBP.ExtEvents
+                     , SwiftNav.SBP.FileIo
+                     , SwiftNav.SBP.Flash
+                     , SwiftNav.SBP.Gnss
+                     , SwiftNav.SBP.Imu
+                     , SwiftNav.SBP.Linux
+                     , SwiftNav.SBP.Logging
+                     , SwiftNav.SBP.Mag
+                     , SwiftNav.SBP.Navigation
+                     , SwiftNav.SBP.Ndb
+                     , SwiftNav.SBP.Observation
+                     , SwiftNav.SBP.Orientation
+                     , SwiftNav.SBP.Piksi
+                     , SwiftNav.SBP.Sbas
+                     , SwiftNav.SBP.Settings
+                     , SwiftNav.SBP.SolutionMeta
+                     , SwiftNav.SBP.Ssr
+                     , SwiftNav.SBP.System
+                     , SwiftNav.SBP.Tracking
+                     , SwiftNav.SBP.User
+                     , SwiftNav.SBP.Vehicle
+                     , SwiftNav.SBP.Types
+  other-modules:       SwiftNav.SBP.Msg
+                     , SwiftNav.SBP.TH
+  default-language:    Haskell2010
+  hs-source-dirs:      src
+  ghc-options:         -Wall
+  build-depends:       aeson
+                     , array
+                     , base >= 4.8 && < 5
+                     , base64-bytestring
+                     , basic-prelude
+                     , binary
+                     , bytestring
+                     , data-binary-ieee754
+                     , lens
+                     , lens-aeson
+                     , monad-loops
+                     , template-haskell
+                     , text
 
 executable sbp2json
-    main-is: SBP2JSON.hs
-    hs-source-dirs: main
-    default-language: Haskell2010
-    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
-    build-depends:
-        aeson >=1.2.4.0,
-        base >=4.10.1.0,
-        basic-prelude >=0.7.0,
-        binary-conduit >=1.2.5,
-        bytestring >=0.10.8.2,
-        conduit >=1.2.13.1,
-        conduit-extra >=1.2.3.2,
-        resourcet >=1.1.11,
-        sbp -any
+  hs-source-dirs:      main
+  main-is:             SBP2JSON.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:       aeson
+                     , base
+                     , basic-prelude
+                     , binary-conduit
+                     , bytestring
+                     , conduit
+                     , conduit-extra
+                     , resourcet
+                     , sbp
+  default-language:    Haskell2010
 
 executable sbp2prettyjson
-    main-is: SBP2PRETTYJSON.hs
-    hs-source-dirs: main
-    default-language: Haskell2010
-    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
-    build-depends:
-        aeson-pretty >=0.8.5,
-        base >=4.10.1.0,
-        basic-prelude >=0.7.0,
-        binary-conduit >=1.2.5,
-        bytestring >=0.10.8.2,
-        cmdargs >=0.10.20,
-        conduit >=1.2.13.1,
-        conduit-extra >=1.2.3.2,
-        resourcet >=1.1.11,
-        sbp -any
+  hs-source-dirs:      main
+  main-is:             SBP2PRETTYJSON.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:       aeson-pretty
+                     , base
+                     , basic-prelude
+                     , binary-conduit
+                     , bytestring
+                     , cmdargs
+                     , conduit
+                     , conduit-extra
+                     , resourcet
+                     , sbp
+  default-language:    Haskell2010
 
 executable json2sbp
-    main-is: JSON2SBP.hs
-    hs-source-dirs: main
-    default-language: Haskell2010
-    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
-    build-depends:
-        aeson >=1.2.4.0,
-        base >=4.10.1.0,
-        basic-prelude >=0.7.0,
-        binary-conduit >=1.2.5,
-        conduit >=1.2.13.1,
-        conduit-extra >=1.2.3.2,
-        resourcet >=1.1.11,
-        sbp -any
+  hs-source-dirs:      main
+  main-is:             JSON2SBP.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:       aeson
+                     , base
+                     , basic-prelude
+                     , binary-conduit
+                     , conduit
+                     , conduit-extra
+                     , resourcet
+                     , sbp
+  default-language:    Haskell2010
 
 executable json2json
-    main-is: JSON2JSON.hs
-    hs-source-dirs: main
-    default-language: Haskell2010
-    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
-    build-depends:
-        aeson >=1.2.4.0,
-        base >=4.10.1.0,
-        basic-prelude >=0.7.0,
-        bytestring >=0.10.8.2,
-        conduit >=1.2.13.1,
-        conduit-extra >=1.2.3.2,
-        resourcet >=1.1.11,
-        sbp -any,
-        time >=1.8.0.2
+  hs-source-dirs:      main
+  main-is:             JSON2JSON.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:       aeson
+                     , base
+                     , basic-prelude
+                     , bytestring
+                     , conduit
+                     , conduit-extra
+                     , resourcet
+                     , sbp
+                     , time
+  default-language:    Haskell2010
 
 executable sbp2yaml
-    main-is: SBP2YAML.hs
-    hs-source-dirs: main
-    default-language: Haskell2010
-    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
-    build-depends:
-        base >=4.10.1.0,
-        basic-prelude >=0.7.0,
-        binary-conduit >=1.2.5,
-        bytestring >=0.10.8.2,
-        conduit >=1.2.13.1,
-        conduit-extra >=1.2.3.2,
-        resourcet >=1.1.11,
-        sbp -any,
-        yaml >=0.8.28
+  hs-source-dirs:      main
+  main-is:             SBP2YAML.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:       base
+                     , basic-prelude
+                     , binary-conduit
+                     , bytestring
+                     , conduit
+                     , conduit-extra
+                     , resourcet
+                     , sbp
+                     , yaml
+  default-language:    Haskell2010
 
+executable sbp2nmea
+  hs-source-dirs:      main
+  main-is:             SBP2NMEA.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
+  build-depends:       base
+                     , basic-prelude
+                     , binary-conduit
+                     , bytestring
+                     , conduit
+                     , conduit-extra
+                     , lens
+                     , resourcet
+                     , sbp
+                     , time
+  default-language:    Haskell2010
+
 test-suite test
-    type: exitcode-stdio-1.0
-    main-is: Test.hs
-    hs-source-dirs: test
-    other-modules:
-        Test.SwiftNav.CRC16
-    default-language: Haskell2010
-    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
-    build-depends:
-        base >=4.10.1.0,
-        basic-prelude >=0.7.0,
-        sbp -any,
-        tasty >=0.11.3,
-        tasty-hunit >=0.9.2
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Test.hs
+  other-modules:       Test.SwiftNav.CRC16
+  build-depends:       base
+                     , basic-prelude
+                     , sbp
+                     , tasty
+                     , tasty-hunit
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
+  default-language:    Haskell2010
diff --git a/src/SwiftNav/CRC16.hs b/src/SwiftNav/CRC16.hs
--- a/src/SwiftNav/CRC16.hs
+++ b/src/SwiftNav/CRC16.hs
@@ -4,7 +4,7 @@
 -- Module:      SwiftNav.SBP
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
 -- License:     LGPL-3
--- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
diff --git a/src/SwiftNav/SBP.hs b/src/SwiftNav/SBP.hs
--- a/src/SwiftNav/SBP.hs
+++ b/src/SwiftNav/SBP.hs
@@ -1,8 +1,8 @@
 -- |
 -- Module:      SwiftNav.SBP
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
@@ -31,6 +31,7 @@
 import SwiftNav.SBP.Piksi as Exports
 import SwiftNav.SBP.Sbas as Exports
 import SwiftNav.SBP.Settings as Exports
+import SwiftNav.SBP.SolutionMeta as Exports
 import SwiftNav.SBP.Ssr as Exports
 import SwiftNav.SBP.System as Exports
 import SwiftNav.SBP.Tracking as Exports
diff --git a/src/SwiftNav/SBP/Acquisition.hs b/src/SwiftNav/SBP/Acquisition.hs
--- a/src/SwiftNav/SBP/Acquisition.hs
+++ b/src/SwiftNav/SBP/Acquisition.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Acquisition
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Satellite acquisition messages from the device.
+-- \< Satellite acquisition messages from the device. \>
 
 module SwiftNav.SBP.Acquisition
   ( module SwiftNav.SBP.Acquisition
@@ -184,7 +184,7 @@
 
 -- | AcqSvProfile.
 --
--- Profile for a specific SV for debugging purposes The message describes SV
+-- Profile for a specific SV for debugging purposes. The message describes SV
 -- profile during acquisition time. The message is used to debug and measure
 -- the performance.
 data AcqSvProfile = AcqSvProfile
@@ -224,9 +224,9 @@
     _acqSvProfile_bin_width <- getWord16le
     _acqSvProfile_timestamp <- getWord32le
     _acqSvProfile_time_spent <- getWord32le
-    _acqSvProfile_cf_min <- fromIntegral <$> getWord32le
-    _acqSvProfile_cf_max <- fromIntegral <$> getWord32le
-    _acqSvProfile_cf <- fromIntegral <$> getWord32le
+    _acqSvProfile_cf_min <- (fromIntegral <$> getWord32le)
+    _acqSvProfile_cf_max <- (fromIntegral <$> getWord32le)
+    _acqSvProfile_cf <- (fromIntegral <$> getWord32le)
     _acqSvProfile_cp <- getWord32le
     pure AcqSvProfile {..}
 
@@ -287,9 +287,9 @@
     _acqSvProfileDep_bin_width <- getWord16le
     _acqSvProfileDep_timestamp <- getWord32le
     _acqSvProfileDep_time_spent <- getWord32le
-    _acqSvProfileDep_cf_min <- fromIntegral <$> getWord32le
-    _acqSvProfileDep_cf_max <- fromIntegral <$> getWord32le
-    _acqSvProfileDep_cf <- fromIntegral <$> getWord32le
+    _acqSvProfileDep_cf_min <- (fromIntegral <$> getWord32le)
+    _acqSvProfileDep_cf_max <- (fromIntegral <$> getWord32le)
+    _acqSvProfileDep_cf <- (fromIntegral <$> getWord32le)
     _acqSvProfileDep_cp <- getWord32le
     pure AcqSvProfileDep {..}
 
diff --git a/src/SwiftNav/SBP/Bootload.hs b/src/SwiftNav/SBP/Bootload.hs
--- a/src/SwiftNav/SBP/Bootload.hs
+++ b/src/SwiftNav/SBP/Bootload.hs
@@ -6,14 +6,16 @@
 -- |
 -- Module:      SwiftNav.SBP.Bootload
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages for the bootloading configuration of a Piksi 2.3.1.  This message
--- group does not apply to Piksi Multi.  Note that some of these messages share
--- the same message type ID for both the host request and the device response.
+-- \< Messages for the bootloading configuration of a Piksi 2.3.1.  This message
+-- group does not apply to Piksi Multi.
+--
+-- Note that some of these messages share the same message type ID for both
+-- the host request and the device response. \>
 
 module SwiftNav.SBP.Bootload
   ( module SwiftNav.SBP.Bootload
@@ -119,8 +121,9 @@
 -- The device message from the host reads a unique device identifier from the
 -- SwiftNAP, an FPGA. The host requests the ID by sending a
 -- MSG_NAP_DEVICE_DNA_REQ message. The device responds with a
--- MSG_NAP_DEVICE_DNA_RESP message with the device ID in the payload. Note that
--- this ID is tied to the FPGA, and not related to the Piksi's serial number.
+-- MSG_NAP_DEVICE_DNA_RESP message with the device ID in the payload. Note
+-- that this ID is tied to the FPGA, and not related to the Piksi's serial
+-- number.
 data MsgNapDeviceDnaReq = MsgNapDeviceDnaReq
   deriving ( Show, Read, Eq )
 
diff --git a/src/SwiftNav/SBP/ExtEvents.hs b/src/SwiftNav/SBP/ExtEvents.hs
--- a/src/SwiftNav/SBP/ExtEvents.hs
+++ b/src/SwiftNav/SBP/ExtEvents.hs
@@ -6,13 +6,13 @@
 -- |
 -- Module:      SwiftNav.SBP.ExtEvents
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages reporting accurately-timestamped external events, e.g. camera
--- shutter time.
+-- \< Messages reporting accurately-timestamped external events, e.g. camera
+-- shutter time. \>
 
 module SwiftNav.SBP.ExtEvents
   ( module SwiftNav.SBP.ExtEvents
@@ -61,7 +61,7 @@
   get = do
     _msgExtEvent_wn <- getWord16le
     _msgExtEvent_tow <- getWord32le
-    _msgExtEvent_ns_residual <- fromIntegral <$> getWord32le
+    _msgExtEvent_ns_residual <- (fromIntegral <$> getWord32le)
     _msgExtEvent_flags <- getWord8
     _msgExtEvent_pin <- getWord8
     pure MsgExtEvent {..}
diff --git a/src/SwiftNav/SBP/FileIo.hs b/src/SwiftNav/SBP/FileIo.hs
--- a/src/SwiftNav/SBP/FileIo.hs
+++ b/src/SwiftNav/SBP/FileIo.hs
@@ -6,17 +6,19 @@
 -- |
 -- Module:      SwiftNav.SBP.FileIo
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages for using device's onboard flash filesystem functionality. This
+-- \< Messages for using device's onboard flash filesystem functionality. This
 -- allows data to be stored persistently in the device's program flash with
 -- wear-levelling using a simple filesystem interface. The file system
 -- interface (CFS) defines an abstract API for reading directories and for
--- reading and writing files.  Note that some of these messages share the same
--- message type ID for both the host request and the device response.
+-- reading and writing files.
+--
+-- Note that some of these messages share the same message type ID for both
+-- the host request and the device response. \>
 
 module SwiftNav.SBP.FileIo
   ( module SwiftNav.SBP.FileIo
@@ -48,8 +50,8 @@
 -- The file read message reads a certain length (up to 255 bytes) from a given
 -- offset into a file, and returns the data in a MSG_FILEIO_READ_RESP message
 -- where the message length field indicates how many bytes were succesfully
--- read.The sequence number in the request will be returned in the response. If
--- the message is invalid, a followup MSG_PRINT message will print "Invalid
+-- read.The sequence number in the request will be returned in the response.
+-- If the message is invalid, a followup MSG_PRINT message will print "Invalid
 -- fileio read message". A device will only respond to this message when it is
 -- received from sender ID 0x42.
 data MsgFileioReadReq = MsgFileioReadReq
@@ -121,9 +123,10 @@
 -- first n elements of the file list. Returns a MSG_FILEIO_READ_DIR_RESP
 -- message containing the directory listings as a NULL delimited list. The
 -- listing is chunked over multiple SBP packets. The sequence number in the
--- request will be returned in the response.  If message is invalid, a followup
--- MSG_PRINT message will print "Invalid fileio read message". A device will
--- only respond to this message when it is received from sender ID 0x42.
+-- request will be returned in the response.  If message is invalid, a
+-- followup MSG_PRINT message will print "Invalid fileio read message". A
+-- device will only respond to this message when it is received from sender ID
+-- 0x42.
 data MsgFileioReadDirReq = MsgFileioReadDirReq
   { _msgFileioReadDirReq_sequence :: !Word32
     -- ^ Read sequence number
@@ -155,10 +158,10 @@
 -- | SBP class for message MSG_FILEIO_READ_DIR_RESP (0x00AA).
 --
 -- The read directory message lists the files in a directory on the device's
--- onboard flash file system. Message contains the directory listings as a NULL
--- delimited list. The listing is chunked over multiple SBP packets and the end
--- of the list is identified by an entry containing just the character 0xFF.
--- The sequence number in the response is preserved from the request.
+-- onboard flash file system. Message contains the directory listings as a
+-- NULL delimited list. The listing is chunked over multiple SBP packets and
+-- the end of the list is identified by an packet with no entries. The
+-- sequence number in the response is preserved from the request.
 data MsgFileioReadDirResp = MsgFileioReadDirResp
   { _msgFileioReadDirResp_sequence :: !Word32
     -- ^ Read sequence number
@@ -216,7 +219,7 @@
 -- MSG_FILEIO_WRITE_RESP message to check integrity of the write. The sequence
 -- number in the request will be returned in the response. If message is
 -- invalid, a followup MSG_PRINT message will print "Invalid fileio write
--- message". A device will only  process this message when it is received from
+-- message". A device will only process this message when it is received from
 -- sender ID 0x42.
 data MsgFileioWriteReq = MsgFileioWriteReq
   { _msgFileioWriteReq_sequence :: !Word32
@@ -278,9 +281,10 @@
 
 -- | SBP class for message MSG_FILEIO_CONFIG_REQ (0x1001).
 --
--- Requests advice on the optimal configuration for a FileIO  transfer.  Newer
+-- Requests advice on the optimal configuration for a FileIO transfer.  Newer
 -- version of FileIO can support greater throughput by supporting a large
--- window of FileIO data that can be in-flight during read or write operations.
+-- window of FileIO data that can be in-flight during read or write
+-- operations.
 data MsgFileioConfigReq = MsgFileioConfigReq
   { _msgFileioConfigReq_sequence :: !Word32
     -- ^ Advice sequence number
@@ -305,7 +309,8 @@
 --
 -- The advice on the optimal configuration for a FileIO transfer.  Newer
 -- version of FileIO can support greater throughput by supporting a large
--- window of FileIO data that can be in-flight during read or write operations.
+-- window of FileIO data that can be in-flight during read or write
+-- operations.
 data MsgFileioConfigResp = MsgFileioConfigResp
   { _msgFileioConfigResp_sequence     :: !Word32
     -- ^ Advice sequence number
diff --git a/src/SwiftNav/SBP/Flash.hs b/src/SwiftNav/SBP/Flash.hs
--- a/src/SwiftNav/SBP/Flash.hs
+++ b/src/SwiftNav/SBP/Flash.hs
@@ -6,15 +6,15 @@
 -- |
 -- Module:      SwiftNav.SBP.Flash
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages for reading/writing the device's onboard flash memory. Many of
+-- \< Messages for reading/writing the device's onboard flash memory. Many of
 -- these messages target specific flash memory peripherals used in Swift
 -- Navigation devices: the STM32 flash and the M25Pxx FPGA configuration flash
--- from Piksi 2.3.1.  This module does not apply  to Piksi Multi.
+-- from Piksi 2.3.1.  This module does not apply to Piksi Multi. \>
 
 module SwiftNav.SBP.Flash
   ( module SwiftNav.SBP.Flash
@@ -45,8 +45,8 @@
 --
 -- The flash program message programs a set of addresses of either the STM or
 -- M25 flash. The device replies with either a MSG_FLASH_DONE message
--- containing the return code FLASH_OK (0) on success, or FLASH_INVALID_LEN (2)
--- if the maximum write size is exceeded. Note that the sector-containing
+-- containing the return code FLASH_OK (0) on success, or FLASH_INVALID_LEN
+-- (2) if the maximum write size is exceeded. Note that the sector-containing
 -- addresses must be erased before addresses can be programmed.
 data MsgFlashProgram = MsgFlashProgram
   { _msgFlashProgram_target   :: !Word8
@@ -54,7 +54,8 @@
   , _msgFlashProgram_addr_start :: ![Word8]
     -- ^ Starting address offset to program
   , _msgFlashProgram_addr_len :: !Word8
-    -- ^ Length of set of addresses to program, counting up from starting address
+    -- ^ Length of set of addresses to program, counting up from starting
+    -- address
   , _msgFlashProgram_data     :: ![Word8]
     -- ^ Data to program addresses with, with length N=addr_len
   } deriving ( Show, Read, Eq )
@@ -83,8 +84,8 @@
 -- | SBP class for message MSG_FLASH_DONE (0x00E0).
 --
 -- This message defines success or failure codes for a variety of flash memory
--- requests from the host to the device. Flash read and write messages, such as
--- MSG_FLASH_READ_REQ, or MSG_FLASH_PROGRAM, may return this message on
+-- requests from the host to the device. Flash read and write messages, such
+-- as MSG_FLASH_READ_REQ, or MSG_FLASH_PROGRAM, may return this message on
 -- failure.
 data MsgFlashDone = MsgFlashDone
   { _msgFlashDone_response :: !Word8
@@ -111,9 +112,9 @@
 -- The flash read message reads a set of addresses of either the STM or M25
 -- onboard flash. The device replies with a MSG_FLASH_READ_RESP message
 -- containing either the read data on success or a MSG_FLASH_DONE message
--- containing the return code FLASH_INVALID_LEN (2) if the maximum read size is
--- exceeded or FLASH_INVALID_ADDR (3) if the address is outside of the allowed
--- range.
+-- containing the return code FLASH_INVALID_LEN (2) if the maximum read size
+-- is exceeded or FLASH_INVALID_ADDR (3) if the address is outside of the
+-- allowed range.
 data MsgFlashReadReq = MsgFlashReadReq
   { _msgFlashReadReq_target   :: !Word8
     -- ^ Target flags
@@ -147,9 +148,9 @@
 -- The flash read message reads a set of addresses of either the STM or M25
 -- onboard flash. The device replies with a MSG_FLASH_READ_RESP message
 -- containing either the read data on success or a MSG_FLASH_DONE message
--- containing the return code FLASH_INVALID_LEN (2) if the maximum read size is
--- exceeded or FLASH_INVALID_ADDR (3) if the address is outside of the allowed
--- range.
+-- containing the return code FLASH_INVALID_LEN (2) if the maximum read size
+-- is exceeded or FLASH_INVALID_ADDR (3) if the address is outside of the
+-- allowed range.
 data MsgFlashReadResp = MsgFlashReadResp
   { _msgFlashReadResp_target   :: !Word8
     -- ^ Target flags
@@ -281,7 +282,7 @@
 --
 -- This message reads the device's hardcoded unique ID. The host requests the
 -- ID by sending a MSG_STM_UNIQUE_ID_REQ. The device responds with a
--- MSG_STM_UNIQUE_ID_RESP with the 12-byte unique ID in the payload..
+-- MSG_STM_UNIQUE_ID_RESP with the 12-byte unique ID in the payload.
 data MsgStmUniqueIdResp = MsgStmUniqueIdResp
   { _msgStmUniqueIdResp_stm_id :: ![Word8]
     -- ^ Device unique ID
diff --git a/src/SwiftNav/SBP/Gnss.hs b/src/SwiftNav/SBP/Gnss.hs
--- a/src/SwiftNav/SBP/Gnss.hs
+++ b/src/SwiftNav/SBP/Gnss.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Gnss
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Various structs shared between modules
+-- \< Various structs shared between modules \>
 
 module SwiftNav.SBP.Gnss
   ( module SwiftNav.SBP.Gnss
@@ -37,11 +37,11 @@
 
 -- | GnssSignal.
 --
--- Signal identifier containing constellation, band, and satellite identifier
+-- Signal identifier containing constellation, band, and satellite identifier.
 data GnssSignal = GnssSignal
   { _gnssSignal_sat :: !Word8
     -- ^ Constellation-specific satellite identifier. This field for Glonass can
-    -- either be (100+FCN) where FCN is in [-7,+6] or  the Slot ID in [1,28]
+    -- either be (100+FCN) where FCN is in [-7,+6] or the Slot ID in [1,28].
   , _gnssSignal_code :: !Word8
     -- ^ Signal constellation, band and code
   } deriving ( Show, Read, Eq )
@@ -62,7 +62,7 @@
 -- | SvId.
 --
 -- A (Constellation ID, satellite ID) tuple that uniquely identifies a space
--- vehicle
+-- vehicle.
 data SvId = SvId
   { _svId_satId       :: !Word8
     -- ^ ID of the space vehicle within its constellation
@@ -88,9 +88,10 @@
 -- Deprecated.
 data GnssSignalDep = GnssSignalDep
   { _gnssSignalDep_sat    :: !Word16
-    -- ^ Constellation-specific satellite identifier.  Note: unlike GnssSignal,
-    -- GPS satellites are encoded as (PRN - 1). Other constellations do not
-    -- have this offset.
+    -- ^ Constellation-specific satellite identifier.
+    --
+    -- Note: unlike GnssSignal, GPS satellites are encoded as (PRN - 1). Other
+    -- constellations do not have this offset.
   , _gnssSignalDep_code   :: !Word8
     -- ^ Signal constellation, band and code
   , _gnssSignalDep_reserved :: !Word8
@@ -178,7 +179,7 @@
 instance Binary GpsTime where
   get = do
     _gpsTime_tow <- getWord32le
-    _gpsTime_ns_residual <- fromIntegral <$> getWord32le
+    _gpsTime_ns_residual <- (fromIntegral <$> getWord32le)
     _gpsTime_wn <- getWord16le
     pure GpsTime {..}
 
@@ -204,7 +205,7 @@
 
 instance Binary CarrierPhase where
   get = do
-    _carrierPhase_i <- fromIntegral <$> getWord32le
+    _carrierPhase_i <- (fromIntegral <$> getWord32le)
     _carrierPhase_f <- getWord8
     pure CarrierPhase {..}
 
diff --git a/src/SwiftNav/SBP/Imu.hs b/src/SwiftNav/SBP/Imu.hs
--- a/src/SwiftNav/SBP/Imu.hs
+++ b/src/SwiftNav/SBP/Imu.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Imu
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Inertial Measurement Unit (IMU) messages.
+-- \< Inertial Measurement Unit (IMU) messages. \>
 
 module SwiftNav.SBP.Imu
   ( module SwiftNav.SBP.Imu
@@ -44,13 +44,17 @@
 -- gyroscope readings. The sense of the measurements are to be aligned with
 -- the indications on the device itself. Measurement units, which are specific
 -- to the device hardware and settings, are communicated via the MSG_IMU_AUX
--- message.
+-- message. If using "time since startup" time tags, the receiving end will
+-- expect a `MSG_GNSS_TIME_OFFSET` when a PVT fix becomes available to
+-- synchronise IMU measurements with GNSS. The timestamp must wrap around to
+-- zero when reaching one week (604800 seconds).
+--
+-- The time-tagging mode should not change throughout a run.
 data MsgImuRaw = MsgImuRaw
   { _msgImuRaw_tow :: !Word32
-    -- ^ Milliseconds since start of GPS week. If the high bit is set, the time
-    -- is unknown or invalid.
+    -- ^ Milliseconds since reference epoch and time status.
   , _msgImuRaw_tow_f :: !Word8
-    -- ^ Milliseconds since start of GPS week, fractional part
+    -- ^ Milliseconds since reference epoch, fractional part
   , _msgImuRaw_acc_x :: !Int16
     -- ^ Acceleration in the IMU frame X axis
   , _msgImuRaw_acc_y :: !Int16
@@ -69,12 +73,12 @@
   get = do
     _msgImuRaw_tow <- getWord32le
     _msgImuRaw_tow_f <- getWord8
-    _msgImuRaw_acc_x <- fromIntegral <$> getWord16le
-    _msgImuRaw_acc_y <- fromIntegral <$> getWord16le
-    _msgImuRaw_acc_z <- fromIntegral <$> getWord16le
-    _msgImuRaw_gyr_x <- fromIntegral <$> getWord16le
-    _msgImuRaw_gyr_y <- fromIntegral <$> getWord16le
-    _msgImuRaw_gyr_z <- fromIntegral <$> getWord16le
+    _msgImuRaw_acc_x <- (fromIntegral <$> getWord16le)
+    _msgImuRaw_acc_y <- (fromIntegral <$> getWord16le)
+    _msgImuRaw_acc_z <- (fromIntegral <$> getWord16le)
+    _msgImuRaw_gyr_x <- (fromIntegral <$> getWord16le)
+    _msgImuRaw_gyr_y <- (fromIntegral <$> getWord16le)
+    _msgImuRaw_gyr_z <- (fromIntegral <$> getWord16le)
     pure MsgImuRaw {..}
 
   put MsgImuRaw {..} = do
@@ -111,7 +115,7 @@
 instance Binary MsgImuAux where
   get = do
     _msgImuAux_imu_type <- getWord8
-    _msgImuAux_temp <- fromIntegral <$> getWord16le
+    _msgImuAux_temp <- (fromIntegral <$> getWord16le)
     _msgImuAux_imu_conf <- getWord8
     pure MsgImuAux {..}
 
diff --git a/src/SwiftNav/SBP/Linux.hs b/src/SwiftNav/SBP/Linux.hs
--- a/src/SwiftNav/SBP/Linux.hs
+++ b/src/SwiftNav/SBP/Linux.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Linux
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Linux state monitoring.
+-- \< Linux state monitoring. \>
 
 module SwiftNav.SBP.Linux
   ( module SwiftNav.SBP.Linux
@@ -35,128 +35,128 @@
 {-# ANN module ("HLint: ignore Use newtype instead of data"::String) #-}
 
 
-msgLinuxCpuState :: Word16
-msgLinuxCpuState = 0x7F00
+msgLinuxCpuStateDepA :: Word16
+msgLinuxCpuStateDepA = 0x7F00
 
--- | SBP class for message MSG_LINUX_CPU_STATE (0x7F00).
+-- | SBP class for message MSG_LINUX_CPU_STATE_DEP_A (0x7F00).
 --
--- This message indicates the process state of the top 10 heaviest consumers of
--- CPU on the system.
-data MsgLinuxCpuState = MsgLinuxCpuState
-  { _msgLinuxCpuState_index :: !Word8
+-- This message indicates the process state of the top 10 heaviest consumers
+-- of CPU on the system.
+data MsgLinuxCpuStateDepA = MsgLinuxCpuStateDepA
+  { _msgLinuxCpuStateDepA_index :: !Word8
     -- ^ sequence of this status message, values from 0-9
-  , _msgLinuxCpuState_pid   :: !Word16
+  , _msgLinuxCpuStateDepA_pid   :: !Word16
     -- ^ the PID of the process
-  , _msgLinuxCpuState_pcpu  :: !Word8
+  , _msgLinuxCpuStateDepA_pcpu  :: !Word8
     -- ^ percent of cpu used, expressed as a fraction of 256
-  , _msgLinuxCpuState_tname :: !Text
+  , _msgLinuxCpuStateDepA_tname :: !Text
     -- ^ fixed length string representing the thread name
-  , _msgLinuxCpuState_cmdline :: !Text
+  , _msgLinuxCpuStateDepA_cmdline :: !Text
     -- ^ the command line (as much as it fits in the remaining packet)
   } deriving ( Show, Read, Eq )
 
-instance Binary MsgLinuxCpuState where
+instance Binary MsgLinuxCpuStateDepA where
   get = do
-    _msgLinuxCpuState_index <- getWord8
-    _msgLinuxCpuState_pid <- getWord16le
-    _msgLinuxCpuState_pcpu <- getWord8
-    _msgLinuxCpuState_tname <- decodeUtf8 <$> getByteString 15
-    _msgLinuxCpuState_cmdline <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
-    pure MsgLinuxCpuState {..}
+    _msgLinuxCpuStateDepA_index <- getWord8
+    _msgLinuxCpuStateDepA_pid <- getWord16le
+    _msgLinuxCpuStateDepA_pcpu <- getWord8
+    _msgLinuxCpuStateDepA_tname <- decodeUtf8 <$> getByteString 15
+    _msgLinuxCpuStateDepA_cmdline <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
+    pure MsgLinuxCpuStateDepA {..}
 
-  put MsgLinuxCpuState {..} = do
-    putWord8 _msgLinuxCpuState_index
-    putWord16le _msgLinuxCpuState_pid
-    putWord8 _msgLinuxCpuState_pcpu
-    putByteString $ encodeUtf8 _msgLinuxCpuState_tname
-    putByteString $ encodeUtf8 _msgLinuxCpuState_cmdline
+  put MsgLinuxCpuStateDepA {..} = do
+    putWord8 _msgLinuxCpuStateDepA_index
+    putWord16le _msgLinuxCpuStateDepA_pid
+    putWord8 _msgLinuxCpuStateDepA_pcpu
+    putByteString $ encodeUtf8 _msgLinuxCpuStateDepA_tname
+    putByteString $ encodeUtf8 _msgLinuxCpuStateDepA_cmdline
 
-$(makeSBP 'msgLinuxCpuState ''MsgLinuxCpuState)
-$(makeJSON "_msgLinuxCpuState_" ''MsgLinuxCpuState)
-$(makeLenses ''MsgLinuxCpuState)
+$(makeSBP 'msgLinuxCpuStateDepA ''MsgLinuxCpuStateDepA)
+$(makeJSON "_msgLinuxCpuStateDepA_" ''MsgLinuxCpuStateDepA)
+$(makeLenses ''MsgLinuxCpuStateDepA)
 
-msgLinuxMemState :: Word16
-msgLinuxMemState = 0x7F01
+msgLinuxMemStateDepA :: Word16
+msgLinuxMemStateDepA = 0x7F01
 
--- | SBP class for message MSG_LINUX_MEM_STATE (0x7F01).
+-- | SBP class for message MSG_LINUX_MEM_STATE_DEP_A (0x7F01).
 --
--- This message indicates the process state of the top 10 heaviest consumers of
--- memory on the system.
-data MsgLinuxMemState = MsgLinuxMemState
-  { _msgLinuxMemState_index :: !Word8
+-- This message indicates the process state of the top 10 heaviest consumers
+-- of memory on the system.
+data MsgLinuxMemStateDepA = MsgLinuxMemStateDepA
+  { _msgLinuxMemStateDepA_index :: !Word8
     -- ^ sequence of this status message, values from 0-9
-  , _msgLinuxMemState_pid   :: !Word16
+  , _msgLinuxMemStateDepA_pid   :: !Word16
     -- ^ the PID of the process
-  , _msgLinuxMemState_pmem  :: !Word8
+  , _msgLinuxMemStateDepA_pmem  :: !Word8
     -- ^ percent of memory used, expressed as a fraction of 256
-  , _msgLinuxMemState_tname :: !Text
+  , _msgLinuxMemStateDepA_tname :: !Text
     -- ^ fixed length string representing the thread name
-  , _msgLinuxMemState_cmdline :: !Text
+  , _msgLinuxMemStateDepA_cmdline :: !Text
     -- ^ the command line (as much as it fits in the remaining packet)
   } deriving ( Show, Read, Eq )
 
-instance Binary MsgLinuxMemState where
+instance Binary MsgLinuxMemStateDepA where
   get = do
-    _msgLinuxMemState_index <- getWord8
-    _msgLinuxMemState_pid <- getWord16le
-    _msgLinuxMemState_pmem <- getWord8
-    _msgLinuxMemState_tname <- decodeUtf8 <$> getByteString 15
-    _msgLinuxMemState_cmdline <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
-    pure MsgLinuxMemState {..}
+    _msgLinuxMemStateDepA_index <- getWord8
+    _msgLinuxMemStateDepA_pid <- getWord16le
+    _msgLinuxMemStateDepA_pmem <- getWord8
+    _msgLinuxMemStateDepA_tname <- decodeUtf8 <$> getByteString 15
+    _msgLinuxMemStateDepA_cmdline <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
+    pure MsgLinuxMemStateDepA {..}
 
-  put MsgLinuxMemState {..} = do
-    putWord8 _msgLinuxMemState_index
-    putWord16le _msgLinuxMemState_pid
-    putWord8 _msgLinuxMemState_pmem
-    putByteString $ encodeUtf8 _msgLinuxMemState_tname
-    putByteString $ encodeUtf8 _msgLinuxMemState_cmdline
+  put MsgLinuxMemStateDepA {..} = do
+    putWord8 _msgLinuxMemStateDepA_index
+    putWord16le _msgLinuxMemStateDepA_pid
+    putWord8 _msgLinuxMemStateDepA_pmem
+    putByteString $ encodeUtf8 _msgLinuxMemStateDepA_tname
+    putByteString $ encodeUtf8 _msgLinuxMemStateDepA_cmdline
 
-$(makeSBP 'msgLinuxMemState ''MsgLinuxMemState)
-$(makeJSON "_msgLinuxMemState_" ''MsgLinuxMemState)
-$(makeLenses ''MsgLinuxMemState)
+$(makeSBP 'msgLinuxMemStateDepA ''MsgLinuxMemStateDepA)
+$(makeJSON "_msgLinuxMemStateDepA_" ''MsgLinuxMemStateDepA)
+$(makeLenses ''MsgLinuxMemStateDepA)
 
-msgLinuxSysState :: Word16
-msgLinuxSysState = 0x7F02
+msgLinuxSysStateDepA :: Word16
+msgLinuxSysStateDepA = 0x7F02
 
--- | SBP class for message MSG_LINUX_SYS_STATE (0x7F02).
+-- | SBP class for message MSG_LINUX_SYS_STATE_DEP_A (0x7F02).
 --
 -- This presents a summary of CPU and memory utilization.
-data MsgLinuxSysState = MsgLinuxSysState
-  { _msgLinuxSysState_mem_total    :: !Word16
+data MsgLinuxSysStateDepA = MsgLinuxSysStateDepA
+  { _msgLinuxSysStateDepA_mem_total    :: !Word16
     -- ^ total system memory
-  , _msgLinuxSysState_pcpu         :: !Word8
+  , _msgLinuxSysStateDepA_pcpu         :: !Word8
     -- ^ percent of total cpu currently utilized
-  , _msgLinuxSysState_pmem         :: !Word8
+  , _msgLinuxSysStateDepA_pmem         :: !Word8
     -- ^ percent of total memory currently utilized
-  , _msgLinuxSysState_procs_starting :: !Word16
+  , _msgLinuxSysStateDepA_procs_starting :: !Word16
     -- ^ number of processes that started during collection phase
-  , _msgLinuxSysState_procs_stopping :: !Word16
+  , _msgLinuxSysStateDepA_procs_stopping :: !Word16
     -- ^ number of processes that stopped during collection phase
-  , _msgLinuxSysState_pid_count    :: !Word16
+  , _msgLinuxSysStateDepA_pid_count    :: !Word16
     -- ^ the count of processes on the system
   } deriving ( Show, Read, Eq )
 
-instance Binary MsgLinuxSysState where
+instance Binary MsgLinuxSysStateDepA where
   get = do
-    _msgLinuxSysState_mem_total <- getWord16le
-    _msgLinuxSysState_pcpu <- getWord8
-    _msgLinuxSysState_pmem <- getWord8
-    _msgLinuxSysState_procs_starting <- getWord16le
-    _msgLinuxSysState_procs_stopping <- getWord16le
-    _msgLinuxSysState_pid_count <- getWord16le
-    pure MsgLinuxSysState {..}
+    _msgLinuxSysStateDepA_mem_total <- getWord16le
+    _msgLinuxSysStateDepA_pcpu <- getWord8
+    _msgLinuxSysStateDepA_pmem <- getWord8
+    _msgLinuxSysStateDepA_procs_starting <- getWord16le
+    _msgLinuxSysStateDepA_procs_stopping <- getWord16le
+    _msgLinuxSysStateDepA_pid_count <- getWord16le
+    pure MsgLinuxSysStateDepA {..}
 
-  put MsgLinuxSysState {..} = do
-    putWord16le _msgLinuxSysState_mem_total
-    putWord8 _msgLinuxSysState_pcpu
-    putWord8 _msgLinuxSysState_pmem
-    putWord16le _msgLinuxSysState_procs_starting
-    putWord16le _msgLinuxSysState_procs_stopping
-    putWord16le _msgLinuxSysState_pid_count
+  put MsgLinuxSysStateDepA {..} = do
+    putWord16le _msgLinuxSysStateDepA_mem_total
+    putWord8 _msgLinuxSysStateDepA_pcpu
+    putWord8 _msgLinuxSysStateDepA_pmem
+    putWord16le _msgLinuxSysStateDepA_procs_starting
+    putWord16le _msgLinuxSysStateDepA_procs_stopping
+    putWord16le _msgLinuxSysStateDepA_pid_count
 
-$(makeSBP 'msgLinuxSysState ''MsgLinuxSysState)
-$(makeJSON "_msgLinuxSysState_" ''MsgLinuxSysState)
-$(makeLenses ''MsgLinuxSysState)
+$(makeSBP 'msgLinuxSysStateDepA ''MsgLinuxSysStateDepA)
+$(makeJSON "_msgLinuxSysStateDepA_" ''MsgLinuxSysStateDepA)
+$(makeLenses ''MsgLinuxSysStateDepA)
 
 msgLinuxProcessSocketCounts :: Word16
 msgLinuxProcessSocketCounts = 0x7F03
@@ -172,13 +172,13 @@
   , _msgLinuxProcessSocketCounts_socket_count :: !Word16
     -- ^ the number of sockets the process is using
   , _msgLinuxProcessSocketCounts_socket_types :: !Word16
-    -- ^ A bitfield indicating the socket types used:   0x1 (tcp), 0x2 (udp), 0x4
-    -- (unix stream), 0x8 (unix dgram), 0x10 (netlink),   and 0x8000 (unknown)
+    -- ^ A bitfield indicating the socket types used: 0x1 (tcp), 0x2 (udp), 0x4
+    -- (unix stream), 0x8 (unix dgram), 0x10 (netlink), and 0x8000 (unknown)
   , _msgLinuxProcessSocketCounts_socket_states :: !Word16
-    -- ^ A bitfield indicating the socket states:   0x1 (established), 0x2 (syn-
-    -- sent), 0x4 (syn-recv), 0x8 (fin-wait-1),   0x10 (fin-wait-2), 0x20
-    -- (time-wait), 0x40 (closed), 0x80 (close-wait),   0x100 (last-ack), 0x200
-    -- (listen), 0x400 (closing), 0x800 (unconnected),   and 0x8000 (unknown)
+    -- ^ A bitfield indicating the socket states: 0x1 (established), 0x2 (syn-
+    -- sent), 0x4 (syn-recv), 0x8 (fin-wait-1), 0x10 (fin-wait-2), 0x20 (time-
+    -- wait), 0x40 (closed), 0x80 (close-wait), 0x100 (last-ack), 0x200
+    -- (listen), 0x400 (closing), 0x800 (unconnected), and 0x8000 (unknown)
   , _msgLinuxProcessSocketCounts_cmdline     :: !Text
     -- ^ the command line of the process in question
   } deriving ( Show, Read, Eq )
@@ -221,13 +221,13 @@
   , _msgLinuxProcessSocketQueues_send_queued      :: !Word16
     -- ^ the total amount of send data queued for this process
   , _msgLinuxProcessSocketQueues_socket_types     :: !Word16
-    -- ^ A bitfield indicating the socket types used:   0x1 (tcp), 0x2 (udp), 0x4
-    -- (unix stream), 0x8 (unix dgram), 0x10 (netlink),   and 0x8000 (unknown)
+    -- ^ A bitfield indicating the socket types used: 0x1 (tcp), 0x2 (udp), 0x4
+    -- (unix stream), 0x8 (unix dgram), 0x10 (netlink), and 0x8000 (unknown)
   , _msgLinuxProcessSocketQueues_socket_states    :: !Word16
-    -- ^ A bitfield indicating the socket states:   0x1 (established), 0x2 (syn-
-    -- sent), 0x4 (syn-recv), 0x8 (fin-wait-1),   0x10 (fin-wait-2), 0x20
-    -- (time-wait), 0x40 (closed), 0x80 (close-wait),   0x100 (last-ack), 0x200
-    -- (listen), 0x400 (closing), 0x800 (unconnected),   and 0x8000 (unknown)
+    -- ^ A bitfield indicating the socket states: 0x1 (established), 0x2 (syn-
+    -- sent), 0x4 (syn-recv), 0x8 (fin-wait-1), 0x10 (fin-wait-2), 0x20 (time-
+    -- wait), 0x40 (closed), 0x80 (close-wait), 0x100 (last-ack), 0x200
+    -- (listen), 0x400 (closing), 0x800 (unconnected), and 0x8000 (unknown)
   , _msgLinuxProcessSocketQueues_address_of_largest :: !Text
     -- ^ Address of the largest queue, remote or local depending on the
     -- directionality of the connection.
@@ -365,3 +365,151 @@
 $(makeSBP 'msgLinuxProcessFdSummary ''MsgLinuxProcessFdSummary)
 $(makeJSON "_msgLinuxProcessFdSummary_" ''MsgLinuxProcessFdSummary)
 $(makeLenses ''MsgLinuxProcessFdSummary)
+
+msgLinuxCpuState :: Word16
+msgLinuxCpuState = 0x7F08
+
+-- | SBP class for message MSG_LINUX_CPU_STATE (0x7F08).
+--
+-- This message indicates the process state of the top 10 heaviest consumers
+-- of CPU on the system, including a timestamp.
+data MsgLinuxCpuState = MsgLinuxCpuState
+  { _msgLinuxCpuState_index :: !Word8
+    -- ^ sequence of this status message, values from 0-9
+  , _msgLinuxCpuState_pid   :: !Word16
+    -- ^ the PID of the process
+  , _msgLinuxCpuState_pcpu  :: !Word8
+    -- ^ percent of CPU used, expressed as a fraction of 256
+  , _msgLinuxCpuState_time  :: !Word32
+    -- ^ timestamp of message, refer to flags field for how to interpret
+  , _msgLinuxCpuState_flags :: !Word8
+    -- ^ flags
+  , _msgLinuxCpuState_tname :: !Text
+    -- ^ fixed length string representing the thread name
+  , _msgLinuxCpuState_cmdline :: !Text
+    -- ^ the command line (as much as it fits in the remaining packet)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgLinuxCpuState where
+  get = do
+    _msgLinuxCpuState_index <- getWord8
+    _msgLinuxCpuState_pid <- getWord16le
+    _msgLinuxCpuState_pcpu <- getWord8
+    _msgLinuxCpuState_time <- getWord32le
+    _msgLinuxCpuState_flags <- getWord8
+    _msgLinuxCpuState_tname <- decodeUtf8 <$> getByteString 15
+    _msgLinuxCpuState_cmdline <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
+    pure MsgLinuxCpuState {..}
+
+  put MsgLinuxCpuState {..} = do
+    putWord8 _msgLinuxCpuState_index
+    putWord16le _msgLinuxCpuState_pid
+    putWord8 _msgLinuxCpuState_pcpu
+    putWord32le _msgLinuxCpuState_time
+    putWord8 _msgLinuxCpuState_flags
+    putByteString $ encodeUtf8 _msgLinuxCpuState_tname
+    putByteString $ encodeUtf8 _msgLinuxCpuState_cmdline
+
+$(makeSBP 'msgLinuxCpuState ''MsgLinuxCpuState)
+$(makeJSON "_msgLinuxCpuState_" ''MsgLinuxCpuState)
+$(makeLenses ''MsgLinuxCpuState)
+
+msgLinuxMemState :: Word16
+msgLinuxMemState = 0x7F09
+
+-- | SBP class for message MSG_LINUX_MEM_STATE (0x7F09).
+--
+-- This message indicates the process state of the top 10 heaviest consumers
+-- of memory on the system, including a timestamp.
+data MsgLinuxMemState = MsgLinuxMemState
+  { _msgLinuxMemState_index :: !Word8
+    -- ^ sequence of this status message, values from 0-9
+  , _msgLinuxMemState_pid   :: !Word16
+    -- ^ the PID of the process
+  , _msgLinuxMemState_pmem  :: !Word8
+    -- ^ percent of memory used, expressed as a fraction of 256
+  , _msgLinuxMemState_time  :: !Word32
+    -- ^ timestamp of message, refer to flags field for how to interpret
+  , _msgLinuxMemState_flags :: !Word8
+    -- ^ flags
+  , _msgLinuxMemState_tname :: !Text
+    -- ^ fixed length string representing the thread name
+  , _msgLinuxMemState_cmdline :: !Text
+    -- ^ the command line (as much as it fits in the remaining packet)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgLinuxMemState where
+  get = do
+    _msgLinuxMemState_index <- getWord8
+    _msgLinuxMemState_pid <- getWord16le
+    _msgLinuxMemState_pmem <- getWord8
+    _msgLinuxMemState_time <- getWord32le
+    _msgLinuxMemState_flags <- getWord8
+    _msgLinuxMemState_tname <- decodeUtf8 <$> getByteString 15
+    _msgLinuxMemState_cmdline <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
+    pure MsgLinuxMemState {..}
+
+  put MsgLinuxMemState {..} = do
+    putWord8 _msgLinuxMemState_index
+    putWord16le _msgLinuxMemState_pid
+    putWord8 _msgLinuxMemState_pmem
+    putWord32le _msgLinuxMemState_time
+    putWord8 _msgLinuxMemState_flags
+    putByteString $ encodeUtf8 _msgLinuxMemState_tname
+    putByteString $ encodeUtf8 _msgLinuxMemState_cmdline
+
+$(makeSBP 'msgLinuxMemState ''MsgLinuxMemState)
+$(makeJSON "_msgLinuxMemState_" ''MsgLinuxMemState)
+$(makeLenses ''MsgLinuxMemState)
+
+msgLinuxSysState :: Word16
+msgLinuxSysState = 0x7F0A
+
+-- | SBP class for message MSG_LINUX_SYS_STATE (0x7F0A).
+--
+-- This presents a summary of CPU and memory utilization, including a
+-- timestamp.
+data MsgLinuxSysState = MsgLinuxSysState
+  { _msgLinuxSysState_mem_total    :: !Word16
+    -- ^ total system memory, in MiB
+  , _msgLinuxSysState_pcpu         :: !Word8
+    -- ^ percent of CPU used, expressed as a fraction of 256
+  , _msgLinuxSysState_pmem         :: !Word8
+    -- ^ percent of memory used, expressed as a fraction of 256
+  , _msgLinuxSysState_procs_starting :: !Word16
+    -- ^ number of processes that started during collection phase
+  , _msgLinuxSysState_procs_stopping :: !Word16
+    -- ^ number of processes that stopped during collection phase
+  , _msgLinuxSysState_pid_count    :: !Word16
+    -- ^ the count of processes on the system
+  , _msgLinuxSysState_time         :: !Word32
+    -- ^ timestamp of message, refer to flags field for how to interpret
+  , _msgLinuxSysState_flags        :: !Word8
+    -- ^ flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgLinuxSysState where
+  get = do
+    _msgLinuxSysState_mem_total <- getWord16le
+    _msgLinuxSysState_pcpu <- getWord8
+    _msgLinuxSysState_pmem <- getWord8
+    _msgLinuxSysState_procs_starting <- getWord16le
+    _msgLinuxSysState_procs_stopping <- getWord16le
+    _msgLinuxSysState_pid_count <- getWord16le
+    _msgLinuxSysState_time <- getWord32le
+    _msgLinuxSysState_flags <- getWord8
+    pure MsgLinuxSysState {..}
+
+  put MsgLinuxSysState {..} = do
+    putWord16le _msgLinuxSysState_mem_total
+    putWord8 _msgLinuxSysState_pcpu
+    putWord8 _msgLinuxSysState_pmem
+    putWord16le _msgLinuxSysState_procs_starting
+    putWord16le _msgLinuxSysState_procs_stopping
+    putWord16le _msgLinuxSysState_pid_count
+    putWord32le _msgLinuxSysState_time
+    putWord8 _msgLinuxSysState_flags
+
+$(makeSBP 'msgLinuxSysState ''MsgLinuxSysState)
+$(makeJSON "_msgLinuxSysState_" ''MsgLinuxSysState)
+$(makeLenses ''MsgLinuxSysState)
diff --git a/src/SwiftNav/SBP/Logging.hs b/src/SwiftNav/SBP/Logging.hs
--- a/src/SwiftNav/SBP/Logging.hs
+++ b/src/SwiftNav/SBP/Logging.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Logging
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Logging and debugging messages from the device.
+-- \< Logging and debugging messages from the device. \>
 
 module SwiftNav.SBP.Logging
   ( module SwiftNav.SBP.Logging
@@ -71,17 +71,18 @@
 --
 -- This message provides the ability to forward messages over SBP.  This may
 -- take the form of wrapping up SBP messages received by Piksi for logging
--- purposes or wrapping  another protocol with SBP.  The source identifier
--- indicates from what interface a forwarded stream derived. The protocol
--- identifier identifies what the expected protocol the forwarded msg contains.
--- Protocol 0 represents SBP and the remaining values are implementation
--- defined.
+-- purposes or wrapping another protocol with SBP.
+--
+-- The source identifier indicates from what interface a forwarded stream
+-- derived. The protocol identifier identifies what the expected protocol the
+-- forwarded msg contains. Protocol 0 represents SBP and the remaining values
+-- are implementation defined.
 data MsgFwd = MsgFwd
   { _msgFwd_source    :: !Word8
     -- ^ source identifier
   , _msgFwd_protocol  :: !Word8
     -- ^ protocol identifier
-  , _msgFwd_fwd_payload :: !Text
+  , _msgFwd_fwd_payload :: ![Word8]
     -- ^ variable length wrapped binary message
   } deriving ( Show, Read, Eq )
 
@@ -89,13 +90,13 @@
   get = do
     _msgFwd_source <- getWord8
     _msgFwd_protocol <- getWord8
-    _msgFwd_fwd_payload <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
+    _msgFwd_fwd_payload <- whileM (not <$> isEmpty) getWord8
     pure MsgFwd {..}
 
   put MsgFwd {..} = do
     putWord8 _msgFwd_source
     putWord8 _msgFwd_protocol
-    putByteString $ encodeUtf8 _msgFwd_fwd_payload
+    mapM_ putWord8 _msgFwd_fwd_payload
 
 $(makeSBP 'msgFwd ''MsgFwd)
 $(makeJSON "_msgFwd_" ''MsgFwd)
diff --git a/src/SwiftNav/SBP/Mag.hs b/src/SwiftNav/SBP/Mag.hs
--- a/src/SwiftNav/SBP/Mag.hs
+++ b/src/SwiftNav/SBP/Mag.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Mag
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Magnetometer (mag) messages.
+-- \< Magnetometer (mag) messages. \>
 
 module SwiftNav.SBP.Mag
   ( module SwiftNav.SBP.Mag
@@ -59,9 +59,9 @@
   get = do
     _msgMagRaw_tow <- getWord32le
     _msgMagRaw_tow_f <- getWord8
-    _msgMagRaw_mag_x <- fromIntegral <$> getWord16le
-    _msgMagRaw_mag_y <- fromIntegral <$> getWord16le
-    _msgMagRaw_mag_z <- fromIntegral <$> getWord16le
+    _msgMagRaw_mag_x <- (fromIntegral <$> getWord16le)
+    _msgMagRaw_mag_y <- (fromIntegral <$> getWord16le)
+    _msgMagRaw_mag_z <- (fromIntegral <$> getWord16le)
     pure MsgMagRaw {..}
 
   put MsgMagRaw {..} = do
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
@@ -7,8 +7,8 @@
 -- |
 -- Module:      SwiftNav.SBP.Msg
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
@@ -41,6 +41,7 @@
 import SwiftNav.SBP.Piksi
 import SwiftNav.SBP.Sbas
 import SwiftNav.SBP.Settings
+import SwiftNav.SBP.SolutionMeta
 import SwiftNav.SBP.Ssr
 import SwiftNav.SBP.System
 import SwiftNav.SBP.Tracking
@@ -129,26 +130,33 @@
    | SBPMsgFwd MsgFwd Msg
    | SBPMsgGloBiases MsgGloBiases Msg
    | SBPMsgGnssCapb MsgGnssCapb Msg
+   | SBPMsgGnssTimeOffset MsgGnssTimeOffset Msg
    | SBPMsgGpsTime MsgGpsTime Msg
    | SBPMsgGpsTimeDepA MsgGpsTimeDepA Msg
+   | SBPMsgGpsTimeGnss MsgGpsTimeGnss Msg
    | SBPMsgGroupDelay MsgGroupDelay Msg
    | SBPMsgGroupDelayDepA MsgGroupDelayDepA Msg
    | SBPMsgGroupDelayDepB MsgGroupDelayDepB Msg
+   | SBPMsgGroupMeta MsgGroupMeta Msg
    | SBPMsgHeartbeat MsgHeartbeat Msg
    | SBPMsgIarState MsgIarState Msg
    | SBPMsgImuAux MsgImuAux Msg
    | SBPMsgImuRaw MsgImuRaw Msg
    | SBPMsgInitBaseDep MsgInitBaseDep Msg
    | SBPMsgInsStatus MsgInsStatus Msg
+   | SBPMsgInsUpdates MsgInsUpdates Msg
    | SBPMsgIono MsgIono Msg
    | SBPMsgLinuxCpuState MsgLinuxCpuState Msg
+   | SBPMsgLinuxCpuStateDepA MsgLinuxCpuStateDepA Msg
    | SBPMsgLinuxMemState MsgLinuxMemState Msg
+   | SBPMsgLinuxMemStateDepA MsgLinuxMemStateDepA Msg
    | SBPMsgLinuxProcessFdCount MsgLinuxProcessFdCount Msg
    | SBPMsgLinuxProcessFdSummary MsgLinuxProcessFdSummary Msg
    | SBPMsgLinuxProcessSocketCounts MsgLinuxProcessSocketCounts Msg
    | SBPMsgLinuxProcessSocketQueues MsgLinuxProcessSocketQueues Msg
    | SBPMsgLinuxSocketUsage MsgLinuxSocketUsage Msg
    | SBPMsgLinuxSysState MsgLinuxSysState Msg
+   | SBPMsgLinuxSysStateDepA MsgLinuxSysStateDepA Msg
    | SBPMsgLog MsgLog Msg
    | SBPMsgM25FlashWriteStatus MsgM25FlashWriteStatus Msg
    | SBPMsgMagRaw MsgMagRaw Msg
@@ -171,11 +179,19 @@
    | SBPMsgOsr MsgOsr Msg
    | SBPMsgPosEcef MsgPosEcef Msg
    | SBPMsgPosEcefCov MsgPosEcefCov Msg
+   | SBPMsgPosEcefCovGnss MsgPosEcefCovGnss Msg
    | SBPMsgPosEcefDepA MsgPosEcefDepA Msg
+   | SBPMsgPosEcefGnss MsgPosEcefGnss Msg
    | SBPMsgPosLlh MsgPosLlh Msg
+   | SBPMsgPosLlhAcc MsgPosLlhAcc Msg
    | SBPMsgPosLlhCov MsgPosLlhCov Msg
+   | SBPMsgPosLlhCovGnss MsgPosLlhCovGnss Msg
    | SBPMsgPosLlhDepA MsgPosLlhDepA Msg
+   | SBPMsgPosLlhGnss MsgPosLlhGnss Msg
+   | SBPMsgPpsTime MsgPpsTime Msg
    | SBPMsgPrintDep MsgPrintDep Msg
+   | SBPMsgProtectionLevel MsgProtectionLevel Msg
+   | SBPMsgProtectionLevelDepA MsgProtectionLevelDepA Msg
    | SBPMsgReset MsgReset Msg
    | SBPMsgResetDep MsgResetDep Msg
    | SBPMsgResetFilters MsgResetFilters Msg
@@ -191,16 +207,24 @@
    | SBPMsgSettingsSave MsgSettingsSave Msg
    | SBPMsgSettingsWrite MsgSettingsWrite Msg
    | SBPMsgSettingsWriteResp MsgSettingsWriteResp Msg
+   | SBPMsgSolnMeta MsgSolnMeta Msg
+   | SBPMsgSolnMetaDepA MsgSolnMetaDepA Msg
    | SBPMsgSpecan MsgSpecan Msg
    | SBPMsgSpecanDep MsgSpecanDep Msg
    | SBPMsgSsrCodeBiases MsgSsrCodeBiases Msg
-   | SBPMsgSsrGridDefinition MsgSsrGridDefinition Msg
+   | SBPMsgSsrGridDefinitionDepA MsgSsrGridDefinitionDepA Msg
    | SBPMsgSsrGriddedCorrection MsgSsrGriddedCorrection Msg
+   | SBPMsgSsrGriddedCorrectionDepA MsgSsrGriddedCorrectionDepA Msg
+   | SBPMsgSsrGriddedCorrectionNoStdDepA MsgSsrGriddedCorrectionNoStdDepA Msg
    | SBPMsgSsrOrbitClock MsgSsrOrbitClock Msg
    | SBPMsgSsrOrbitClockDepA MsgSsrOrbitClockDepA Msg
    | SBPMsgSsrPhaseBiases MsgSsrPhaseBiases Msg
+   | SBPMsgSsrSatelliteApc MsgSsrSatelliteApc Msg
    | SBPMsgSsrStecCorrection MsgSsrStecCorrection Msg
+   | SBPMsgSsrStecCorrectionDepA MsgSsrStecCorrectionDepA Msg
+   | SBPMsgSsrTileDefinition MsgSsrTileDefinition Msg
    | SBPMsgStartup MsgStartup Msg
+   | SBPMsgStatusReport MsgStatusReport Msg
    | SBPMsgStmFlashLockSector MsgStmFlashLockSector Msg
    | SBPMsgStmFlashUnlockSector MsgStmFlashUnlockSector Msg
    | SBPMsgStmUniqueIdReq MsgStmUniqueIdReq Msg
@@ -220,13 +244,20 @@
    | SBPMsgUartStateDepa MsgUartStateDepa Msg
    | SBPMsgUserData MsgUserData Msg
    | SBPMsgUtcTime MsgUtcTime Msg
+   | SBPMsgUtcTimeGnss MsgUtcTimeGnss Msg
    | SBPMsgVelBody MsgVelBody Msg
+   | SBPMsgVelCog MsgVelCog Msg
    | SBPMsgVelEcef MsgVelEcef Msg
    | SBPMsgVelEcefCov MsgVelEcefCov Msg
+   | SBPMsgVelEcefCovGnss MsgVelEcefCovGnss Msg
    | SBPMsgVelEcefDepA MsgVelEcefDepA Msg
+   | SBPMsgVelEcefGnss MsgVelEcefGnss Msg
    | SBPMsgVelNed MsgVelNed Msg
    | SBPMsgVelNedCov MsgVelNedCov Msg
+   | SBPMsgVelNedCovGnss MsgVelNedCovGnss Msg
    | SBPMsgVelNedDepA MsgVelNedDepA Msg
+   | SBPMsgVelNedGnss MsgVelNedGnss Msg
+   | SBPMsgWheeltick MsgWheeltick Msg
    | SBPMsgBadCrc Msg
    | SBPMsgUnknown Msg
   deriving ( Show, Read, Eq )
@@ -315,26 +346,33 @@
           | _msgSBPType == msgFwd = SBPMsgFwd (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgGloBiases = SBPMsgGloBiases (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgGnssCapb = SBPMsgGnssCapb (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgGnssTimeOffset = SBPMsgGnssTimeOffset (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgGpsTime = SBPMsgGpsTime (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgGpsTimeDepA = SBPMsgGpsTimeDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgGpsTimeGnss = SBPMsgGpsTimeGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgGroupDelay = SBPMsgGroupDelay (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgGroupDelayDepA = SBPMsgGroupDelayDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgGroupDelayDepB = SBPMsgGroupDelayDepB (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgGroupMeta = SBPMsgGroupMeta (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgHeartbeat = SBPMsgHeartbeat (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgIarState = SBPMsgIarState (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgImuAux = SBPMsgImuAux (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgImuRaw = SBPMsgImuRaw (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgInitBaseDep = SBPMsgInitBaseDep (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgInsStatus = SBPMsgInsStatus (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgInsUpdates = SBPMsgInsUpdates (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgIono = SBPMsgIono (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLinuxCpuState = SBPMsgLinuxCpuState (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgLinuxCpuStateDepA = SBPMsgLinuxCpuStateDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLinuxMemState = SBPMsgLinuxMemState (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgLinuxMemStateDepA = SBPMsgLinuxMemStateDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLinuxProcessFdCount = SBPMsgLinuxProcessFdCount (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLinuxProcessFdSummary = SBPMsgLinuxProcessFdSummary (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLinuxProcessSocketCounts = SBPMsgLinuxProcessSocketCounts (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLinuxProcessSocketQueues = SBPMsgLinuxProcessSocketQueues (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLinuxSocketUsage = SBPMsgLinuxSocketUsage (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLinuxSysState = SBPMsgLinuxSysState (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgLinuxSysStateDepA = SBPMsgLinuxSysStateDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgLog = SBPMsgLog (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgMagRaw = SBPMsgMagRaw (decode (fromStrict (unBytes _msgSBPPayload))) m
@@ -357,11 +395,19 @@
           | _msgSBPType == msgOsr = SBPMsgOsr (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgPosEcef = SBPMsgPosEcef (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgPosEcefCov = SBPMsgPosEcefCov (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgPosEcefCovGnss = SBPMsgPosEcefCovGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgPosEcefDepA = SBPMsgPosEcefDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgPosEcefGnss = SBPMsgPosEcefGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgPosLlh = SBPMsgPosLlh (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgPosLlhAcc = SBPMsgPosLlhAcc (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgPosLlhCov = SBPMsgPosLlhCov (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgPosLlhCovGnss = SBPMsgPosLlhCovGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgPosLlhDepA = SBPMsgPosLlhDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgPosLlhGnss = SBPMsgPosLlhGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgPpsTime = SBPMsgPpsTime (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgPrintDep = SBPMsgPrintDep (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgProtectionLevel = SBPMsgProtectionLevel (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgProtectionLevelDepA = SBPMsgProtectionLevelDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgReset = SBPMsgReset (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgResetDep = SBPMsgResetDep (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgResetFilters = SBPMsgResetFilters (decode (fromStrict (unBytes _msgSBPPayload))) m
@@ -377,16 +423,24 @@
           | _msgSBPType == msgSettingsSave = SBPMsgSettingsSave (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSettingsWrite = SBPMsgSettingsWrite (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSettingsWriteResp = SBPMsgSettingsWriteResp (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgSolnMeta = SBPMsgSolnMeta (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgSolnMetaDepA = SBPMsgSolnMetaDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSpecan = SBPMsgSpecan (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSpecanDep = SBPMsgSpecanDep (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSsrCodeBiases = SBPMsgSsrCodeBiases (decode (fromStrict (unBytes _msgSBPPayload))) m
-          | _msgSBPType == msgSsrGridDefinition = SBPMsgSsrGridDefinition (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgSsrGridDefinitionDepA = SBPMsgSsrGridDefinitionDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSsrGriddedCorrection = SBPMsgSsrGriddedCorrection (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgSsrGriddedCorrectionDepA = SBPMsgSsrGriddedCorrectionDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgSsrGriddedCorrectionNoStdDepA = SBPMsgSsrGriddedCorrectionNoStdDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSsrOrbitClock = SBPMsgSsrOrbitClock (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSsrOrbitClockDepA = SBPMsgSsrOrbitClockDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSsrPhaseBiases = SBPMsgSsrPhaseBiases (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgSsrSatelliteApc = SBPMsgSsrSatelliteApc (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgSsrStecCorrection = SBPMsgSsrStecCorrection (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgSsrStecCorrectionDepA = SBPMsgSsrStecCorrectionDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgSsrTileDefinition = SBPMsgSsrTileDefinition (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgStartup = SBPMsgStartup (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgStatusReport = SBPMsgStatusReport (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgStmFlashLockSector = SBPMsgStmFlashLockSector (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq (decode (fromStrict (unBytes _msgSBPPayload))) m
@@ -406,13 +460,20 @@
           | _msgSBPType == msgUartStateDepa = SBPMsgUartStateDepa (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgUserData = SBPMsgUserData (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _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
           | _msgSBPType == msgVelEcefDepA = SBPMsgVelEcefDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgVelEcefGnss = SBPMsgVelEcefGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgVelNed = SBPMsgVelNed (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgVelNedCov = SBPMsgVelNedCov (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgVelNedCovGnss = SBPMsgVelNedCovGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgVelNedDepA = SBPMsgVelNedDepA (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgVelNedGnss = SBPMsgVelNedGnss (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgWheeltick = SBPMsgWheeltick (decode (fromStrict (unBytes _msgSBPPayload))) m
           | otherwise = SBPMsgUnknown m
 
   put sm = do
@@ -493,26 +554,33 @@
       encoder (SBPMsgFwd _ m) = put m
       encoder (SBPMsgGloBiases _ m) = put m
       encoder (SBPMsgGnssCapb _ m) = put m
+      encoder (SBPMsgGnssTimeOffset _ m) = put m
       encoder (SBPMsgGpsTime _ m) = put m
       encoder (SBPMsgGpsTimeDepA _ m) = put m
+      encoder (SBPMsgGpsTimeGnss _ m) = put m
       encoder (SBPMsgGroupDelay _ m) = put m
       encoder (SBPMsgGroupDelayDepA _ m) = put m
       encoder (SBPMsgGroupDelayDepB _ m) = put m
+      encoder (SBPMsgGroupMeta _ m) = put m
       encoder (SBPMsgHeartbeat _ m) = put m
       encoder (SBPMsgIarState _ m) = put m
       encoder (SBPMsgImuAux _ m) = put m
       encoder (SBPMsgImuRaw _ m) = put m
       encoder (SBPMsgInitBaseDep _ m) = put m
       encoder (SBPMsgInsStatus _ m) = put m
+      encoder (SBPMsgInsUpdates _ m) = put m
       encoder (SBPMsgIono _ m) = put m
       encoder (SBPMsgLinuxCpuState _ m) = put m
+      encoder (SBPMsgLinuxCpuStateDepA _ m) = put m
       encoder (SBPMsgLinuxMemState _ m) = put m
+      encoder (SBPMsgLinuxMemStateDepA _ m) = put m
       encoder (SBPMsgLinuxProcessFdCount _ m) = put m
       encoder (SBPMsgLinuxProcessFdSummary _ m) = put m
       encoder (SBPMsgLinuxProcessSocketCounts _ m) = put m
       encoder (SBPMsgLinuxProcessSocketQueues _ m) = put m
       encoder (SBPMsgLinuxSocketUsage _ m) = put m
       encoder (SBPMsgLinuxSysState _ m) = put m
+      encoder (SBPMsgLinuxSysStateDepA _ m) = put m
       encoder (SBPMsgLog _ m) = put m
       encoder (SBPMsgM25FlashWriteStatus _ m) = put m
       encoder (SBPMsgMagRaw _ m) = put m
@@ -535,11 +603,19 @@
       encoder (SBPMsgOsr _ m) = put m
       encoder (SBPMsgPosEcef _ m) = put m
       encoder (SBPMsgPosEcefCov _ m) = put m
+      encoder (SBPMsgPosEcefCovGnss _ m) = put m
       encoder (SBPMsgPosEcefDepA _ m) = put m
+      encoder (SBPMsgPosEcefGnss _ m) = put m
       encoder (SBPMsgPosLlh _ m) = put m
+      encoder (SBPMsgPosLlhAcc _ m) = put m
       encoder (SBPMsgPosLlhCov _ m) = put m
+      encoder (SBPMsgPosLlhCovGnss _ m) = put m
       encoder (SBPMsgPosLlhDepA _ m) = put m
+      encoder (SBPMsgPosLlhGnss _ m) = put m
+      encoder (SBPMsgPpsTime _ m) = put m
       encoder (SBPMsgPrintDep _ m) = put m
+      encoder (SBPMsgProtectionLevel _ m) = put m
+      encoder (SBPMsgProtectionLevelDepA _ m) = put m
       encoder (SBPMsgReset _ m) = put m
       encoder (SBPMsgResetDep _ m) = put m
       encoder (SBPMsgResetFilters _ m) = put m
@@ -555,16 +631,24 @@
       encoder (SBPMsgSettingsSave _ m) = put m
       encoder (SBPMsgSettingsWrite _ m) = put m
       encoder (SBPMsgSettingsWriteResp _ m) = put m
+      encoder (SBPMsgSolnMeta _ m) = put m
+      encoder (SBPMsgSolnMetaDepA _ m) = put m
       encoder (SBPMsgSpecan _ m) = put m
       encoder (SBPMsgSpecanDep _ m) = put m
       encoder (SBPMsgSsrCodeBiases _ m) = put m
-      encoder (SBPMsgSsrGridDefinition _ m) = put m
+      encoder (SBPMsgSsrGridDefinitionDepA _ m) = put m
       encoder (SBPMsgSsrGriddedCorrection _ m) = put m
+      encoder (SBPMsgSsrGriddedCorrectionDepA _ m) = put m
+      encoder (SBPMsgSsrGriddedCorrectionNoStdDepA _ m) = put m
       encoder (SBPMsgSsrOrbitClock _ m) = put m
       encoder (SBPMsgSsrOrbitClockDepA _ m) = put m
       encoder (SBPMsgSsrPhaseBiases _ m) = put m
+      encoder (SBPMsgSsrSatelliteApc _ m) = put m
       encoder (SBPMsgSsrStecCorrection _ m) = put m
+      encoder (SBPMsgSsrStecCorrectionDepA _ m) = put m
+      encoder (SBPMsgSsrTileDefinition _ m) = put m
       encoder (SBPMsgStartup _ m) = put m
+      encoder (SBPMsgStatusReport _ m) = put m
       encoder (SBPMsgStmFlashLockSector _ m) = put m
       encoder (SBPMsgStmFlashUnlockSector _ m) = put m
       encoder (SBPMsgStmUniqueIdReq _ m) = put m
@@ -584,13 +668,20 @@
       encoder (SBPMsgUartStateDepa _ m) = put m
       encoder (SBPMsgUserData _ m) = put m
       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
       encoder (SBPMsgVelEcefDepA _ m) = put m
+      encoder (SBPMsgVelEcefGnss _ m) = put m
       encoder (SBPMsgVelNed _ m) = put m
       encoder (SBPMsgVelNedCov _ m) = put m
+      encoder (SBPMsgVelNedCovGnss _ m) = put m
       encoder (SBPMsgVelNedDepA _ m) = put m
+      encoder (SBPMsgVelNedGnss _ m) = put m
+      encoder (SBPMsgWheeltick _ m) = put m
       encoder (SBPMsgUnknown m) = put m
       encoder (SBPMsgBadCrc m) = put m
 
@@ -675,26 +766,33 @@
         | msgType == msgFwd = SBPMsgFwd <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgGloBiases = SBPMsgGloBiases <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgGnssCapb = SBPMsgGnssCapb <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgGnssTimeOffset = SBPMsgGnssTimeOffset <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgGpsTime = SBPMsgGpsTime <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgGpsTimeDepA = SBPMsgGpsTimeDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgGpsTimeGnss = SBPMsgGpsTimeGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgGroupDelay = SBPMsgGroupDelay <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgGroupDelayDepA = SBPMsgGroupDelayDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgGroupDelayDepB = SBPMsgGroupDelayDepB <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgGroupMeta = SBPMsgGroupMeta <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgHeartbeat = SBPMsgHeartbeat <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgIarState = SBPMsgIarState <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgImuAux = SBPMsgImuAux <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgImuRaw = SBPMsgImuRaw <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgInitBaseDep = SBPMsgInitBaseDep <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgInsStatus = SBPMsgInsStatus <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgInsUpdates = SBPMsgInsUpdates <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgIono = SBPMsgIono <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLinuxCpuState = SBPMsgLinuxCpuState <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgLinuxCpuStateDepA = SBPMsgLinuxCpuStateDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLinuxMemState = SBPMsgLinuxMemState <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgLinuxMemStateDepA = SBPMsgLinuxMemStateDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLinuxProcessFdCount = SBPMsgLinuxProcessFdCount <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLinuxProcessFdSummary = SBPMsgLinuxProcessFdSummary <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLinuxProcessSocketCounts = SBPMsgLinuxProcessSocketCounts <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLinuxProcessSocketQueues = SBPMsgLinuxProcessSocketQueues <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLinuxSocketUsage = SBPMsgLinuxSocketUsage <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLinuxSysState = SBPMsgLinuxSysState <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgLinuxSysStateDepA = SBPMsgLinuxSysStateDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgLog = SBPMsgLog <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgMagRaw = SBPMsgMagRaw <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
@@ -717,11 +815,19 @@
         | msgType == msgOsr = SBPMsgOsr <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgPosEcef = SBPMsgPosEcef <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgPosEcefCov = SBPMsgPosEcefCov <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgPosEcefCovGnss = SBPMsgPosEcefCovGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgPosEcefDepA = SBPMsgPosEcefDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgPosEcefGnss = SBPMsgPosEcefGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgPosLlh = SBPMsgPosLlh <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgPosLlhAcc = SBPMsgPosLlhAcc <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgPosLlhCov = SBPMsgPosLlhCov <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgPosLlhCovGnss = SBPMsgPosLlhCovGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgPosLlhDepA = SBPMsgPosLlhDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgPosLlhGnss = SBPMsgPosLlhGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgPpsTime = SBPMsgPpsTime <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgPrintDep = SBPMsgPrintDep <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgProtectionLevel = SBPMsgProtectionLevel <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgProtectionLevelDepA = SBPMsgProtectionLevelDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgReset = SBPMsgReset <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgResetDep = SBPMsgResetDep <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgResetFilters = SBPMsgResetFilters <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
@@ -737,16 +843,24 @@
         | msgType == msgSettingsSave = SBPMsgSettingsSave <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSettingsWrite = SBPMsgSettingsWrite <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSettingsWriteResp = SBPMsgSettingsWriteResp <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgSolnMeta = SBPMsgSolnMeta <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgSolnMetaDepA = SBPMsgSolnMetaDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSpecan = SBPMsgSpecan <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSpecanDep = SBPMsgSpecanDep <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSsrCodeBiases = SBPMsgSsrCodeBiases <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
-        | msgType == msgSsrGridDefinition = SBPMsgSsrGridDefinition <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgSsrGridDefinitionDepA = SBPMsgSsrGridDefinitionDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSsrGriddedCorrection = SBPMsgSsrGriddedCorrection <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgSsrGriddedCorrectionDepA = SBPMsgSsrGriddedCorrectionDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgSsrGriddedCorrectionNoStdDepA = SBPMsgSsrGriddedCorrectionNoStdDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSsrOrbitClock = SBPMsgSsrOrbitClock <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSsrOrbitClockDepA = SBPMsgSsrOrbitClockDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSsrPhaseBiases = SBPMsgSsrPhaseBiases <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgSsrSatelliteApc = SBPMsgSsrSatelliteApc <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgSsrStecCorrection = SBPMsgSsrStecCorrection <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgSsrStecCorrectionDepA = SBPMsgSsrStecCorrectionDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgSsrTileDefinition = SBPMsgSsrTileDefinition <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgStartup = SBPMsgStartup <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgStatusReport = SBPMsgStatusReport <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgStmFlashLockSector = SBPMsgStmFlashLockSector <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
@@ -766,13 +880,20 @@
         | msgType == msgUartStateDepa = SBPMsgUartStateDepa <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgUserData = SBPMsgUserData <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | 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
         | msgType == msgVelEcefDepA = SBPMsgVelEcefDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgVelEcefGnss = SBPMsgVelEcefGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgVelNed = SBPMsgVelNed <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgVelNedCov = SBPMsgVelNedCov <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgVelNedCovGnss = SBPMsgVelNedCovGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgVelNedDepA = SBPMsgVelNedDepA <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgVelNedGnss = SBPMsgVelNedGnss <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgWheeltick = SBPMsgWheeltick <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | otherwise = SBPMsgUnknown <$> parseJSON obj
   parseJSON _ = mzero
 
@@ -858,26 +979,33 @@
   toJSON (SBPMsgFwd n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgGloBiases n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgGnssCapb n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgGnssTimeOffset n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgGpsTime n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgGpsTimeDepA n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgGpsTimeGnss n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgGroupDelay n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgGroupDelayDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgGroupDelayDepB n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgGroupMeta n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgHeartbeat n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgIarState n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgImuAux n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgImuRaw n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgInitBaseDep _ m) = toJSON m
   toJSON (SBPMsgInsStatus n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgInsUpdates n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgIono n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLinuxCpuState n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgLinuxCpuStateDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLinuxMemState n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgLinuxMemStateDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLinuxProcessFdCount n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLinuxProcessFdSummary n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLinuxProcessSocketCounts n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLinuxProcessSocketQueues n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLinuxSocketUsage n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLinuxSysState n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgLinuxSysStateDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgLog n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgM25FlashWriteStatus n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgMagRaw n m) = toJSON n <<>> toJSON m
@@ -900,11 +1028,19 @@
   toJSON (SBPMsgOsr n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgPosEcef n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgPosEcefCov n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgPosEcefCovGnss n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgPosEcefDepA n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgPosEcefGnss n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgPosLlh n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgPosLlhAcc n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgPosLlhCov n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgPosLlhCovGnss n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgPosLlhDepA n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgPosLlhGnss n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgPpsTime n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgPrintDep n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgProtectionLevel n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgProtectionLevelDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgReset n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgResetDep _ m) = toJSON m
   toJSON (SBPMsgResetFilters n m) = toJSON n <<>> toJSON m
@@ -920,16 +1056,24 @@
   toJSON (SBPMsgSettingsSave _ m) = toJSON m
   toJSON (SBPMsgSettingsWrite n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSettingsWriteResp n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgSolnMeta n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgSolnMetaDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSpecan n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSpecanDep n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSsrCodeBiases n m) = toJSON n <<>> toJSON m
-  toJSON (SBPMsgSsrGridDefinition n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgSsrGridDefinitionDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSsrGriddedCorrection n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgSsrGriddedCorrectionDepA n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgSsrGriddedCorrectionNoStdDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSsrOrbitClock n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSsrOrbitClockDepA n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSsrPhaseBiases n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgSsrSatelliteApc n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgSsrStecCorrection n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgSsrStecCorrectionDepA n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgSsrTileDefinition n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgStartup n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgStatusReport n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgStmFlashLockSector n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgStmFlashUnlockSector n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgStmUniqueIdReq _ m) = toJSON m
@@ -949,13 +1093,20 @@
   toJSON (SBPMsgUartStateDepa n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgUserData n m) = toJSON n <<>> toJSON m
   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
   toJSON (SBPMsgVelEcefDepA n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgVelEcefGnss n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgVelNed n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgVelNedCov n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgVelNedCovGnss n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgVelNedDepA n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgVelNedGnss n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgWheeltick n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgBadCrc m) = toJSON m
   toJSON (SBPMsgUnknown m) = toJSON m
 
@@ -1035,26 +1186,33 @@
   msg f (SBPMsgFwd n m) = SBPMsgFwd n <$> f m
   msg f (SBPMsgGloBiases n m) = SBPMsgGloBiases n <$> f m
   msg f (SBPMsgGnssCapb n m) = SBPMsgGnssCapb n <$> f m
+  msg f (SBPMsgGnssTimeOffset n m) = SBPMsgGnssTimeOffset n <$> f m
   msg f (SBPMsgGpsTime n m) = SBPMsgGpsTime n <$> f m
   msg f (SBPMsgGpsTimeDepA n m) = SBPMsgGpsTimeDepA n <$> f m
+  msg f (SBPMsgGpsTimeGnss n m) = SBPMsgGpsTimeGnss n <$> f m
   msg f (SBPMsgGroupDelay n m) = SBPMsgGroupDelay n <$> f m
   msg f (SBPMsgGroupDelayDepA n m) = SBPMsgGroupDelayDepA n <$> f m
   msg f (SBPMsgGroupDelayDepB n m) = SBPMsgGroupDelayDepB n <$> f m
+  msg f (SBPMsgGroupMeta n m) = SBPMsgGroupMeta n <$> f m
   msg f (SBPMsgHeartbeat n m) = SBPMsgHeartbeat n <$> f m
   msg f (SBPMsgIarState n m) = SBPMsgIarState n <$> f m
   msg f (SBPMsgImuAux n m) = SBPMsgImuAux n <$> f m
   msg f (SBPMsgImuRaw n m) = SBPMsgImuRaw n <$> f m
   msg f (SBPMsgInitBaseDep n m) = SBPMsgInitBaseDep n <$> f m
   msg f (SBPMsgInsStatus n m) = SBPMsgInsStatus n <$> f m
+  msg f (SBPMsgInsUpdates n m) = SBPMsgInsUpdates n <$> f m
   msg f (SBPMsgIono n m) = SBPMsgIono n <$> f m
   msg f (SBPMsgLinuxCpuState n m) = SBPMsgLinuxCpuState n <$> f m
+  msg f (SBPMsgLinuxCpuStateDepA n m) = SBPMsgLinuxCpuStateDepA n <$> f m
   msg f (SBPMsgLinuxMemState n m) = SBPMsgLinuxMemState n <$> f m
+  msg f (SBPMsgLinuxMemStateDepA n m) = SBPMsgLinuxMemStateDepA n <$> f m
   msg f (SBPMsgLinuxProcessFdCount n m) = SBPMsgLinuxProcessFdCount n <$> f m
   msg f (SBPMsgLinuxProcessFdSummary n m) = SBPMsgLinuxProcessFdSummary n <$> f m
   msg f (SBPMsgLinuxProcessSocketCounts n m) = SBPMsgLinuxProcessSocketCounts n <$> f m
   msg f (SBPMsgLinuxProcessSocketQueues n m) = SBPMsgLinuxProcessSocketQueues n <$> f m
   msg f (SBPMsgLinuxSocketUsage n m) = SBPMsgLinuxSocketUsage n <$> f m
   msg f (SBPMsgLinuxSysState n m) = SBPMsgLinuxSysState n <$> f m
+  msg f (SBPMsgLinuxSysStateDepA n m) = SBPMsgLinuxSysStateDepA n <$> f m
   msg f (SBPMsgLog n m) = SBPMsgLog n <$> f m
   msg f (SBPMsgM25FlashWriteStatus n m) = SBPMsgM25FlashWriteStatus n <$> f m
   msg f (SBPMsgMagRaw n m) = SBPMsgMagRaw n <$> f m
@@ -1077,11 +1235,19 @@
   msg f (SBPMsgOsr n m) = SBPMsgOsr n <$> f m
   msg f (SBPMsgPosEcef n m) = SBPMsgPosEcef n <$> f m
   msg f (SBPMsgPosEcefCov n m) = SBPMsgPosEcefCov n <$> f m
+  msg f (SBPMsgPosEcefCovGnss n m) = SBPMsgPosEcefCovGnss n <$> f m
   msg f (SBPMsgPosEcefDepA n m) = SBPMsgPosEcefDepA n <$> f m
+  msg f (SBPMsgPosEcefGnss n m) = SBPMsgPosEcefGnss n <$> f m
   msg f (SBPMsgPosLlh n m) = SBPMsgPosLlh n <$> f m
+  msg f (SBPMsgPosLlhAcc n m) = SBPMsgPosLlhAcc n <$> f m
   msg f (SBPMsgPosLlhCov n m) = SBPMsgPosLlhCov n <$> f m
+  msg f (SBPMsgPosLlhCovGnss n m) = SBPMsgPosLlhCovGnss n <$> f m
   msg f (SBPMsgPosLlhDepA n m) = SBPMsgPosLlhDepA n <$> f m
+  msg f (SBPMsgPosLlhGnss n m) = SBPMsgPosLlhGnss n <$> f m
+  msg f (SBPMsgPpsTime n m) = SBPMsgPpsTime n <$> f m
   msg f (SBPMsgPrintDep n m) = SBPMsgPrintDep n <$> f m
+  msg f (SBPMsgProtectionLevel n m) = SBPMsgProtectionLevel n <$> f m
+  msg f (SBPMsgProtectionLevelDepA n m) = SBPMsgProtectionLevelDepA n <$> f m
   msg f (SBPMsgReset n m) = SBPMsgReset n <$> f m
   msg f (SBPMsgResetDep n m) = SBPMsgResetDep n <$> f m
   msg f (SBPMsgResetFilters n m) = SBPMsgResetFilters n <$> f m
@@ -1097,16 +1263,24 @@
   msg f (SBPMsgSettingsSave n m) = SBPMsgSettingsSave n <$> f m
   msg f (SBPMsgSettingsWrite n m) = SBPMsgSettingsWrite n <$> f m
   msg f (SBPMsgSettingsWriteResp n m) = SBPMsgSettingsWriteResp n <$> f m
+  msg f (SBPMsgSolnMeta n m) = SBPMsgSolnMeta n <$> f m
+  msg f (SBPMsgSolnMetaDepA n m) = SBPMsgSolnMetaDepA n <$> f m
   msg f (SBPMsgSpecan n m) = SBPMsgSpecan n <$> f m
   msg f (SBPMsgSpecanDep n m) = SBPMsgSpecanDep n <$> f m
   msg f (SBPMsgSsrCodeBiases n m) = SBPMsgSsrCodeBiases n <$> f m
-  msg f (SBPMsgSsrGridDefinition n m) = SBPMsgSsrGridDefinition n <$> f m
+  msg f (SBPMsgSsrGridDefinitionDepA n m) = SBPMsgSsrGridDefinitionDepA n <$> f m
   msg f (SBPMsgSsrGriddedCorrection n m) = SBPMsgSsrGriddedCorrection n <$> f m
+  msg f (SBPMsgSsrGriddedCorrectionDepA n m) = SBPMsgSsrGriddedCorrectionDepA n <$> f m
+  msg f (SBPMsgSsrGriddedCorrectionNoStdDepA n m) = SBPMsgSsrGriddedCorrectionNoStdDepA n <$> f m
   msg f (SBPMsgSsrOrbitClock n m) = SBPMsgSsrOrbitClock n <$> f m
   msg f (SBPMsgSsrOrbitClockDepA n m) = SBPMsgSsrOrbitClockDepA n <$> f m
   msg f (SBPMsgSsrPhaseBiases n m) = SBPMsgSsrPhaseBiases n <$> f m
+  msg f (SBPMsgSsrSatelliteApc n m) = SBPMsgSsrSatelliteApc n <$> f m
   msg f (SBPMsgSsrStecCorrection n m) = SBPMsgSsrStecCorrection n <$> f m
+  msg f (SBPMsgSsrStecCorrectionDepA n m) = SBPMsgSsrStecCorrectionDepA n <$> f m
+  msg f (SBPMsgSsrTileDefinition n m) = SBPMsgSsrTileDefinition n <$> f m
   msg f (SBPMsgStartup n m) = SBPMsgStartup n <$> f m
+  msg f (SBPMsgStatusReport n m) = SBPMsgStatusReport n <$> f m
   msg f (SBPMsgStmFlashLockSector n m) = SBPMsgStmFlashLockSector n <$> f m
   msg f (SBPMsgStmFlashUnlockSector n m) = SBPMsgStmFlashUnlockSector n <$> f m
   msg f (SBPMsgStmUniqueIdReq n m) = SBPMsgStmUniqueIdReq n <$> f m
@@ -1126,12 +1300,19 @@
   msg f (SBPMsgUartStateDepa n m) = SBPMsgUartStateDepa n <$> f m
   msg f (SBPMsgUserData n m) = SBPMsgUserData n <$> f m
   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
   msg f (SBPMsgVelEcefDepA n m) = SBPMsgVelEcefDepA n <$> f m
+  msg f (SBPMsgVelEcefGnss n m) = SBPMsgVelEcefGnss n <$> f m
   msg f (SBPMsgVelNed n m) = SBPMsgVelNed n <$> f m
   msg f (SBPMsgVelNedCov n m) = SBPMsgVelNedCov n <$> f m
+  msg f (SBPMsgVelNedCovGnss n m) = SBPMsgVelNedCovGnss n <$> f m
   msg f (SBPMsgVelNedDepA n m) = SBPMsgVelNedDepA n <$> f m
+  msg f (SBPMsgVelNedGnss n m) = SBPMsgVelNedGnss n <$> f m
+  msg f (SBPMsgWheeltick n m) = SBPMsgWheeltick n <$> f m
   msg f (SBPMsgUnknown m) = SBPMsgUnknown <$> f m
   msg f (SBPMsgBadCrc m) = SBPMsgBadCrc <$> 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
@@ -6,1350 +6,2297 @@
 -- |
 -- Module:      SwiftNav.SBP.Navigation
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
--- Stability:   experimental
--- Portability: portable
---
--- Geodetic navigation messages reporting GPS time, position, velocity, and
--- baseline position solutions. For position solutions, these messages define
--- several different position solutions: single-point (SPP), RTK, and pseudo-
--- absolute position solutions.  The SPP is the standalone, absolute GPS
--- position solution using only a single receiver. The RTK solution is the
--- differential GPS solution, which can use either a fixed/integer or floating
--- carrier phase ambiguity. The pseudo-absolute position solution uses a user-
--- provided, well-surveyed base station position (if available) and the RTK
--- solution in tandem.  When the inertial navigation mode indicates that the
--- IMU is used, all messages are reported in the vehicle body frame as defined
--- by device settings.  By default, the vehicle body frame is configured to be
--- coincident with the antenna phase center.  When there is no inertial
--- navigation, the solution will be reported at the phase center of the
--- antenna. There is no inertial navigation capability on Piksi Multi or Duro.
-
-module SwiftNav.SBP.Navigation
-  ( module SwiftNav.SBP.Navigation
-  ) where
-
-import BasicPrelude
-import Control.Lens
-import Control.Monad.Loops
-import Data.Binary
-import Data.Binary.Get
-import Data.Binary.IEEE754
-import Data.Binary.Put
-import Data.ByteString.Lazy    hiding (ByteString)
-import Data.Int
-import Data.Word
-import SwiftNav.SBP.TH
-import SwiftNav.SBP.Types
-
-{-# ANN module ("HLint: ignore Use camelCase"::String) #-}
-{-# ANN module ("HLint: ignore Redundant do"::String) #-}
-{-# ANN module ("HLint: ignore Use newtype instead of data"::String) #-}
-
-
-msgGpsTime :: Word16
-msgGpsTime = 0x0102
-
--- | SBP class for message MSG_GPS_TIME (0x0102).
---
--- This message reports the GPS time, representing the time since the GPS epoch
--- began on midnight January 6, 1980 UTC. GPS time counts the weeks and seconds
--- of the week. The weeks begin at the Saturday/Sunday transition. GPS week 0
--- began at the beginning of the GPS time scale.  Within each week number, the
--- GPS time of the week is between between 0 and 604800 seconds (=60*60*24*7).
--- Note that GPS time does not accumulate leap seconds, and as of now, has a
--- small offset from UTC. In a message stream, this message precedes a set of
--- other navigation messages referenced to the same time (but lacking the ns
--- field) and indicates a more precise time of these messages.
-data MsgGpsTime = MsgGpsTime
-  { _msgGpsTime_wn        :: !Word16
-    -- ^ GPS week number
-  , _msgGpsTime_tow       :: !Word32
-    -- ^ GPS time of week rounded to the nearest millisecond
-  , _msgGpsTime_ns_residual :: !Int32
-    -- ^ Nanosecond residual of millisecond-rounded TOW (ranges from -500000 to
-    -- 500000)
-  , _msgGpsTime_flags     :: !Word8
-    -- ^ Status flags (reserved)
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgGpsTime where
-  get = do
-    _msgGpsTime_wn <- getWord16le
-    _msgGpsTime_tow <- getWord32le
-    _msgGpsTime_ns_residual <- fromIntegral <$> getWord32le
-    _msgGpsTime_flags <- getWord8
-    pure MsgGpsTime {..}
-
-  put MsgGpsTime {..} = do
-    putWord16le _msgGpsTime_wn
-    putWord32le _msgGpsTime_tow
-    (putWord32le . fromIntegral) _msgGpsTime_ns_residual
-    putWord8 _msgGpsTime_flags
-
-$(makeSBP 'msgGpsTime ''MsgGpsTime)
-$(makeJSON "_msgGpsTime_" ''MsgGpsTime)
-$(makeLenses ''MsgGpsTime)
-
-msgUtcTime :: Word16
-msgUtcTime = 0x0103
-
--- | SBP class for message MSG_UTC_TIME (0x0103).
---
--- This message reports the Universal Coordinated Time (UTC).  Note the flags
--- which indicate the source of the UTC offset value and source of the time
--- fix.
-data MsgUtcTime = MsgUtcTime
-  { _msgUtcTime_flags :: !Word8
-    -- ^ Indicates source and time validity
-  , _msgUtcTime_tow   :: !Word32
-    -- ^ GPS time of week rounded to the nearest millisecond
-  , _msgUtcTime_year  :: !Word16
-    -- ^ Year
-  , _msgUtcTime_month :: !Word8
-    -- ^ Month (range 1 .. 12)
-  , _msgUtcTime_day   :: !Word8
-    -- ^ days in the month (range 1-31)
-  , _msgUtcTime_hours :: !Word8
-    -- ^ hours of day (range 0-23)
-  , _msgUtcTime_minutes :: !Word8
-    -- ^ minutes of hour (range 0-59)
-  , _msgUtcTime_seconds :: !Word8
-    -- ^ seconds of minute (range 0-60) rounded down
-  , _msgUtcTime_ns    :: !Word32
-    -- ^ nanoseconds of second (range 0-999999999)
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgUtcTime where
-  get = do
-    _msgUtcTime_flags <- getWord8
-    _msgUtcTime_tow <- getWord32le
-    _msgUtcTime_year <- getWord16le
-    _msgUtcTime_month <- getWord8
-    _msgUtcTime_day <- getWord8
-    _msgUtcTime_hours <- getWord8
-    _msgUtcTime_minutes <- getWord8
-    _msgUtcTime_seconds <- getWord8
-    _msgUtcTime_ns <- getWord32le
-    pure MsgUtcTime {..}
-
-  put MsgUtcTime {..} = do
-    putWord8 _msgUtcTime_flags
-    putWord32le _msgUtcTime_tow
-    putWord16le _msgUtcTime_year
-    putWord8 _msgUtcTime_month
-    putWord8 _msgUtcTime_day
-    putWord8 _msgUtcTime_hours
-    putWord8 _msgUtcTime_minutes
-    putWord8 _msgUtcTime_seconds
-    putWord32le _msgUtcTime_ns
-
-$(makeSBP 'msgUtcTime ''MsgUtcTime)
-$(makeJSON "_msgUtcTime_" ''MsgUtcTime)
-$(makeLenses ''MsgUtcTime)
-
-msgDops :: Word16
-msgDops = 0x0208
-
--- | SBP class for message MSG_DOPS (0x0208).
---
--- This dilution of precision (DOP) message describes the effect of navigation
--- satellite geometry on positional measurement precision.  The flags field
--- indicated whether the DOP reported corresponds to differential or SPP
--- solution.
-data MsgDops = MsgDops
-  { _msgDops_tow :: !Word32
-    -- ^ GPS Time of Week
-  , _msgDops_gdop :: !Word16
-    -- ^ Geometric Dilution of Precision
-  , _msgDops_pdop :: !Word16
-    -- ^ Position Dilution of Precision
-  , _msgDops_tdop :: !Word16
-    -- ^ Time Dilution of Precision
-  , _msgDops_hdop :: !Word16
-    -- ^ Horizontal Dilution of Precision
-  , _msgDops_vdop :: !Word16
-    -- ^ Vertical Dilution of Precision
-  , _msgDops_flags :: !Word8
-    -- ^ Indicates the position solution with which the DOPS message corresponds
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgDops where
-  get = do
-    _msgDops_tow <- getWord32le
-    _msgDops_gdop <- getWord16le
-    _msgDops_pdop <- getWord16le
-    _msgDops_tdop <- getWord16le
-    _msgDops_hdop <- getWord16le
-    _msgDops_vdop <- getWord16le
-    _msgDops_flags <- getWord8
-    pure MsgDops {..}
-
-  put MsgDops {..} = do
-    putWord32le _msgDops_tow
-    putWord16le _msgDops_gdop
-    putWord16le _msgDops_pdop
-    putWord16le _msgDops_tdop
-    putWord16le _msgDops_hdop
-    putWord16le _msgDops_vdop
-    putWord8 _msgDops_flags
-
-$(makeSBP 'msgDops ''MsgDops)
-$(makeJSON "_msgDops_" ''MsgDops)
-$(makeLenses ''MsgDops)
-
-msgPosEcef :: Word16
-msgPosEcef = 0x0209
-
--- | SBP class for message MSG_POS_ECEF (0x0209).
---
--- The position solution message reports absolute Earth Centered Earth Fixed
--- (ECEF) coordinates and the status (single point vs pseudo-absolute RTK) of
--- the position solution. If the rover receiver knows the surveyed position of
--- the base station and has an RTK solution, this reports a pseudo-absolute
--- position solution using the base station position and the rover's RTK
--- baseline vector. The full GPS time is given by the preceding MSG_GPS_TIME
--- with the matching time-of-week (tow).
-data MsgPosEcef = MsgPosEcef
-  { _msgPosEcef_tow    :: !Word32
-    -- ^ GPS Time of Week
-  , _msgPosEcef_x      :: !Double
-    -- ^ ECEF X coordinate
-  , _msgPosEcef_y      :: !Double
-    -- ^ ECEF Y coordinate
-  , _msgPosEcef_z      :: !Double
-    -- ^ ECEF Z coordinate
-  , _msgPosEcef_accuracy :: !Word16
-    -- ^ Position estimated standard deviation
-  , _msgPosEcef_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgPosEcef_flags  :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgPosEcef where
-  get = do
-    _msgPosEcef_tow <- getWord32le
-    _msgPosEcef_x <- getFloat64le
-    _msgPosEcef_y <- getFloat64le
-    _msgPosEcef_z <- getFloat64le
-    _msgPosEcef_accuracy <- getWord16le
-    _msgPosEcef_n_sats <- getWord8
-    _msgPosEcef_flags <- getWord8
-    pure MsgPosEcef {..}
-
-  put MsgPosEcef {..} = do
-    putWord32le _msgPosEcef_tow
-    putFloat64le _msgPosEcef_x
-    putFloat64le _msgPosEcef_y
-    putFloat64le _msgPosEcef_z
-    putWord16le _msgPosEcef_accuracy
-    putWord8 _msgPosEcef_n_sats
-    putWord8 _msgPosEcef_flags
-
-$(makeSBP 'msgPosEcef ''MsgPosEcef)
-$(makeJSON "_msgPosEcef_" ''MsgPosEcef)
-$(makeLenses ''MsgPosEcef)
-
-msgPosEcefCov :: Word16
-msgPosEcefCov = 0x0214
-
--- | SBP class for message MSG_POS_ECEF_COV (0x0214).
---
--- The position solution message reports absolute Earth Centered Earth Fixed
--- (ECEF) coordinates and the status (single point vs pseudo-absolute RTK) of
--- the position solution. The message also reports the upper triangular portion
--- of the 3x3 covariance matrix. If the receiver knows the surveyed position of
--- the base station and has an RTK solution, this reports a pseudo-absolute
--- position solution using the base station position and the rover's RTK
--- baseline vector. The full GPS time is given by the preceding MSG_GPS_TIME
--- with the matching time-of-week (tow).
-data MsgPosEcefCov = MsgPosEcefCov
-  { _msgPosEcefCov_tow   :: !Word32
-    -- ^ GPS Time of Week
-  , _msgPosEcefCov_x     :: !Double
-    -- ^ ECEF X coordinate
-  , _msgPosEcefCov_y     :: !Double
-    -- ^ ECEF Y coordinate
-  , _msgPosEcefCov_z     :: !Double
-    -- ^ ECEF Z coordinate
-  , _msgPosEcefCov_cov_x_x :: !Float
-    -- ^ Estimated variance of x
-  , _msgPosEcefCov_cov_x_y :: !Float
-    -- ^ Estimated covariance of x and y
-  , _msgPosEcefCov_cov_x_z :: !Float
-    -- ^ Estimated covariance of x and z
-  , _msgPosEcefCov_cov_y_y :: !Float
-    -- ^ Estimated variance of y
-  , _msgPosEcefCov_cov_y_z :: !Float
-    -- ^ Estimated covariance of y and z
-  , _msgPosEcefCov_cov_z_z :: !Float
-    -- ^ Estimated variance of z
-  , _msgPosEcefCov_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgPosEcefCov_flags :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgPosEcefCov where
-  get = do
-    _msgPosEcefCov_tow <- getWord32le
-    _msgPosEcefCov_x <- getFloat64le
-    _msgPosEcefCov_y <- getFloat64le
-    _msgPosEcefCov_z <- getFloat64le
-    _msgPosEcefCov_cov_x_x <- getFloat32le
-    _msgPosEcefCov_cov_x_y <- getFloat32le
-    _msgPosEcefCov_cov_x_z <- getFloat32le
-    _msgPosEcefCov_cov_y_y <- getFloat32le
-    _msgPosEcefCov_cov_y_z <- getFloat32le
-    _msgPosEcefCov_cov_z_z <- getFloat32le
-    _msgPosEcefCov_n_sats <- getWord8
-    _msgPosEcefCov_flags <- getWord8
-    pure MsgPosEcefCov {..}
-
-  put MsgPosEcefCov {..} = do
-    putWord32le _msgPosEcefCov_tow
-    putFloat64le _msgPosEcefCov_x
-    putFloat64le _msgPosEcefCov_y
-    putFloat64le _msgPosEcefCov_z
-    putFloat32le _msgPosEcefCov_cov_x_x
-    putFloat32le _msgPosEcefCov_cov_x_y
-    putFloat32le _msgPosEcefCov_cov_x_z
-    putFloat32le _msgPosEcefCov_cov_y_y
-    putFloat32le _msgPosEcefCov_cov_y_z
-    putFloat32le _msgPosEcefCov_cov_z_z
-    putWord8 _msgPosEcefCov_n_sats
-    putWord8 _msgPosEcefCov_flags
-
-$(makeSBP 'msgPosEcefCov ''MsgPosEcefCov)
-$(makeJSON "_msgPosEcefCov_" ''MsgPosEcefCov)
-$(makeLenses ''MsgPosEcefCov)
-
-msgPosLlh :: Word16
-msgPosLlh = 0x020A
-
--- | SBP class for message MSG_POS_LLH (0x020A).
---
--- This position solution message reports the absolute geodetic coordinates and
--- the status (single point vs pseudo-absolute RTK) of the position solution.
--- If the rover receiver knows the surveyed position of the base station and
--- has an RTK solution, this reports a pseudo-absolute position solution using
--- the base station position and the rover's RTK baseline vector. The full GPS
--- time is given by the preceding MSG_GPS_TIME with the matching time-of-week
--- (tow).
-data MsgPosLlh = MsgPosLlh
-  { _msgPosLlh_tow      :: !Word32
-    -- ^ GPS Time of Week
-  , _msgPosLlh_lat      :: !Double
-    -- ^ Latitude
-  , _msgPosLlh_lon      :: !Double
-    -- ^ Longitude
-  , _msgPosLlh_height   :: !Double
-    -- ^ Height above WGS84 ellipsoid
-  , _msgPosLlh_h_accuracy :: !Word16
-    -- ^ Horizontal position estimated standard deviation
-  , _msgPosLlh_v_accuracy :: !Word16
-    -- ^ Vertical position estimated standard deviation
-  , _msgPosLlh_n_sats   :: !Word8
-    -- ^ Number of satellites used in solution.
-  , _msgPosLlh_flags    :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgPosLlh where
-  get = do
-    _msgPosLlh_tow <- getWord32le
-    _msgPosLlh_lat <- getFloat64le
-    _msgPosLlh_lon <- getFloat64le
-    _msgPosLlh_height <- getFloat64le
-    _msgPosLlh_h_accuracy <- getWord16le
-    _msgPosLlh_v_accuracy <- getWord16le
-    _msgPosLlh_n_sats <- getWord8
-    _msgPosLlh_flags <- getWord8
-    pure MsgPosLlh {..}
-
-  put MsgPosLlh {..} = do
-    putWord32le _msgPosLlh_tow
-    putFloat64le _msgPosLlh_lat
-    putFloat64le _msgPosLlh_lon
-    putFloat64le _msgPosLlh_height
-    putWord16le _msgPosLlh_h_accuracy
-    putWord16le _msgPosLlh_v_accuracy
-    putWord8 _msgPosLlh_n_sats
-    putWord8 _msgPosLlh_flags
-
-$(makeSBP 'msgPosLlh ''MsgPosLlh)
-$(makeJSON "_msgPosLlh_" ''MsgPosLlh)
-$(makeLenses ''MsgPosLlh)
-
-msgPosLlhCov :: Word16
-msgPosLlhCov = 0x0211
-
--- | SBP class for message MSG_POS_LLH_COV (0x0211).
---
--- This position solution message reports the absolute geodetic coordinates and
--- the status (single point vs pseudo-absolute RTK) of the position solution as
--- well as the upper triangle of the 3x3 covariance matrix.  The position
--- information and Fix Mode flags should follow the MSG_POS_LLH message.  Since
--- the covariance matrix is computed in the local-level North, East, Down
--- frame, the covariance terms follow with that convention. Thus, covariances
--- are reported against the "downward" measurement and care should be taken
--- with the sign convention.
-data MsgPosLlhCov = MsgPosLlhCov
-  { _msgPosLlhCov_tow   :: !Word32
-    -- ^ GPS Time of Week
-  , _msgPosLlhCov_lat   :: !Double
-    -- ^ Latitude
-  , _msgPosLlhCov_lon   :: !Double
-    -- ^ Longitude
-  , _msgPosLlhCov_height :: !Double
-    -- ^ Height above WGS84 ellipsoid
-  , _msgPosLlhCov_cov_n_n :: !Float
-    -- ^ Estimated variance of northing
-  , _msgPosLlhCov_cov_n_e :: !Float
-    -- ^ Covariance of northing and easting
-  , _msgPosLlhCov_cov_n_d :: !Float
-    -- ^ Covariance of northing and downward measurement
-  , _msgPosLlhCov_cov_e_e :: !Float
-    -- ^ Estimated variance of easting
-  , _msgPosLlhCov_cov_e_d :: !Float
-    -- ^ Covariance of easting and downward measurement
-  , _msgPosLlhCov_cov_d_d :: !Float
-    -- ^ Estimated variance of downward measurement
-  , _msgPosLlhCov_n_sats :: !Word8
-    -- ^ Number of satellites used in solution.
-  , _msgPosLlhCov_flags :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgPosLlhCov where
-  get = do
-    _msgPosLlhCov_tow <- getWord32le
-    _msgPosLlhCov_lat <- getFloat64le
-    _msgPosLlhCov_lon <- getFloat64le
-    _msgPosLlhCov_height <- getFloat64le
-    _msgPosLlhCov_cov_n_n <- getFloat32le
-    _msgPosLlhCov_cov_n_e <- getFloat32le
-    _msgPosLlhCov_cov_n_d <- getFloat32le
-    _msgPosLlhCov_cov_e_e <- getFloat32le
-    _msgPosLlhCov_cov_e_d <- getFloat32le
-    _msgPosLlhCov_cov_d_d <- getFloat32le
-    _msgPosLlhCov_n_sats <- getWord8
-    _msgPosLlhCov_flags <- getWord8
-    pure MsgPosLlhCov {..}
-
-  put MsgPosLlhCov {..} = do
-    putWord32le _msgPosLlhCov_tow
-    putFloat64le _msgPosLlhCov_lat
-    putFloat64le _msgPosLlhCov_lon
-    putFloat64le _msgPosLlhCov_height
-    putFloat32le _msgPosLlhCov_cov_n_n
-    putFloat32le _msgPosLlhCov_cov_n_e
-    putFloat32le _msgPosLlhCov_cov_n_d
-    putFloat32le _msgPosLlhCov_cov_e_e
-    putFloat32le _msgPosLlhCov_cov_e_d
-    putFloat32le _msgPosLlhCov_cov_d_d
-    putWord8 _msgPosLlhCov_n_sats
-    putWord8 _msgPosLlhCov_flags
-
-$(makeSBP 'msgPosLlhCov ''MsgPosLlhCov)
-$(makeJSON "_msgPosLlhCov_" ''MsgPosLlhCov)
-$(makeLenses ''MsgPosLlhCov)
-
-msgBaselineEcef :: Word16
-msgBaselineEcef = 0x020B
-
--- | SBP class for message MSG_BASELINE_ECEF (0x020B).
---
--- This message reports the baseline solution in Earth Centered Earth Fixed
--- (ECEF) coordinates. This baseline is the relative vector distance from the
--- base station to the rover receiver. The full GPS time is given by the
--- preceding MSG_GPS_TIME with the matching time-of-week (tow).
-data MsgBaselineEcef = MsgBaselineEcef
-  { _msgBaselineEcef_tow    :: !Word32
-    -- ^ GPS Time of Week
-  , _msgBaselineEcef_x      :: !Int32
-    -- ^ Baseline ECEF X coordinate
-  , _msgBaselineEcef_y      :: !Int32
-    -- ^ Baseline ECEF Y coordinate
-  , _msgBaselineEcef_z      :: !Int32
-    -- ^ Baseline ECEF Z coordinate
-  , _msgBaselineEcef_accuracy :: !Word16
-    -- ^ Position estimated standard deviation
-  , _msgBaselineEcef_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgBaselineEcef_flags  :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgBaselineEcef where
-  get = do
-    _msgBaselineEcef_tow <- getWord32le
-    _msgBaselineEcef_x <- fromIntegral <$> getWord32le
-    _msgBaselineEcef_y <- fromIntegral <$> getWord32le
-    _msgBaselineEcef_z <- fromIntegral <$> getWord32le
-    _msgBaselineEcef_accuracy <- getWord16le
-    _msgBaselineEcef_n_sats <- getWord8
-    _msgBaselineEcef_flags <- getWord8
-    pure MsgBaselineEcef {..}
-
-  put MsgBaselineEcef {..} = do
-    putWord32le _msgBaselineEcef_tow
-    (putWord32le . fromIntegral) _msgBaselineEcef_x
-    (putWord32le . fromIntegral) _msgBaselineEcef_y
-    (putWord32le . fromIntegral) _msgBaselineEcef_z
-    putWord16le _msgBaselineEcef_accuracy
-    putWord8 _msgBaselineEcef_n_sats
-    putWord8 _msgBaselineEcef_flags
-
-$(makeSBP 'msgBaselineEcef ''MsgBaselineEcef)
-$(makeJSON "_msgBaselineEcef_" ''MsgBaselineEcef)
-$(makeLenses ''MsgBaselineEcef)
-
-msgBaselineNed :: Word16
-msgBaselineNed = 0x020C
-
--- | SBP class for message MSG_BASELINE_NED (0x020C).
---
--- This message reports the baseline solution in North East Down (NED)
--- coordinates. This baseline is the relative vector distance from the base
--- station to the rover receiver, and NED coordinate system is defined at the
--- local WGS84 tangent plane centered at the base station position.  The full
--- GPS time is given by the preceding MSG_GPS_TIME with the matching time-of-
--- week (tow).
-data MsgBaselineNed = MsgBaselineNed
-  { _msgBaselineNed_tow      :: !Word32
-    -- ^ GPS Time of Week
-  , _msgBaselineNed_n        :: !Int32
-    -- ^ Baseline North coordinate
-  , _msgBaselineNed_e        :: !Int32
-    -- ^ Baseline East coordinate
-  , _msgBaselineNed_d        :: !Int32
-    -- ^ Baseline Down coordinate
-  , _msgBaselineNed_h_accuracy :: !Word16
-    -- ^ Horizontal position estimated standard deviation
-  , _msgBaselineNed_v_accuracy :: !Word16
-    -- ^ Vertical position estimated standard deviation
-  , _msgBaselineNed_n_sats   :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgBaselineNed_flags    :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgBaselineNed where
-  get = do
-    _msgBaselineNed_tow <- getWord32le
-    _msgBaselineNed_n <- fromIntegral <$> getWord32le
-    _msgBaselineNed_e <- fromIntegral <$> getWord32le
-    _msgBaselineNed_d <- fromIntegral <$> getWord32le
-    _msgBaselineNed_h_accuracy <- getWord16le
-    _msgBaselineNed_v_accuracy <- getWord16le
-    _msgBaselineNed_n_sats <- getWord8
-    _msgBaselineNed_flags <- getWord8
-    pure MsgBaselineNed {..}
-
-  put MsgBaselineNed {..} = do
-    putWord32le _msgBaselineNed_tow
-    (putWord32le . fromIntegral) _msgBaselineNed_n
-    (putWord32le . fromIntegral) _msgBaselineNed_e
-    (putWord32le . fromIntegral) _msgBaselineNed_d
-    putWord16le _msgBaselineNed_h_accuracy
-    putWord16le _msgBaselineNed_v_accuracy
-    putWord8 _msgBaselineNed_n_sats
-    putWord8 _msgBaselineNed_flags
-
-$(makeSBP 'msgBaselineNed ''MsgBaselineNed)
-$(makeJSON "_msgBaselineNed_" ''MsgBaselineNed)
-$(makeLenses ''MsgBaselineNed)
-
-msgVelEcef :: Word16
-msgVelEcef = 0x020D
-
--- | SBP class for message MSG_VEL_ECEF (0x020D).
---
--- This message reports the velocity in Earth Centered Earth Fixed (ECEF)
--- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
--- the matching time-of-week (tow).
-data MsgVelEcef = MsgVelEcef
-  { _msgVelEcef_tow    :: !Word32
-    -- ^ GPS Time of Week
-  , _msgVelEcef_x      :: !Int32
-    -- ^ Velocity ECEF X coordinate
-  , _msgVelEcef_y      :: !Int32
-    -- ^ Velocity ECEF Y coordinate
-  , _msgVelEcef_z      :: !Int32
-    -- ^ Velocity ECEF Z coordinate
-  , _msgVelEcef_accuracy :: !Word16
-    -- ^ Velocity estimated standard deviation
-  , _msgVelEcef_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgVelEcef_flags  :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgVelEcef where
-  get = do
-    _msgVelEcef_tow <- getWord32le
-    _msgVelEcef_x <- fromIntegral <$> getWord32le
-    _msgVelEcef_y <- fromIntegral <$> getWord32le
-    _msgVelEcef_z <- fromIntegral <$> getWord32le
-    _msgVelEcef_accuracy <- getWord16le
-    _msgVelEcef_n_sats <- getWord8
-    _msgVelEcef_flags <- getWord8
-    pure MsgVelEcef {..}
-
-  put MsgVelEcef {..} = do
-    putWord32le _msgVelEcef_tow
-    (putWord32le . fromIntegral) _msgVelEcef_x
-    (putWord32le . fromIntegral) _msgVelEcef_y
-    (putWord32le . fromIntegral) _msgVelEcef_z
-    putWord16le _msgVelEcef_accuracy
-    putWord8 _msgVelEcef_n_sats
-    putWord8 _msgVelEcef_flags
-
-$(makeSBP 'msgVelEcef ''MsgVelEcef)
-$(makeJSON "_msgVelEcef_" ''MsgVelEcef)
-$(makeLenses ''MsgVelEcef)
-
-msgVelEcefCov :: Word16
-msgVelEcefCov = 0x0215
-
--- | SBP class for message MSG_VEL_ECEF_COV (0x0215).
---
--- This message reports the velocity in Earth Centered Earth Fixed (ECEF)
--- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
--- the matching time-of-week (tow).
-data MsgVelEcefCov = MsgVelEcefCov
-  { _msgVelEcefCov_tow   :: !Word32
-    -- ^ GPS Time of Week
-  , _msgVelEcefCov_x     :: !Int32
-    -- ^ Velocity ECEF X coordinate
-  , _msgVelEcefCov_y     :: !Int32
-    -- ^ Velocity ECEF Y coordinate
-  , _msgVelEcefCov_z     :: !Int32
-    -- ^ Velocity ECEF Z coordinate
-  , _msgVelEcefCov_cov_x_x :: !Float
-    -- ^ Estimated variance of x
-  , _msgVelEcefCov_cov_x_y :: !Float
-    -- ^ Estimated covariance of x and y
-  , _msgVelEcefCov_cov_x_z :: !Float
-    -- ^ Estimated covariance of x and z
-  , _msgVelEcefCov_cov_y_y :: !Float
-    -- ^ Estimated variance of y
-  , _msgVelEcefCov_cov_y_z :: !Float
-    -- ^ Estimated covariance of y and z
-  , _msgVelEcefCov_cov_z_z :: !Float
-    -- ^ Estimated variance of z
-  , _msgVelEcefCov_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgVelEcefCov_flags :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgVelEcefCov where
-  get = do
-    _msgVelEcefCov_tow <- getWord32le
-    _msgVelEcefCov_x <- fromIntegral <$> getWord32le
-    _msgVelEcefCov_y <- fromIntegral <$> getWord32le
-    _msgVelEcefCov_z <- fromIntegral <$> getWord32le
-    _msgVelEcefCov_cov_x_x <- getFloat32le
-    _msgVelEcefCov_cov_x_y <- getFloat32le
-    _msgVelEcefCov_cov_x_z <- getFloat32le
-    _msgVelEcefCov_cov_y_y <- getFloat32le
-    _msgVelEcefCov_cov_y_z <- getFloat32le
-    _msgVelEcefCov_cov_z_z <- getFloat32le
-    _msgVelEcefCov_n_sats <- getWord8
-    _msgVelEcefCov_flags <- getWord8
-    pure MsgVelEcefCov {..}
-
-  put MsgVelEcefCov {..} = do
-    putWord32le _msgVelEcefCov_tow
-    (putWord32le . fromIntegral) _msgVelEcefCov_x
-    (putWord32le . fromIntegral) _msgVelEcefCov_y
-    (putWord32le . fromIntegral) _msgVelEcefCov_z
-    putFloat32le _msgVelEcefCov_cov_x_x
-    putFloat32le _msgVelEcefCov_cov_x_y
-    putFloat32le _msgVelEcefCov_cov_x_z
-    putFloat32le _msgVelEcefCov_cov_y_y
-    putFloat32le _msgVelEcefCov_cov_y_z
-    putFloat32le _msgVelEcefCov_cov_z_z
-    putWord8 _msgVelEcefCov_n_sats
-    putWord8 _msgVelEcefCov_flags
-
-$(makeSBP 'msgVelEcefCov ''MsgVelEcefCov)
-$(makeJSON "_msgVelEcefCov_" ''MsgVelEcefCov)
-$(makeLenses ''MsgVelEcefCov)
-
-msgVelNed :: Word16
-msgVelNed = 0x020E
-
--- | SBP class for message MSG_VEL_NED (0x020E).
---
--- This message reports the velocity in local North East Down (NED)
--- coordinates. 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).
-data MsgVelNed = MsgVelNed
-  { _msgVelNed_tow      :: !Word32
-    -- ^ GPS Time of Week
-  , _msgVelNed_n        :: !Int32
-    -- ^ Velocity North coordinate
-  , _msgVelNed_e        :: !Int32
-    -- ^ Velocity East coordinate
-  , _msgVelNed_d        :: !Int32
-    -- ^ Velocity Down coordinate
-  , _msgVelNed_h_accuracy :: !Word16
-    -- ^ Horizontal velocity estimated standard deviation
-  , _msgVelNed_v_accuracy :: !Word16
-    -- ^ Vertical velocity estimated standard deviation
-  , _msgVelNed_n_sats   :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgVelNed_flags    :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgVelNed where
-  get = do
-    _msgVelNed_tow <- getWord32le
-    _msgVelNed_n <- fromIntegral <$> getWord32le
-    _msgVelNed_e <- fromIntegral <$> getWord32le
-    _msgVelNed_d <- fromIntegral <$> getWord32le
-    _msgVelNed_h_accuracy <- getWord16le
-    _msgVelNed_v_accuracy <- getWord16le
-    _msgVelNed_n_sats <- getWord8
-    _msgVelNed_flags <- getWord8
-    pure MsgVelNed {..}
-
-  put MsgVelNed {..} = do
-    putWord32le _msgVelNed_tow
-    (putWord32le . fromIntegral) _msgVelNed_n
-    (putWord32le . fromIntegral) _msgVelNed_e
-    (putWord32le . fromIntegral) _msgVelNed_d
-    putWord16le _msgVelNed_h_accuracy
-    putWord16le _msgVelNed_v_accuracy
-    putWord8 _msgVelNed_n_sats
-    putWord8 _msgVelNed_flags
-
-$(makeSBP 'msgVelNed ''MsgVelNed)
-$(makeJSON "_msgVelNed_" ''MsgVelNed)
-$(makeLenses ''MsgVelNed)
-
-msgVelNedCov :: Word16
-msgVelNedCov = 0x0212
-
--- | SBP class for message MSG_VEL_NED_COV (0x0212).
---
--- This message reports the velocity in local North East Down (NED)
--- coordinates. 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). This message is
--- similar to the MSG_VEL_NED, but it includes the upper triangular portion of
--- the 3x3 covariance matrix.
-data MsgVelNedCov = MsgVelNedCov
-  { _msgVelNedCov_tow   :: !Word32
-    -- ^ GPS Time of Week
-  , _msgVelNedCov_n     :: !Int32
-    -- ^ Velocity North coordinate
-  , _msgVelNedCov_e     :: !Int32
-    -- ^ Velocity East coordinate
-  , _msgVelNedCov_d     :: !Int32
-    -- ^ Velocity Down coordinate
-  , _msgVelNedCov_cov_n_n :: !Float
-    -- ^ Estimated variance of northward measurement
-  , _msgVelNedCov_cov_n_e :: !Float
-    -- ^ Covariance of northward and eastward measurement
-  , _msgVelNedCov_cov_n_d :: !Float
-    -- ^ Covariance of northward and downward measurement
-  , _msgVelNedCov_cov_e_e :: !Float
-    -- ^ Estimated variance of eastward measurement
-  , _msgVelNedCov_cov_e_d :: !Float
-    -- ^ Covariance of eastward and downward measurement
-  , _msgVelNedCov_cov_d_d :: !Float
-    -- ^ Estimated variance of downward measurement
-  , _msgVelNedCov_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgVelNedCov_flags :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgVelNedCov where
-  get = do
-    _msgVelNedCov_tow <- getWord32le
-    _msgVelNedCov_n <- fromIntegral <$> getWord32le
-    _msgVelNedCov_e <- fromIntegral <$> getWord32le
-    _msgVelNedCov_d <- fromIntegral <$> getWord32le
-    _msgVelNedCov_cov_n_n <- getFloat32le
-    _msgVelNedCov_cov_n_e <- getFloat32le
-    _msgVelNedCov_cov_n_d <- getFloat32le
-    _msgVelNedCov_cov_e_e <- getFloat32le
-    _msgVelNedCov_cov_e_d <- getFloat32le
-    _msgVelNedCov_cov_d_d <- getFloat32le
-    _msgVelNedCov_n_sats <- getWord8
-    _msgVelNedCov_flags <- getWord8
-    pure MsgVelNedCov {..}
-
-  put MsgVelNedCov {..} = do
-    putWord32le _msgVelNedCov_tow
-    (putWord32le . fromIntegral) _msgVelNedCov_n
-    (putWord32le . fromIntegral) _msgVelNedCov_e
-    (putWord32le . fromIntegral) _msgVelNedCov_d
-    putFloat32le _msgVelNedCov_cov_n_n
-    putFloat32le _msgVelNedCov_cov_n_e
-    putFloat32le _msgVelNedCov_cov_n_d
-    putFloat32le _msgVelNedCov_cov_e_e
-    putFloat32le _msgVelNedCov_cov_e_d
-    putFloat32le _msgVelNedCov_cov_d_d
-    putWord8 _msgVelNedCov_n_sats
-    putWord8 _msgVelNedCov_flags
-
-$(makeSBP 'msgVelNedCov ''MsgVelNedCov)
-$(makeJSON "_msgVelNedCov_" ''MsgVelNedCov)
-$(makeLenses ''MsgVelNedCov)
-
-msgVelBody :: Word16
-msgVelBody = 0x0213
-
--- | SBP class for message MSG_VEL_BODY (0x0213).
---
--- This message reports the velocity in the Vehicle Body Frame. By convention,
--- the x-axis should point out the nose of the vehicle and represent the
--- forward direction, while as the y-axis should point out the right hand side
--- of the vehicle. Since this is a right handed system, z should point out the
--- bottom of the vehicle. The orientation and origin of the Vehicle Body Frame
--- are specified via the device settings. The full GPS time is given by the
--- preceding MSG_GPS_TIME with the matching time-of-week (tow). This message is
--- only produced by inertial versions of Swift products and is not available
--- from Piksi Multi or Duro.
-data MsgVelBody = MsgVelBody
-  { _msgVelBody_tow   :: !Word32
-    -- ^ GPS Time of Week
-  , _msgVelBody_x     :: !Int32
-    -- ^ Velocity in x direction
-  , _msgVelBody_y     :: !Int32
-    -- ^ Velocity in y direction
-  , _msgVelBody_z     :: !Int32
-    -- ^ Velocity in z direction
-  , _msgVelBody_cov_x_x :: !Float
-    -- ^ Estimated variance of x
-  , _msgVelBody_cov_x_y :: !Float
-    -- ^ Covariance of x and y
-  , _msgVelBody_cov_x_z :: !Float
-    -- ^ Covariance of x and z
-  , _msgVelBody_cov_y_y :: !Float
-    -- ^ Estimated variance of y
-  , _msgVelBody_cov_y_z :: !Float
-    -- ^ Covariance of y and z
-  , _msgVelBody_cov_z_z :: !Float
-    -- ^ Estimated variance of z
-  , _msgVelBody_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgVelBody_flags :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgVelBody where
-  get = do
-    _msgVelBody_tow <- getWord32le
-    _msgVelBody_x <- fromIntegral <$> getWord32le
-    _msgVelBody_y <- fromIntegral <$> getWord32le
-    _msgVelBody_z <- fromIntegral <$> getWord32le
-    _msgVelBody_cov_x_x <- getFloat32le
-    _msgVelBody_cov_x_y <- getFloat32le
-    _msgVelBody_cov_x_z <- getFloat32le
-    _msgVelBody_cov_y_y <- getFloat32le
-    _msgVelBody_cov_y_z <- getFloat32le
-    _msgVelBody_cov_z_z <- getFloat32le
-    _msgVelBody_n_sats <- getWord8
-    _msgVelBody_flags <- getWord8
-    pure MsgVelBody {..}
-
-  put MsgVelBody {..} = do
-    putWord32le _msgVelBody_tow
-    (putWord32le . fromIntegral) _msgVelBody_x
-    (putWord32le . fromIntegral) _msgVelBody_y
-    (putWord32le . fromIntegral) _msgVelBody_z
-    putFloat32le _msgVelBody_cov_x_x
-    putFloat32le _msgVelBody_cov_x_y
-    putFloat32le _msgVelBody_cov_x_z
-    putFloat32le _msgVelBody_cov_y_y
-    putFloat32le _msgVelBody_cov_y_z
-    putFloat32le _msgVelBody_cov_z_z
-    putWord8 _msgVelBody_n_sats
-    putWord8 _msgVelBody_flags
-
-$(makeSBP 'msgVelBody ''MsgVelBody)
-$(makeJSON "_msgVelBody_" ''MsgVelBody)
-$(makeLenses ''MsgVelBody)
-
-msgAgeCorrections :: Word16
-msgAgeCorrections = 0x0210
-
--- | SBP class for message MSG_AGE_CORRECTIONS (0x0210).
---
--- This message reports the Age of the corrections used for the current
--- Differential solution
-data MsgAgeCorrections = MsgAgeCorrections
-  { _msgAgeCorrections_tow :: !Word32
-    -- ^ GPS Time of Week
-  , _msgAgeCorrections_age :: !Word16
-    -- ^ Age of the corrections (0xFFFF indicates invalid)
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgAgeCorrections where
-  get = do
-    _msgAgeCorrections_tow <- getWord32le
-    _msgAgeCorrections_age <- getWord16le
-    pure MsgAgeCorrections {..}
-
-  put MsgAgeCorrections {..} = do
-    putWord32le _msgAgeCorrections_tow
-    putWord16le _msgAgeCorrections_age
-
-$(makeSBP 'msgAgeCorrections ''MsgAgeCorrections)
-$(makeJSON "_msgAgeCorrections_" ''MsgAgeCorrections)
-$(makeLenses ''MsgAgeCorrections)
-
-msgGpsTimeDepA :: Word16
-msgGpsTimeDepA = 0x0100
-
--- | SBP class for message MSG_GPS_TIME_DEP_A (0x0100).
---
--- This message reports the GPS time, representing the time since the GPS epoch
--- began on midnight January 6, 1980 UTC. GPS time counts the weeks and seconds
--- of the week. The weeks begin at the Saturday/Sunday transition. GPS week 0
--- began at the beginning of the GPS time scale.  Within each week number, the
--- GPS time of the week is between between 0 and 604800 seconds (=60*60*24*7).
--- Note that GPS time does not accumulate leap seconds, and as of now, has a
--- small offset from UTC. In a message stream, this message precedes a set of
--- other navigation messages referenced to the same time (but lacking the ns
--- field) and indicates a more precise time of these messages.
-data MsgGpsTimeDepA = MsgGpsTimeDepA
-  { _msgGpsTimeDepA_wn        :: !Word16
-    -- ^ GPS week number
-  , _msgGpsTimeDepA_tow       :: !Word32
-    -- ^ GPS time of week rounded to the nearest millisecond
-  , _msgGpsTimeDepA_ns_residual :: !Int32
-    -- ^ Nanosecond residual of millisecond-rounded TOW (ranges from -500000 to
-    -- 500000)
-  , _msgGpsTimeDepA_flags     :: !Word8
-    -- ^ Status flags (reserved)
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgGpsTimeDepA where
-  get = do
-    _msgGpsTimeDepA_wn <- getWord16le
-    _msgGpsTimeDepA_tow <- getWord32le
-    _msgGpsTimeDepA_ns_residual <- fromIntegral <$> getWord32le
-    _msgGpsTimeDepA_flags <- getWord8
-    pure MsgGpsTimeDepA {..}
-
-  put MsgGpsTimeDepA {..} = do
-    putWord16le _msgGpsTimeDepA_wn
-    putWord32le _msgGpsTimeDepA_tow
-    (putWord32le . fromIntegral) _msgGpsTimeDepA_ns_residual
-    putWord8 _msgGpsTimeDepA_flags
-
-$(makeSBP 'msgGpsTimeDepA ''MsgGpsTimeDepA)
-$(makeJSON "_msgGpsTimeDepA_" ''MsgGpsTimeDepA)
-$(makeLenses ''MsgGpsTimeDepA)
-
-msgDopsDepA :: Word16
-msgDopsDepA = 0x0206
-
--- | SBP class for message MSG_DOPS_DEP_A (0x0206).
---
--- This dilution of precision (DOP) message describes the effect of navigation
--- satellite geometry on positional measurement precision.
-data MsgDopsDepA = MsgDopsDepA
-  { _msgDopsDepA_tow :: !Word32
-    -- ^ GPS Time of Week
-  , _msgDopsDepA_gdop :: !Word16
-    -- ^ Geometric Dilution of Precision
-  , _msgDopsDepA_pdop :: !Word16
-    -- ^ Position Dilution of Precision
-  , _msgDopsDepA_tdop :: !Word16
-    -- ^ Time Dilution of Precision
-  , _msgDopsDepA_hdop :: !Word16
-    -- ^ Horizontal Dilution of Precision
-  , _msgDopsDepA_vdop :: !Word16
-    -- ^ Vertical Dilution of Precision
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgDopsDepA where
-  get = do
-    _msgDopsDepA_tow <- getWord32le
-    _msgDopsDepA_gdop <- getWord16le
-    _msgDopsDepA_pdop <- getWord16le
-    _msgDopsDepA_tdop <- getWord16le
-    _msgDopsDepA_hdop <- getWord16le
-    _msgDopsDepA_vdop <- getWord16le
-    pure MsgDopsDepA {..}
-
-  put MsgDopsDepA {..} = do
-    putWord32le _msgDopsDepA_tow
-    putWord16le _msgDopsDepA_gdop
-    putWord16le _msgDopsDepA_pdop
-    putWord16le _msgDopsDepA_tdop
-    putWord16le _msgDopsDepA_hdop
-    putWord16le _msgDopsDepA_vdop
-
-$(makeSBP 'msgDopsDepA ''MsgDopsDepA)
-$(makeJSON "_msgDopsDepA_" ''MsgDopsDepA)
-$(makeLenses ''MsgDopsDepA)
-
-msgPosEcefDepA :: Word16
-msgPosEcefDepA = 0x0200
-
--- | SBP class for message MSG_POS_ECEF_DEP_A (0x0200).
---
--- The position solution message reports absolute Earth Centered Earth Fixed
--- (ECEF) coordinates and the status (single point vs pseudo-absolute RTK) of
--- the position solution. If the rover receiver knows the surveyed position of
--- the base station and has an RTK solution, this reports a pseudo-absolute
--- position solution using the base station position and the rover's RTK
--- baseline vector. The full GPS time is given by the preceding MSG_GPS_TIME
--- with the matching time-of-week (tow).
-data MsgPosEcefDepA = MsgPosEcefDepA
-  { _msgPosEcefDepA_tow    :: !Word32
-    -- ^ GPS Time of Week
-  , _msgPosEcefDepA_x      :: !Double
-    -- ^ ECEF X coordinate
-  , _msgPosEcefDepA_y      :: !Double
-    -- ^ ECEF Y coordinate
-  , _msgPosEcefDepA_z      :: !Double
-    -- ^ ECEF Z coordinate
-  , _msgPosEcefDepA_accuracy :: !Word16
-    -- ^ Position accuracy estimate (not implemented). Defaults to 0.
-  , _msgPosEcefDepA_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgPosEcefDepA_flags  :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgPosEcefDepA where
-  get = do
-    _msgPosEcefDepA_tow <- getWord32le
-    _msgPosEcefDepA_x <- getFloat64le
-    _msgPosEcefDepA_y <- getFloat64le
-    _msgPosEcefDepA_z <- getFloat64le
-    _msgPosEcefDepA_accuracy <- getWord16le
-    _msgPosEcefDepA_n_sats <- getWord8
-    _msgPosEcefDepA_flags <- getWord8
-    pure MsgPosEcefDepA {..}
-
-  put MsgPosEcefDepA {..} = do
-    putWord32le _msgPosEcefDepA_tow
-    putFloat64le _msgPosEcefDepA_x
-    putFloat64le _msgPosEcefDepA_y
-    putFloat64le _msgPosEcefDepA_z
-    putWord16le _msgPosEcefDepA_accuracy
-    putWord8 _msgPosEcefDepA_n_sats
-    putWord8 _msgPosEcefDepA_flags
-
-$(makeSBP 'msgPosEcefDepA ''MsgPosEcefDepA)
-$(makeJSON "_msgPosEcefDepA_" ''MsgPosEcefDepA)
-$(makeLenses ''MsgPosEcefDepA)
-
-msgPosLlhDepA :: Word16
-msgPosLlhDepA = 0x0201
-
--- | SBP class for message MSG_POS_LLH_DEP_A (0x0201).
---
--- This position solution message reports the absolute geodetic coordinates and
--- the status (single point vs pseudo-absolute RTK) of the position solution.
--- If the rover receiver knows the surveyed position of the base station and
--- has an RTK solution, this reports a pseudo-absolute position solution using
--- the base station position and the rover's RTK baseline vector. The full GPS
--- time is given by the preceding MSG_GPS_TIME with the matching time-of-week
--- (tow).
-data MsgPosLlhDepA = MsgPosLlhDepA
-  { _msgPosLlhDepA_tow      :: !Word32
-    -- ^ GPS Time of Week
-  , _msgPosLlhDepA_lat      :: !Double
-    -- ^ Latitude
-  , _msgPosLlhDepA_lon      :: !Double
-    -- ^ Longitude
-  , _msgPosLlhDepA_height   :: !Double
-    -- ^ Height
-  , _msgPosLlhDepA_h_accuracy :: !Word16
-    -- ^ Horizontal position accuracy estimate (not implemented). Defaults to 0.
-  , _msgPosLlhDepA_v_accuracy :: !Word16
-    -- ^ Vertical position accuracy estimate (not implemented). Defaults to 0.
-  , _msgPosLlhDepA_n_sats   :: !Word8
-    -- ^ Number of satellites used in solution.
-  , _msgPosLlhDepA_flags    :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgPosLlhDepA where
-  get = do
-    _msgPosLlhDepA_tow <- getWord32le
-    _msgPosLlhDepA_lat <- getFloat64le
-    _msgPosLlhDepA_lon <- getFloat64le
-    _msgPosLlhDepA_height <- getFloat64le
-    _msgPosLlhDepA_h_accuracy <- getWord16le
-    _msgPosLlhDepA_v_accuracy <- getWord16le
-    _msgPosLlhDepA_n_sats <- getWord8
-    _msgPosLlhDepA_flags <- getWord8
-    pure MsgPosLlhDepA {..}
-
-  put MsgPosLlhDepA {..} = do
-    putWord32le _msgPosLlhDepA_tow
-    putFloat64le _msgPosLlhDepA_lat
-    putFloat64le _msgPosLlhDepA_lon
-    putFloat64le _msgPosLlhDepA_height
-    putWord16le _msgPosLlhDepA_h_accuracy
-    putWord16le _msgPosLlhDepA_v_accuracy
-    putWord8 _msgPosLlhDepA_n_sats
-    putWord8 _msgPosLlhDepA_flags
-
-$(makeSBP 'msgPosLlhDepA ''MsgPosLlhDepA)
-$(makeJSON "_msgPosLlhDepA_" ''MsgPosLlhDepA)
-$(makeLenses ''MsgPosLlhDepA)
-
-msgBaselineEcefDepA :: Word16
-msgBaselineEcefDepA = 0x0202
-
--- | SBP class for message MSG_BASELINE_ECEF_DEP_A (0x0202).
---
--- This message reports the baseline solution in Earth Centered Earth Fixed
--- (ECEF) coordinates. This baseline is the relative vector distance from the
--- base station to the rover receiver. The full GPS time is given by the
--- preceding MSG_GPS_TIME with the matching time-of-week (tow).
-data MsgBaselineEcefDepA = MsgBaselineEcefDepA
-  { _msgBaselineEcefDepA_tow    :: !Word32
-    -- ^ GPS Time of Week
-  , _msgBaselineEcefDepA_x      :: !Int32
-    -- ^ Baseline ECEF X coordinate
-  , _msgBaselineEcefDepA_y      :: !Int32
-    -- ^ Baseline ECEF Y coordinate
-  , _msgBaselineEcefDepA_z      :: !Int32
-    -- ^ Baseline ECEF Z coordinate
-  , _msgBaselineEcefDepA_accuracy :: !Word16
-    -- ^ Position accuracy estimate
-  , _msgBaselineEcefDepA_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgBaselineEcefDepA_flags  :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgBaselineEcefDepA where
-  get = do
-    _msgBaselineEcefDepA_tow <- getWord32le
-    _msgBaselineEcefDepA_x <- fromIntegral <$> getWord32le
-    _msgBaselineEcefDepA_y <- fromIntegral <$> getWord32le
-    _msgBaselineEcefDepA_z <- fromIntegral <$> getWord32le
-    _msgBaselineEcefDepA_accuracy <- getWord16le
-    _msgBaselineEcefDepA_n_sats <- getWord8
-    _msgBaselineEcefDepA_flags <- getWord8
-    pure MsgBaselineEcefDepA {..}
-
-  put MsgBaselineEcefDepA {..} = do
-    putWord32le _msgBaselineEcefDepA_tow
-    (putWord32le . fromIntegral) _msgBaselineEcefDepA_x
-    (putWord32le . fromIntegral) _msgBaselineEcefDepA_y
-    (putWord32le . fromIntegral) _msgBaselineEcefDepA_z
-    putWord16le _msgBaselineEcefDepA_accuracy
-    putWord8 _msgBaselineEcefDepA_n_sats
-    putWord8 _msgBaselineEcefDepA_flags
-
-$(makeSBP 'msgBaselineEcefDepA ''MsgBaselineEcefDepA)
-$(makeJSON "_msgBaselineEcefDepA_" ''MsgBaselineEcefDepA)
-$(makeLenses ''MsgBaselineEcefDepA)
-
-msgBaselineNedDepA :: Word16
-msgBaselineNedDepA = 0x0203
-
--- | SBP class for message MSG_BASELINE_NED_DEP_A (0x0203).
---
--- This message reports the baseline solution in North East Down (NED)
--- coordinates. This baseline is the relative vector distance from the base
--- station to the rover receiver, and NED coordinate system is defined at the
--- local WGS84 tangent plane centered at the base station position.  The full
--- GPS time is given by the preceding MSG_GPS_TIME with the matching time-of-
--- week (tow).
-data MsgBaselineNedDepA = MsgBaselineNedDepA
-  { _msgBaselineNedDepA_tow      :: !Word32
-    -- ^ GPS Time of Week
-  , _msgBaselineNedDepA_n        :: !Int32
-    -- ^ Baseline North coordinate
-  , _msgBaselineNedDepA_e        :: !Int32
-    -- ^ Baseline East coordinate
-  , _msgBaselineNedDepA_d        :: !Int32
-    -- ^ Baseline Down coordinate
-  , _msgBaselineNedDepA_h_accuracy :: !Word16
-    -- ^ Horizontal position accuracy estimate (not implemented). Defaults to 0.
-  , _msgBaselineNedDepA_v_accuracy :: !Word16
-    -- ^ Vertical position accuracy estimate (not implemented). Defaults to 0.
-  , _msgBaselineNedDepA_n_sats   :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgBaselineNedDepA_flags    :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgBaselineNedDepA where
-  get = do
-    _msgBaselineNedDepA_tow <- getWord32le
-    _msgBaselineNedDepA_n <- fromIntegral <$> getWord32le
-    _msgBaselineNedDepA_e <- fromIntegral <$> getWord32le
-    _msgBaselineNedDepA_d <- fromIntegral <$> getWord32le
-    _msgBaselineNedDepA_h_accuracy <- getWord16le
-    _msgBaselineNedDepA_v_accuracy <- getWord16le
-    _msgBaselineNedDepA_n_sats <- getWord8
-    _msgBaselineNedDepA_flags <- getWord8
-    pure MsgBaselineNedDepA {..}
-
-  put MsgBaselineNedDepA {..} = do
-    putWord32le _msgBaselineNedDepA_tow
-    (putWord32le . fromIntegral) _msgBaselineNedDepA_n
-    (putWord32le . fromIntegral) _msgBaselineNedDepA_e
-    (putWord32le . fromIntegral) _msgBaselineNedDepA_d
-    putWord16le _msgBaselineNedDepA_h_accuracy
-    putWord16le _msgBaselineNedDepA_v_accuracy
-    putWord8 _msgBaselineNedDepA_n_sats
-    putWord8 _msgBaselineNedDepA_flags
-
-$(makeSBP 'msgBaselineNedDepA ''MsgBaselineNedDepA)
-$(makeJSON "_msgBaselineNedDepA_" ''MsgBaselineNedDepA)
-$(makeLenses ''MsgBaselineNedDepA)
-
-msgVelEcefDepA :: Word16
-msgVelEcefDepA = 0x0204
-
--- | SBP class for message MSG_VEL_ECEF_DEP_A (0x0204).
---
--- This message reports the velocity in Earth Centered Earth Fixed (ECEF)
--- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
--- the matching time-of-week (tow).
-data MsgVelEcefDepA = MsgVelEcefDepA
-  { _msgVelEcefDepA_tow    :: !Word32
-    -- ^ GPS Time of Week
-  , _msgVelEcefDepA_x      :: !Int32
-    -- ^ Velocity ECEF X coordinate
-  , _msgVelEcefDepA_y      :: !Int32
-    -- ^ Velocity ECEF Y coordinate
-  , _msgVelEcefDepA_z      :: !Int32
-    -- ^ Velocity ECEF Z coordinate
-  , _msgVelEcefDepA_accuracy :: !Word16
-    -- ^ Velocity accuracy estimate (not implemented). Defaults to 0.
-  , _msgVelEcefDepA_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgVelEcefDepA_flags  :: !Word8
-    -- ^ Status flags (reserved)
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgVelEcefDepA where
-  get = do
-    _msgVelEcefDepA_tow <- getWord32le
-    _msgVelEcefDepA_x <- fromIntegral <$> getWord32le
-    _msgVelEcefDepA_y <- fromIntegral <$> getWord32le
-    _msgVelEcefDepA_z <- fromIntegral <$> getWord32le
-    _msgVelEcefDepA_accuracy <- getWord16le
-    _msgVelEcefDepA_n_sats <- getWord8
-    _msgVelEcefDepA_flags <- getWord8
-    pure MsgVelEcefDepA {..}
-
-  put MsgVelEcefDepA {..} = do
-    putWord32le _msgVelEcefDepA_tow
-    (putWord32le . fromIntegral) _msgVelEcefDepA_x
-    (putWord32le . fromIntegral) _msgVelEcefDepA_y
-    (putWord32le . fromIntegral) _msgVelEcefDepA_z
-    putWord16le _msgVelEcefDepA_accuracy
-    putWord8 _msgVelEcefDepA_n_sats
-    putWord8 _msgVelEcefDepA_flags
-
-$(makeSBP 'msgVelEcefDepA ''MsgVelEcefDepA)
-$(makeJSON "_msgVelEcefDepA_" ''MsgVelEcefDepA)
-$(makeLenses ''MsgVelEcefDepA)
-
-msgVelNedDepA :: Word16
-msgVelNedDepA = 0x0205
-
--- | SBP class for message MSG_VEL_NED_DEP_A (0x0205).
---
--- This message reports the velocity in local North East Down (NED)
--- coordinates. 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).
-data MsgVelNedDepA = MsgVelNedDepA
-  { _msgVelNedDepA_tow      :: !Word32
-    -- ^ GPS Time of Week
-  , _msgVelNedDepA_n        :: !Int32
-    -- ^ Velocity North coordinate
-  , _msgVelNedDepA_e        :: !Int32
-    -- ^ Velocity East coordinate
-  , _msgVelNedDepA_d        :: !Int32
-    -- ^ Velocity Down coordinate
-  , _msgVelNedDepA_h_accuracy :: !Word16
-    -- ^ Horizontal velocity accuracy estimate (not implemented). Defaults to 0.
-  , _msgVelNedDepA_v_accuracy :: !Word16
-    -- ^ Vertical velocity accuracy estimate (not implemented). Defaults to 0.
-  , _msgVelNedDepA_n_sats   :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgVelNedDepA_flags    :: !Word8
-    -- ^ Status flags (reserved)
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgVelNedDepA where
-  get = do
-    _msgVelNedDepA_tow <- getWord32le
-    _msgVelNedDepA_n <- fromIntegral <$> getWord32le
-    _msgVelNedDepA_e <- fromIntegral <$> getWord32le
-    _msgVelNedDepA_d <- fromIntegral <$> getWord32le
-    _msgVelNedDepA_h_accuracy <- getWord16le
-    _msgVelNedDepA_v_accuracy <- getWord16le
-    _msgVelNedDepA_n_sats <- getWord8
-    _msgVelNedDepA_flags <- getWord8
-    pure MsgVelNedDepA {..}
-
-  put MsgVelNedDepA {..} = do
-    putWord32le _msgVelNedDepA_tow
-    (putWord32le . fromIntegral) _msgVelNedDepA_n
-    (putWord32le . fromIntegral) _msgVelNedDepA_e
-    (putWord32le . fromIntegral) _msgVelNedDepA_d
-    putWord16le _msgVelNedDepA_h_accuracy
-    putWord16le _msgVelNedDepA_v_accuracy
-    putWord8 _msgVelNedDepA_n_sats
-    putWord8 _msgVelNedDepA_flags
-
-$(makeSBP 'msgVelNedDepA ''MsgVelNedDepA)
-$(makeJSON "_msgVelNedDepA_" ''MsgVelNedDepA)
-$(makeLenses ''MsgVelNedDepA)
-
-msgBaselineHeadingDepA :: Word16
-msgBaselineHeadingDepA = 0x0207
-
--- | SBP class for message MSG_BASELINE_HEADING_DEP_A (0x0207).
---
--- This message reports the baseline heading pointing from the base station to
--- the rover relative to True North. The full GPS time is given by the
--- preceding MSG_GPS_TIME with the matching time-of-week (tow).
-data MsgBaselineHeadingDepA = MsgBaselineHeadingDepA
-  { _msgBaselineHeadingDepA_tow   :: !Word32
-    -- ^ GPS Time of Week
-  , _msgBaselineHeadingDepA_heading :: !Word32
-    -- ^ Heading
-  , _msgBaselineHeadingDepA_n_sats :: !Word8
-    -- ^ Number of satellites used in solution
-  , _msgBaselineHeadingDepA_flags :: !Word8
-    -- ^ Status flags
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgBaselineHeadingDepA where
-  get = do
-    _msgBaselineHeadingDepA_tow <- getWord32le
-    _msgBaselineHeadingDepA_heading <- getWord32le
-    _msgBaselineHeadingDepA_n_sats <- getWord8
-    _msgBaselineHeadingDepA_flags <- getWord8
-    pure MsgBaselineHeadingDepA {..}
-
-  put MsgBaselineHeadingDepA {..} = do
-    putWord32le _msgBaselineHeadingDepA_tow
-    putWord32le _msgBaselineHeadingDepA_heading
-    putWord8 _msgBaselineHeadingDepA_n_sats
-    putWord8 _msgBaselineHeadingDepA_flags
-
-$(makeSBP 'msgBaselineHeadingDepA ''MsgBaselineHeadingDepA)
-$(makeJSON "_msgBaselineHeadingDepA_" ''MsgBaselineHeadingDepA)
-$(makeLenses ''MsgBaselineHeadingDepA)
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
+-- Stability:   experimental
+-- Portability: portable
+--
+-- \< Geodetic navigation messages reporting GPS time, position, velocity, and
+-- baseline position solutions. For position solutions, these messages define
+-- several different position solutions: single-point (SPP), RTK, and pseudo-
+-- absolute position solutions.
+--
+-- The SPP is the standalone, absolute GPS position solution using only a
+-- single receiver. The RTK solution is the differential GPS solution, which
+-- can use either a fixed/integer or floating carrier phase ambiguity. The
+-- pseudo-absolute position solution uses a user-provided, well-surveyed base
+-- station position (if available) and the RTK solution in tandem.
+--
+-- When the inertial navigation mode indicates that the IMU is used, all
+-- messages are reported in the vehicle body frame as defined by device
+-- settings.  By default, the vehicle body frame is configured to be
+-- coincident with the antenna phase center.  When there is no inertial
+-- navigation, the solution will be reported at the phase center of the
+-- antenna. There is no inertial navigation capability on Piksi Multi or Duro.
+--
+-- The tow field, when valid, is most often the Time of Measurement. When this
+-- is the case, the 5th bit of flags is set to the default value of 0. When
+-- this is not the case, the tow may be a time of arrival or a local system
+-- timestamp, irrespective of the time reference (GPS Week or else), but not a
+-- Time of Measurement. \>
+
+module SwiftNav.SBP.Navigation
+  ( module SwiftNav.SBP.Navigation
+  ) where
+
+import BasicPrelude
+import Control.Lens
+import Control.Monad.Loops
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.IEEE754
+import Data.Binary.Put
+import Data.ByteString.Lazy    hiding (ByteString)
+import Data.Int
+import Data.Word
+import SwiftNav.SBP.TH
+import SwiftNav.SBP.Types
+
+{-# ANN module ("HLint: ignore Use camelCase"::String) #-}
+{-# ANN module ("HLint: ignore Redundant do"::String) #-}
+{-# ANN module ("HLint: ignore Use newtype instead of data"::String) #-}
+
+
+msgGpsTime :: Word16
+msgGpsTime = 0x0102
+
+-- | SBP class for message MSG_GPS_TIME (0x0102).
+--
+-- This message reports the GPS time, representing the time since the GPS
+-- epoch began on midnight January 6, 1980 UTC. GPS time counts the weeks and
+-- seconds of the week. The weeks begin at the Saturday/Sunday transition. GPS
+-- week 0 began at the beginning of the GPS time scale.
+--
+-- Within each week number, the GPS time of the week is between between 0 and
+-- 604800 seconds (=60*60*24*7). Note that GPS time does not accumulate leap
+-- seconds, and as of now, has a small offset from UTC. In a message stream,
+-- this message precedes a set of other navigation messages referenced to the
+-- same time (but lacking the ns field) and indicates a more precise time of
+-- these messages.
+data MsgGpsTime = MsgGpsTime
+  { _msgGpsTime_wn        :: !Word16
+    -- ^ GPS week number
+  , _msgGpsTime_tow       :: !Word32
+    -- ^ GPS time of week rounded to the nearest millisecond
+  , _msgGpsTime_ns_residual :: !Int32
+    -- ^ Nanosecond residual of millisecond-rounded TOW (ranges from -500000 to
+    -- 500000)
+  , _msgGpsTime_flags     :: !Word8
+    -- ^ Status flags (reserved)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgGpsTime where
+  get = do
+    _msgGpsTime_wn <- getWord16le
+    _msgGpsTime_tow <- getWord32le
+    _msgGpsTime_ns_residual <- (fromIntegral <$> getWord32le)
+    _msgGpsTime_flags <- getWord8
+    pure MsgGpsTime {..}
+
+  put MsgGpsTime {..} = do
+    putWord16le _msgGpsTime_wn
+    putWord32le _msgGpsTime_tow
+    (putWord32le . fromIntegral) _msgGpsTime_ns_residual
+    putWord8 _msgGpsTime_flags
+
+$(makeSBP 'msgGpsTime ''MsgGpsTime)
+$(makeJSON "_msgGpsTime_" ''MsgGpsTime)
+$(makeLenses ''MsgGpsTime)
+
+msgGpsTimeGnss :: Word16
+msgGpsTimeGnss = 0x0104
+
+-- | SBP class for message MSG_GPS_TIME_GNSS (0x0104).
+--
+-- This message reports the GPS time, representing the time since the GPS
+-- epoch began on midnight January 6, 1980 UTC. GPS time counts the weeks and
+-- seconds of the week. The weeks begin at the Saturday/Sunday transition. GPS
+-- week 0 began at the beginning of the GPS time scale.
+--
+-- Within each week number, the GPS time of the week is between between 0 and
+-- 604800 seconds (=60*60*24*7). Note that GPS time does not accumulate leap
+-- seconds, and as of now, has a small offset from UTC. In a message stream,
+-- this message precedes a set of other navigation messages referenced to the
+-- same time (but lacking the ns field) and indicates a more precise time of
+-- these messages.
+data MsgGpsTimeGnss = MsgGpsTimeGnss
+  { _msgGpsTimeGnss_wn        :: !Word16
+    -- ^ GPS week number
+  , _msgGpsTimeGnss_tow       :: !Word32
+    -- ^ GPS time of week rounded to the nearest millisecond
+  , _msgGpsTimeGnss_ns_residual :: !Int32
+    -- ^ Nanosecond residual of millisecond-rounded TOW (ranges from -500000 to
+    -- 500000)
+  , _msgGpsTimeGnss_flags     :: !Word8
+    -- ^ Status flags (reserved)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgGpsTimeGnss where
+  get = do
+    _msgGpsTimeGnss_wn <- getWord16le
+    _msgGpsTimeGnss_tow <- getWord32le
+    _msgGpsTimeGnss_ns_residual <- (fromIntegral <$> getWord32le)
+    _msgGpsTimeGnss_flags <- getWord8
+    pure MsgGpsTimeGnss {..}
+
+  put MsgGpsTimeGnss {..} = do
+    putWord16le _msgGpsTimeGnss_wn
+    putWord32le _msgGpsTimeGnss_tow
+    (putWord32le . fromIntegral) _msgGpsTimeGnss_ns_residual
+    putWord8 _msgGpsTimeGnss_flags
+
+$(makeSBP 'msgGpsTimeGnss ''MsgGpsTimeGnss)
+$(makeJSON "_msgGpsTimeGnss_" ''MsgGpsTimeGnss)
+$(makeLenses ''MsgGpsTimeGnss)
+
+msgUtcTime :: Word16
+msgUtcTime = 0x0103
+
+-- | SBP class for message MSG_UTC_TIME (0x0103).
+--
+-- This message reports the Universal Coordinated Time (UTC).  Note the flags
+-- which indicate the source of the UTC offset value and source of the time
+-- fix.
+data MsgUtcTime = MsgUtcTime
+  { _msgUtcTime_flags :: !Word8
+    -- ^ Indicates source and time validity
+  , _msgUtcTime_tow   :: !Word32
+    -- ^ GPS time of week rounded to the nearest millisecond
+  , _msgUtcTime_year  :: !Word16
+    -- ^ Year
+  , _msgUtcTime_month :: !Word8
+    -- ^ Month (range 1 .. 12)
+  , _msgUtcTime_day   :: !Word8
+    -- ^ days in the month (range 1-31)
+  , _msgUtcTime_hours :: !Word8
+    -- ^ hours of day (range 0-23)
+  , _msgUtcTime_minutes :: !Word8
+    -- ^ minutes of hour (range 0-59)
+  , _msgUtcTime_seconds :: !Word8
+    -- ^ seconds of minute (range 0-60) rounded down
+  , _msgUtcTime_ns    :: !Word32
+    -- ^ nanoseconds of second (range 0-999999999)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgUtcTime where
+  get = do
+    _msgUtcTime_flags <- getWord8
+    _msgUtcTime_tow <- getWord32le
+    _msgUtcTime_year <- getWord16le
+    _msgUtcTime_month <- getWord8
+    _msgUtcTime_day <- getWord8
+    _msgUtcTime_hours <- getWord8
+    _msgUtcTime_minutes <- getWord8
+    _msgUtcTime_seconds <- getWord8
+    _msgUtcTime_ns <- getWord32le
+    pure MsgUtcTime {..}
+
+  put MsgUtcTime {..} = do
+    putWord8 _msgUtcTime_flags
+    putWord32le _msgUtcTime_tow
+    putWord16le _msgUtcTime_year
+    putWord8 _msgUtcTime_month
+    putWord8 _msgUtcTime_day
+    putWord8 _msgUtcTime_hours
+    putWord8 _msgUtcTime_minutes
+    putWord8 _msgUtcTime_seconds
+    putWord32le _msgUtcTime_ns
+
+$(makeSBP 'msgUtcTime ''MsgUtcTime)
+$(makeJSON "_msgUtcTime_" ''MsgUtcTime)
+$(makeLenses ''MsgUtcTime)
+
+msgUtcTimeGnss :: Word16
+msgUtcTimeGnss = 0x0105
+
+-- | SBP class for message MSG_UTC_TIME_GNSS (0x0105).
+--
+-- This message reports the Universal Coordinated Time (UTC).  Note the flags
+-- which indicate the source of the UTC offset value and source of the time
+-- fix.
+data MsgUtcTimeGnss = MsgUtcTimeGnss
+  { _msgUtcTimeGnss_flags :: !Word8
+    -- ^ Indicates source and time validity
+  , _msgUtcTimeGnss_tow   :: !Word32
+    -- ^ GPS time of week rounded to the nearest millisecond
+  , _msgUtcTimeGnss_year  :: !Word16
+    -- ^ Year
+  , _msgUtcTimeGnss_month :: !Word8
+    -- ^ Month (range 1 .. 12)
+  , _msgUtcTimeGnss_day   :: !Word8
+    -- ^ days in the month (range 1-31)
+  , _msgUtcTimeGnss_hours :: !Word8
+    -- ^ hours of day (range 0-23)
+  , _msgUtcTimeGnss_minutes :: !Word8
+    -- ^ minutes of hour (range 0-59)
+  , _msgUtcTimeGnss_seconds :: !Word8
+    -- ^ seconds of minute (range 0-60) rounded down
+  , _msgUtcTimeGnss_ns    :: !Word32
+    -- ^ nanoseconds of second (range 0-999999999)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgUtcTimeGnss where
+  get = do
+    _msgUtcTimeGnss_flags <- getWord8
+    _msgUtcTimeGnss_tow <- getWord32le
+    _msgUtcTimeGnss_year <- getWord16le
+    _msgUtcTimeGnss_month <- getWord8
+    _msgUtcTimeGnss_day <- getWord8
+    _msgUtcTimeGnss_hours <- getWord8
+    _msgUtcTimeGnss_minutes <- getWord8
+    _msgUtcTimeGnss_seconds <- getWord8
+    _msgUtcTimeGnss_ns <- getWord32le
+    pure MsgUtcTimeGnss {..}
+
+  put MsgUtcTimeGnss {..} = do
+    putWord8 _msgUtcTimeGnss_flags
+    putWord32le _msgUtcTimeGnss_tow
+    putWord16le _msgUtcTimeGnss_year
+    putWord8 _msgUtcTimeGnss_month
+    putWord8 _msgUtcTimeGnss_day
+    putWord8 _msgUtcTimeGnss_hours
+    putWord8 _msgUtcTimeGnss_minutes
+    putWord8 _msgUtcTimeGnss_seconds
+    putWord32le _msgUtcTimeGnss_ns
+
+$(makeSBP 'msgUtcTimeGnss ''MsgUtcTimeGnss)
+$(makeJSON "_msgUtcTimeGnss_" ''MsgUtcTimeGnss)
+$(makeLenses ''MsgUtcTimeGnss)
+
+msgDops :: Word16
+msgDops = 0x0208
+
+-- | SBP class for message MSG_DOPS (0x0208).
+--
+-- This dilution of precision (DOP) message describes the effect of navigation
+-- satellite geometry on positional measurement precision.  The flags field
+-- indicated whether the DOP reported corresponds to differential or SPP
+-- solution.
+data MsgDops = MsgDops
+  { _msgDops_tow :: !Word32
+    -- ^ GPS Time of Week
+  , _msgDops_gdop :: !Word16
+    -- ^ Geometric Dilution of Precision
+  , _msgDops_pdop :: !Word16
+    -- ^ Position Dilution of Precision
+  , _msgDops_tdop :: !Word16
+    -- ^ Time Dilution of Precision
+  , _msgDops_hdop :: !Word16
+    -- ^ Horizontal Dilution of Precision
+  , _msgDops_vdop :: !Word16
+    -- ^ Vertical Dilution of Precision
+  , _msgDops_flags :: !Word8
+    -- ^ Indicates the position solution with which the DOPS message corresponds
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgDops where
+  get = do
+    _msgDops_tow <- getWord32le
+    _msgDops_gdop <- getWord16le
+    _msgDops_pdop <- getWord16le
+    _msgDops_tdop <- getWord16le
+    _msgDops_hdop <- getWord16le
+    _msgDops_vdop <- getWord16le
+    _msgDops_flags <- getWord8
+    pure MsgDops {..}
+
+  put MsgDops {..} = do
+    putWord32le _msgDops_tow
+    putWord16le _msgDops_gdop
+    putWord16le _msgDops_pdop
+    putWord16le _msgDops_tdop
+    putWord16le _msgDops_hdop
+    putWord16le _msgDops_vdop
+    putWord8 _msgDops_flags
+
+$(makeSBP 'msgDops ''MsgDops)
+$(makeJSON "_msgDops_" ''MsgDops)
+$(makeLenses ''MsgDops)
+
+msgPosEcef :: Word16
+msgPosEcef = 0x0209
+
+-- | SBP class for message MSG_POS_ECEF (0x0209).
+--
+-- The position solution message reports absolute Earth Centered Earth Fixed
+-- (ECEF) coordinates and the status (single point vs pseudo-absolute RTK) of
+-- the position solution. If the rover receiver knows the surveyed position of
+-- the base station and has an RTK solution, this reports a pseudo-absolute
+-- position solution using the base station position and the rover's RTK
+-- baseline vector. The full GPS time is given by the preceding MSG_GPS_TIME
+-- with the matching time-of-week (tow).
+data MsgPosEcef = MsgPosEcef
+  { _msgPosEcef_tow    :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosEcef_x      :: !Double
+    -- ^ ECEF X coordinate
+  , _msgPosEcef_y      :: !Double
+    -- ^ ECEF Y coordinate
+  , _msgPosEcef_z      :: !Double
+    -- ^ ECEF Z coordinate
+  , _msgPosEcef_accuracy :: !Word16
+    -- ^ Position estimated standard deviation
+  , _msgPosEcef_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgPosEcef_flags  :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosEcef where
+  get = do
+    _msgPosEcef_tow <- getWord32le
+    _msgPosEcef_x <- getFloat64le
+    _msgPosEcef_y <- getFloat64le
+    _msgPosEcef_z <- getFloat64le
+    _msgPosEcef_accuracy <- getWord16le
+    _msgPosEcef_n_sats <- getWord8
+    _msgPosEcef_flags <- getWord8
+    pure MsgPosEcef {..}
+
+  put MsgPosEcef {..} = do
+    putWord32le _msgPosEcef_tow
+    putFloat64le _msgPosEcef_x
+    putFloat64le _msgPosEcef_y
+    putFloat64le _msgPosEcef_z
+    putWord16le _msgPosEcef_accuracy
+    putWord8 _msgPosEcef_n_sats
+    putWord8 _msgPosEcef_flags
+
+$(makeSBP 'msgPosEcef ''MsgPosEcef)
+$(makeJSON "_msgPosEcef_" ''MsgPosEcef)
+$(makeLenses ''MsgPosEcef)
+
+msgPosEcefCov :: Word16
+msgPosEcefCov = 0x0214
+
+-- | SBP class for message MSG_POS_ECEF_COV (0x0214).
+--
+-- The position solution message reports absolute Earth Centered Earth Fixed
+-- (ECEF) coordinates and the status (single point vs pseudo-absolute RTK) of
+-- the position solution. The message also reports the upper triangular
+-- portion of the 3x3 covariance matrix. If the receiver knows the surveyed
+-- position of the base station and has an RTK solution, this reports a
+-- pseudo-absolute position solution using the base station position and the
+-- rover's RTK baseline vector. The full GPS time is given by the preceding
+-- MSG_GPS_TIME with the matching time-of-week (tow).
+data MsgPosEcefCov = MsgPosEcefCov
+  { _msgPosEcefCov_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosEcefCov_x     :: !Double
+    -- ^ ECEF X coordinate
+  , _msgPosEcefCov_y     :: !Double
+    -- ^ ECEF Y coordinate
+  , _msgPosEcefCov_z     :: !Double
+    -- ^ ECEF Z coordinate
+  , _msgPosEcefCov_cov_x_x :: !Float
+    -- ^ Estimated variance of x
+  , _msgPosEcefCov_cov_x_y :: !Float
+    -- ^ Estimated covariance of x and y
+  , _msgPosEcefCov_cov_x_z :: !Float
+    -- ^ Estimated covariance of x and z
+  , _msgPosEcefCov_cov_y_y :: !Float
+    -- ^ Estimated variance of y
+  , _msgPosEcefCov_cov_y_z :: !Float
+    -- ^ Estimated covariance of y and z
+  , _msgPosEcefCov_cov_z_z :: !Float
+    -- ^ Estimated variance of z
+  , _msgPosEcefCov_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgPosEcefCov_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosEcefCov where
+  get = do
+    _msgPosEcefCov_tow <- getWord32le
+    _msgPosEcefCov_x <- getFloat64le
+    _msgPosEcefCov_y <- getFloat64le
+    _msgPosEcefCov_z <- getFloat64le
+    _msgPosEcefCov_cov_x_x <- getFloat32le
+    _msgPosEcefCov_cov_x_y <- getFloat32le
+    _msgPosEcefCov_cov_x_z <- getFloat32le
+    _msgPosEcefCov_cov_y_y <- getFloat32le
+    _msgPosEcefCov_cov_y_z <- getFloat32le
+    _msgPosEcefCov_cov_z_z <- getFloat32le
+    _msgPosEcefCov_n_sats <- getWord8
+    _msgPosEcefCov_flags <- getWord8
+    pure MsgPosEcefCov {..}
+
+  put MsgPosEcefCov {..} = do
+    putWord32le _msgPosEcefCov_tow
+    putFloat64le _msgPosEcefCov_x
+    putFloat64le _msgPosEcefCov_y
+    putFloat64le _msgPosEcefCov_z
+    putFloat32le _msgPosEcefCov_cov_x_x
+    putFloat32le _msgPosEcefCov_cov_x_y
+    putFloat32le _msgPosEcefCov_cov_x_z
+    putFloat32le _msgPosEcefCov_cov_y_y
+    putFloat32le _msgPosEcefCov_cov_y_z
+    putFloat32le _msgPosEcefCov_cov_z_z
+    putWord8 _msgPosEcefCov_n_sats
+    putWord8 _msgPosEcefCov_flags
+
+$(makeSBP 'msgPosEcefCov ''MsgPosEcefCov)
+$(makeJSON "_msgPosEcefCov_" ''MsgPosEcefCov)
+$(makeLenses ''MsgPosEcefCov)
+
+msgPosLlh :: Word16
+msgPosLlh = 0x020A
+
+-- | SBP class for message MSG_POS_LLH (0x020A).
+--
+-- This position solution message reports the absolute geodetic coordinates
+-- and the status (single point vs pseudo-absolute RTK) of the position
+-- solution. If the rover receiver knows the surveyed position of the base
+-- station and has an RTK solution, this reports a pseudo-absolute position
+-- solution using the base station position and the rover's RTK baseline
+-- vector. The full GPS time is given by the preceding MSG_GPS_TIME with the
+-- matching time-of-week (tow).
+data MsgPosLlh = MsgPosLlh
+  { _msgPosLlh_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosLlh_lat      :: !Double
+    -- ^ Latitude
+  , _msgPosLlh_lon      :: !Double
+    -- ^ Longitude
+  , _msgPosLlh_height   :: !Double
+    -- ^ Height above WGS84 ellipsoid
+  , _msgPosLlh_h_accuracy :: !Word16
+    -- ^ Horizontal position estimated standard deviation
+  , _msgPosLlh_v_accuracy :: !Word16
+    -- ^ Vertical position estimated standard deviation
+  , _msgPosLlh_n_sats   :: !Word8
+    -- ^ Number of satellites used in solution.
+  , _msgPosLlh_flags    :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosLlh where
+  get = do
+    _msgPosLlh_tow <- getWord32le
+    _msgPosLlh_lat <- getFloat64le
+    _msgPosLlh_lon <- getFloat64le
+    _msgPosLlh_height <- getFloat64le
+    _msgPosLlh_h_accuracy <- getWord16le
+    _msgPosLlh_v_accuracy <- getWord16le
+    _msgPosLlh_n_sats <- getWord8
+    _msgPosLlh_flags <- getWord8
+    pure MsgPosLlh {..}
+
+  put MsgPosLlh {..} = do
+    putWord32le _msgPosLlh_tow
+    putFloat64le _msgPosLlh_lat
+    putFloat64le _msgPosLlh_lon
+    putFloat64le _msgPosLlh_height
+    putWord16le _msgPosLlh_h_accuracy
+    putWord16le _msgPosLlh_v_accuracy
+    putWord8 _msgPosLlh_n_sats
+    putWord8 _msgPosLlh_flags
+
+$(makeSBP 'msgPosLlh ''MsgPosLlh)
+$(makeJSON "_msgPosLlh_" ''MsgPosLlh)
+$(makeLenses ''MsgPosLlh)
+
+msgPosLlhCov :: Word16
+msgPosLlhCov = 0x0211
+
+-- | SBP class for message MSG_POS_LLH_COV (0x0211).
+--
+-- This position solution message reports the absolute geodetic coordinates
+-- and the status (single point vs pseudo-absolute RTK) of the position
+-- solution as well as the upper triangle of the 3x3 covariance matrix.  The
+-- position information and Fix Mode flags follow the MSG_POS_LLH message.
+-- Since the covariance matrix is computed in the local-level North, East,
+-- Down frame, the covariance terms follow that convention. Thus, covariances
+-- are reported against the "downward" measurement and care should be taken
+-- with the sign convention.
+data MsgPosLlhCov = MsgPosLlhCov
+  { _msgPosLlhCov_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosLlhCov_lat   :: !Double
+    -- ^ Latitude
+  , _msgPosLlhCov_lon   :: !Double
+    -- ^ Longitude
+  , _msgPosLlhCov_height :: !Double
+    -- ^ Height above WGS84 ellipsoid
+  , _msgPosLlhCov_cov_n_n :: !Float
+    -- ^ Estimated variance of northing
+  , _msgPosLlhCov_cov_n_e :: !Float
+    -- ^ Covariance of northing and easting
+  , _msgPosLlhCov_cov_n_d :: !Float
+    -- ^ Covariance of northing and downward measurement
+  , _msgPosLlhCov_cov_e_e :: !Float
+    -- ^ Estimated variance of easting
+  , _msgPosLlhCov_cov_e_d :: !Float
+    -- ^ Covariance of easting and downward measurement
+  , _msgPosLlhCov_cov_d_d :: !Float
+    -- ^ Estimated variance of downward measurement
+  , _msgPosLlhCov_n_sats :: !Word8
+    -- ^ Number of satellites used in solution.
+  , _msgPosLlhCov_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosLlhCov where
+  get = do
+    _msgPosLlhCov_tow <- getWord32le
+    _msgPosLlhCov_lat <- getFloat64le
+    _msgPosLlhCov_lon <- getFloat64le
+    _msgPosLlhCov_height <- getFloat64le
+    _msgPosLlhCov_cov_n_n <- getFloat32le
+    _msgPosLlhCov_cov_n_e <- getFloat32le
+    _msgPosLlhCov_cov_n_d <- getFloat32le
+    _msgPosLlhCov_cov_e_e <- getFloat32le
+    _msgPosLlhCov_cov_e_d <- getFloat32le
+    _msgPosLlhCov_cov_d_d <- getFloat32le
+    _msgPosLlhCov_n_sats <- getWord8
+    _msgPosLlhCov_flags <- getWord8
+    pure MsgPosLlhCov {..}
+
+  put MsgPosLlhCov {..} = do
+    putWord32le _msgPosLlhCov_tow
+    putFloat64le _msgPosLlhCov_lat
+    putFloat64le _msgPosLlhCov_lon
+    putFloat64le _msgPosLlhCov_height
+    putFloat32le _msgPosLlhCov_cov_n_n
+    putFloat32le _msgPosLlhCov_cov_n_e
+    putFloat32le _msgPosLlhCov_cov_n_d
+    putFloat32le _msgPosLlhCov_cov_e_e
+    putFloat32le _msgPosLlhCov_cov_e_d
+    putFloat32le _msgPosLlhCov_cov_d_d
+    putWord8 _msgPosLlhCov_n_sats
+    putWord8 _msgPosLlhCov_flags
+
+$(makeSBP 'msgPosLlhCov ''MsgPosLlhCov)
+$(makeJSON "_msgPosLlhCov_" ''MsgPosLlhCov)
+$(makeLenses ''MsgPosLlhCov)
+
+data EstimatedHorizontalErrorEllipse = EstimatedHorizontalErrorEllipse
+  { _estimatedHorizontalErrorEllipse_semi_major :: !Float
+    -- ^ The semi major axis of the estimated horizontal error ellipse at the
+    -- user-configured confidence level; zero implies invalid.
+  , _estimatedHorizontalErrorEllipse_semi_minor :: !Float
+    -- ^ The semi minor axis of the estimated horizontal error ellipse at the
+    -- user-configured confidence level; zero implies invalid.
+  , _estimatedHorizontalErrorEllipse_orientation :: !Float
+    -- ^ The orientation of the semi major axis of the estimated horizontal
+    -- error ellipse with respect to North.
+  } deriving ( Show, Read, Eq )
+
+instance Binary EstimatedHorizontalErrorEllipse where
+  get = do
+    _estimatedHorizontalErrorEllipse_semi_major <- getFloat32le
+    _estimatedHorizontalErrorEllipse_semi_minor <- getFloat32le
+    _estimatedHorizontalErrorEllipse_orientation <- getFloat32le
+    pure EstimatedHorizontalErrorEllipse {..}
+
+  put EstimatedHorizontalErrorEllipse {..} = do
+    putFloat32le _estimatedHorizontalErrorEllipse_semi_major
+    putFloat32le _estimatedHorizontalErrorEllipse_semi_minor
+    putFloat32le _estimatedHorizontalErrorEllipse_orientation
+
+$(makeJSON "_estimatedHorizontalErrorEllipse_" ''EstimatedHorizontalErrorEllipse)
+$(makeLenses ''EstimatedHorizontalErrorEllipse)
+
+msgPosLlhAcc :: Word16
+msgPosLlhAcc = 0x0218
+
+-- | SBP class for message MSG_POS_LLH_ACC (0x0218).
+--
+-- This position solution message reports the absolute geodetic coordinates
+-- and the status (single point vs pseudo-absolute RTK) of the position
+-- solution as well as the estimated horizontal, vertical, cross-track and
+-- along-track errors.  The position information and Fix Mode flags  follow
+-- the MSG_POS_LLH message. Since the covariance matrix is computed in the
+-- local-level North, East, Down frame, the estimated error terms follow that
+-- convention.
+--
+-- The estimated errors are reported at a user-configurable confidence level.
+-- The user-configured percentile is encoded in the percentile field.
+data MsgPosLlhAcc = MsgPosLlhAcc
+  { _msgPosLlhAcc_tow                :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosLlhAcc_lat                :: !Double
+    -- ^ Latitude
+  , _msgPosLlhAcc_lon                :: !Double
+    -- ^ Longitude
+  , _msgPosLlhAcc_height             :: !Double
+    -- ^ Height above WGS84 ellipsoid
+  , _msgPosLlhAcc_orthometric_height :: !Double
+    -- ^ Height above the geoid (i.e. height above mean sea level). See
+    -- confidence_and_geoid for geoid model used.
+  , _msgPosLlhAcc_h_accuracy         :: !Float
+    -- ^ Estimated horizontal error at the user-configured confidence level;
+    -- zero implies invalid.
+  , _msgPosLlhAcc_v_accuracy         :: !Float
+    -- ^ Estimated vertical error at the user-configured confidence level; zero
+    -- implies invalid.
+  , _msgPosLlhAcc_ct_accuracy        :: !Float
+    -- ^ Estimated cross-track error at the user-configured confidence level;
+    -- zero implies invalid.
+  , _msgPosLlhAcc_at_accuracy        :: !Float
+    -- ^ Estimated along-track error at the user-configured confidence level;
+    -- zero implies invalid.
+  , _msgPosLlhAcc_h_ellipse          :: !EstimatedHorizontalErrorEllipse
+    -- ^ The estimated horizontal error ellipse at the user-configured
+    -- confidence level.
+  , _msgPosLlhAcc_confidence_and_geoid :: !Word8
+    -- ^ The lower bits describe the configured confidence level for the
+    -- estimated position error. The middle bits describe the geoid model used
+    -- to calculate the orthometric height.
+  , _msgPosLlhAcc_n_sats             :: !Word8
+    -- ^ Number of satellites used in solution.
+  , _msgPosLlhAcc_flags              :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosLlhAcc where
+  get = do
+    _msgPosLlhAcc_tow <- getWord32le
+    _msgPosLlhAcc_lat <- getFloat64le
+    _msgPosLlhAcc_lon <- getFloat64le
+    _msgPosLlhAcc_height <- getFloat64le
+    _msgPosLlhAcc_orthometric_height <- getFloat64le
+    _msgPosLlhAcc_h_accuracy <- getFloat32le
+    _msgPosLlhAcc_v_accuracy <- getFloat32le
+    _msgPosLlhAcc_ct_accuracy <- getFloat32le
+    _msgPosLlhAcc_at_accuracy <- getFloat32le
+    _msgPosLlhAcc_h_ellipse <- get
+    _msgPosLlhAcc_confidence_and_geoid <- getWord8
+    _msgPosLlhAcc_n_sats <- getWord8
+    _msgPosLlhAcc_flags <- getWord8
+    pure MsgPosLlhAcc {..}
+
+  put MsgPosLlhAcc {..} = do
+    putWord32le _msgPosLlhAcc_tow
+    putFloat64le _msgPosLlhAcc_lat
+    putFloat64le _msgPosLlhAcc_lon
+    putFloat64le _msgPosLlhAcc_height
+    putFloat64le _msgPosLlhAcc_orthometric_height
+    putFloat32le _msgPosLlhAcc_h_accuracy
+    putFloat32le _msgPosLlhAcc_v_accuracy
+    putFloat32le _msgPosLlhAcc_ct_accuracy
+    putFloat32le _msgPosLlhAcc_at_accuracy
+    put _msgPosLlhAcc_h_ellipse
+    putWord8 _msgPosLlhAcc_confidence_and_geoid
+    putWord8 _msgPosLlhAcc_n_sats
+    putWord8 _msgPosLlhAcc_flags
+
+$(makeSBP 'msgPosLlhAcc ''MsgPosLlhAcc)
+$(makeJSON "_msgPosLlhAcc_" ''MsgPosLlhAcc)
+$(makeLenses ''MsgPosLlhAcc)
+
+msgBaselineEcef :: Word16
+msgBaselineEcef = 0x020B
+
+-- | SBP class for message MSG_BASELINE_ECEF (0x020B).
+--
+-- This message reports the baseline solution in Earth Centered Earth Fixed
+-- (ECEF) coordinates. This baseline is the relative vector distance from the
+-- base station to the rover receiver. The full GPS time is given by the
+-- preceding MSG_GPS_TIME with the matching time-of-week (tow).
+data MsgBaselineEcef = MsgBaselineEcef
+  { _msgBaselineEcef_tow    :: !Word32
+    -- ^ GPS Time of Week
+  , _msgBaselineEcef_x      :: !Int32
+    -- ^ Baseline ECEF X coordinate
+  , _msgBaselineEcef_y      :: !Int32
+    -- ^ Baseline ECEF Y coordinate
+  , _msgBaselineEcef_z      :: !Int32
+    -- ^ Baseline ECEF Z coordinate
+  , _msgBaselineEcef_accuracy :: !Word16
+    -- ^ Position estimated standard deviation
+  , _msgBaselineEcef_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgBaselineEcef_flags  :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgBaselineEcef where
+  get = do
+    _msgBaselineEcef_tow <- getWord32le
+    _msgBaselineEcef_x <- (fromIntegral <$> getWord32le)
+    _msgBaselineEcef_y <- (fromIntegral <$> getWord32le)
+    _msgBaselineEcef_z <- (fromIntegral <$> getWord32le)
+    _msgBaselineEcef_accuracy <- getWord16le
+    _msgBaselineEcef_n_sats <- getWord8
+    _msgBaselineEcef_flags <- getWord8
+    pure MsgBaselineEcef {..}
+
+  put MsgBaselineEcef {..} = do
+    putWord32le _msgBaselineEcef_tow
+    (putWord32le . fromIntegral) _msgBaselineEcef_x
+    (putWord32le . fromIntegral) _msgBaselineEcef_y
+    (putWord32le . fromIntegral) _msgBaselineEcef_z
+    putWord16le _msgBaselineEcef_accuracy
+    putWord8 _msgBaselineEcef_n_sats
+    putWord8 _msgBaselineEcef_flags
+
+$(makeSBP 'msgBaselineEcef ''MsgBaselineEcef)
+$(makeJSON "_msgBaselineEcef_" ''MsgBaselineEcef)
+$(makeLenses ''MsgBaselineEcef)
+
+msgBaselineNed :: Word16
+msgBaselineNed = 0x020C
+
+-- | SBP class for message MSG_BASELINE_NED (0x020C).
+--
+-- This message reports the baseline solution in North East Down (NED)
+-- coordinates. This baseline is the relative vector distance from the base
+-- station to the rover receiver, and NED coordinate system is defined at the
+-- local WGS84 tangent plane centered at the base station position.  The full
+-- GPS time is given by the preceding MSG_GPS_TIME with the matching time-of-
+-- week (tow).
+data MsgBaselineNed = MsgBaselineNed
+  { _msgBaselineNed_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgBaselineNed_n        :: !Int32
+    -- ^ Baseline North coordinate
+  , _msgBaselineNed_e        :: !Int32
+    -- ^ Baseline East coordinate
+  , _msgBaselineNed_d        :: !Int32
+    -- ^ Baseline Down coordinate
+  , _msgBaselineNed_h_accuracy :: !Word16
+    -- ^ Horizontal position estimated standard deviation
+  , _msgBaselineNed_v_accuracy :: !Word16
+    -- ^ Vertical position estimated standard deviation
+  , _msgBaselineNed_n_sats   :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgBaselineNed_flags    :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgBaselineNed where
+  get = do
+    _msgBaselineNed_tow <- getWord32le
+    _msgBaselineNed_n <- (fromIntegral <$> getWord32le)
+    _msgBaselineNed_e <- (fromIntegral <$> getWord32le)
+    _msgBaselineNed_d <- (fromIntegral <$> getWord32le)
+    _msgBaselineNed_h_accuracy <- getWord16le
+    _msgBaselineNed_v_accuracy <- getWord16le
+    _msgBaselineNed_n_sats <- getWord8
+    _msgBaselineNed_flags <- getWord8
+    pure MsgBaselineNed {..}
+
+  put MsgBaselineNed {..} = do
+    putWord32le _msgBaselineNed_tow
+    (putWord32le . fromIntegral) _msgBaselineNed_n
+    (putWord32le . fromIntegral) _msgBaselineNed_e
+    (putWord32le . fromIntegral) _msgBaselineNed_d
+    putWord16le _msgBaselineNed_h_accuracy
+    putWord16le _msgBaselineNed_v_accuracy
+    putWord8 _msgBaselineNed_n_sats
+    putWord8 _msgBaselineNed_flags
+
+$(makeSBP 'msgBaselineNed ''MsgBaselineNed)
+$(makeJSON "_msgBaselineNed_" ''MsgBaselineNed)
+$(makeLenses ''MsgBaselineNed)
+
+msgVelEcef :: Word16
+msgVelEcef = 0x020D
+
+-- | SBP class for message MSG_VEL_ECEF (0x020D).
+--
+-- This message reports the velocity in Earth Centered Earth Fixed (ECEF)
+-- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
+-- the matching time-of-week (tow).
+data MsgVelEcef = MsgVelEcef
+  { _msgVelEcef_tow    :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelEcef_x      :: !Int32
+    -- ^ Velocity ECEF X coordinate
+  , _msgVelEcef_y      :: !Int32
+    -- ^ Velocity ECEF Y coordinate
+  , _msgVelEcef_z      :: !Int32
+    -- ^ Velocity ECEF Z coordinate
+  , _msgVelEcef_accuracy :: !Word16
+    -- ^ Velocity estimated standard deviation
+  , _msgVelEcef_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelEcef_flags  :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelEcef where
+  get = do
+    _msgVelEcef_tow <- getWord32le
+    _msgVelEcef_x <- (fromIntegral <$> getWord32le)
+    _msgVelEcef_y <- (fromIntegral <$> getWord32le)
+    _msgVelEcef_z <- (fromIntegral <$> getWord32le)
+    _msgVelEcef_accuracy <- getWord16le
+    _msgVelEcef_n_sats <- getWord8
+    _msgVelEcef_flags <- getWord8
+    pure MsgVelEcef {..}
+
+  put MsgVelEcef {..} = do
+    putWord32le _msgVelEcef_tow
+    (putWord32le . fromIntegral) _msgVelEcef_x
+    (putWord32le . fromIntegral) _msgVelEcef_y
+    (putWord32le . fromIntegral) _msgVelEcef_z
+    putWord16le _msgVelEcef_accuracy
+    putWord8 _msgVelEcef_n_sats
+    putWord8 _msgVelEcef_flags
+
+$(makeSBP 'msgVelEcef ''MsgVelEcef)
+$(makeJSON "_msgVelEcef_" ''MsgVelEcef)
+$(makeLenses ''MsgVelEcef)
+
+msgVelEcefCov :: Word16
+msgVelEcefCov = 0x0215
+
+-- | SBP class for message MSG_VEL_ECEF_COV (0x0215).
+--
+-- This message reports the velocity in Earth Centered Earth Fixed (ECEF)
+-- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
+-- the matching time-of-week (tow).
+data MsgVelEcefCov = MsgVelEcefCov
+  { _msgVelEcefCov_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelEcefCov_x     :: !Int32
+    -- ^ Velocity ECEF X coordinate
+  , _msgVelEcefCov_y     :: !Int32
+    -- ^ Velocity ECEF Y coordinate
+  , _msgVelEcefCov_z     :: !Int32
+    -- ^ Velocity ECEF Z coordinate
+  , _msgVelEcefCov_cov_x_x :: !Float
+    -- ^ Estimated variance of x
+  , _msgVelEcefCov_cov_x_y :: !Float
+    -- ^ Estimated covariance of x and y
+  , _msgVelEcefCov_cov_x_z :: !Float
+    -- ^ Estimated covariance of x and z
+  , _msgVelEcefCov_cov_y_y :: !Float
+    -- ^ Estimated variance of y
+  , _msgVelEcefCov_cov_y_z :: !Float
+    -- ^ Estimated covariance of y and z
+  , _msgVelEcefCov_cov_z_z :: !Float
+    -- ^ Estimated variance of z
+  , _msgVelEcefCov_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelEcefCov_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelEcefCov where
+  get = do
+    _msgVelEcefCov_tow <- getWord32le
+    _msgVelEcefCov_x <- (fromIntegral <$> getWord32le)
+    _msgVelEcefCov_y <- (fromIntegral <$> getWord32le)
+    _msgVelEcefCov_z <- (fromIntegral <$> getWord32le)
+    _msgVelEcefCov_cov_x_x <- getFloat32le
+    _msgVelEcefCov_cov_x_y <- getFloat32le
+    _msgVelEcefCov_cov_x_z <- getFloat32le
+    _msgVelEcefCov_cov_y_y <- getFloat32le
+    _msgVelEcefCov_cov_y_z <- getFloat32le
+    _msgVelEcefCov_cov_z_z <- getFloat32le
+    _msgVelEcefCov_n_sats <- getWord8
+    _msgVelEcefCov_flags <- getWord8
+    pure MsgVelEcefCov {..}
+
+  put MsgVelEcefCov {..} = do
+    putWord32le _msgVelEcefCov_tow
+    (putWord32le . fromIntegral) _msgVelEcefCov_x
+    (putWord32le . fromIntegral) _msgVelEcefCov_y
+    (putWord32le . fromIntegral) _msgVelEcefCov_z
+    putFloat32le _msgVelEcefCov_cov_x_x
+    putFloat32le _msgVelEcefCov_cov_x_y
+    putFloat32le _msgVelEcefCov_cov_x_z
+    putFloat32le _msgVelEcefCov_cov_y_y
+    putFloat32le _msgVelEcefCov_cov_y_z
+    putFloat32le _msgVelEcefCov_cov_z_z
+    putWord8 _msgVelEcefCov_n_sats
+    putWord8 _msgVelEcefCov_flags
+
+$(makeSBP 'msgVelEcefCov ''MsgVelEcefCov)
+$(makeJSON "_msgVelEcefCov_" ''MsgVelEcefCov)
+$(makeLenses ''MsgVelEcefCov)
+
+msgVelNed :: Word16
+msgVelNed = 0x020E
+
+-- | SBP class for message MSG_VEL_NED (0x020E).
+--
+-- This message reports the velocity in local North East Down (NED)
+-- coordinates. 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).
+data MsgVelNed = MsgVelNed
+  { _msgVelNed_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelNed_n        :: !Int32
+    -- ^ Velocity North coordinate
+  , _msgVelNed_e        :: !Int32
+    -- ^ Velocity East coordinate
+  , _msgVelNed_d        :: !Int32
+    -- ^ Velocity Down coordinate
+  , _msgVelNed_h_accuracy :: !Word16
+    -- ^ Horizontal velocity estimated standard deviation
+  , _msgVelNed_v_accuracy :: !Word16
+    -- ^ Vertical velocity estimated standard deviation
+  , _msgVelNed_n_sats   :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelNed_flags    :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelNed where
+  get = do
+    _msgVelNed_tow <- getWord32le
+    _msgVelNed_n <- (fromIntegral <$> getWord32le)
+    _msgVelNed_e <- (fromIntegral <$> getWord32le)
+    _msgVelNed_d <- (fromIntegral <$> getWord32le)
+    _msgVelNed_h_accuracy <- getWord16le
+    _msgVelNed_v_accuracy <- getWord16le
+    _msgVelNed_n_sats <- getWord8
+    _msgVelNed_flags <- getWord8
+    pure MsgVelNed {..}
+
+  put MsgVelNed {..} = do
+    putWord32le _msgVelNed_tow
+    (putWord32le . fromIntegral) _msgVelNed_n
+    (putWord32le . fromIntegral) _msgVelNed_e
+    (putWord32le . fromIntegral) _msgVelNed_d
+    putWord16le _msgVelNed_h_accuracy
+    putWord16le _msgVelNed_v_accuracy
+    putWord8 _msgVelNed_n_sats
+    putWord8 _msgVelNed_flags
+
+$(makeSBP 'msgVelNed ''MsgVelNed)
+$(makeJSON "_msgVelNed_" ''MsgVelNed)
+$(makeLenses ''MsgVelNed)
+
+msgVelNedCov :: Word16
+msgVelNedCov = 0x0212
+
+-- | SBP class for message MSG_VEL_NED_COV (0x0212).
+--
+-- This message reports the velocity in local North East Down (NED)
+-- coordinates. 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). This
+-- message is similar to the MSG_VEL_NED, but it includes the upper triangular
+-- portion of the 3x3 covariance matrix.
+data MsgVelNedCov = MsgVelNedCov
+  { _msgVelNedCov_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelNedCov_n     :: !Int32
+    -- ^ Velocity North coordinate
+  , _msgVelNedCov_e     :: !Int32
+    -- ^ Velocity East coordinate
+  , _msgVelNedCov_d     :: !Int32
+    -- ^ Velocity Down coordinate
+  , _msgVelNedCov_cov_n_n :: !Float
+    -- ^ Estimated variance of northward measurement
+  , _msgVelNedCov_cov_n_e :: !Float
+    -- ^ Covariance of northward and eastward measurement
+  , _msgVelNedCov_cov_n_d :: !Float
+    -- ^ Covariance of northward and downward measurement
+  , _msgVelNedCov_cov_e_e :: !Float
+    -- ^ Estimated variance of eastward measurement
+  , _msgVelNedCov_cov_e_d :: !Float
+    -- ^ Covariance of eastward and downward measurement
+  , _msgVelNedCov_cov_d_d :: !Float
+    -- ^ Estimated variance of downward measurement
+  , _msgVelNedCov_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelNedCov_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelNedCov where
+  get = do
+    _msgVelNedCov_tow <- getWord32le
+    _msgVelNedCov_n <- (fromIntegral <$> getWord32le)
+    _msgVelNedCov_e <- (fromIntegral <$> getWord32le)
+    _msgVelNedCov_d <- (fromIntegral <$> getWord32le)
+    _msgVelNedCov_cov_n_n <- getFloat32le
+    _msgVelNedCov_cov_n_e <- getFloat32le
+    _msgVelNedCov_cov_n_d <- getFloat32le
+    _msgVelNedCov_cov_e_e <- getFloat32le
+    _msgVelNedCov_cov_e_d <- getFloat32le
+    _msgVelNedCov_cov_d_d <- getFloat32le
+    _msgVelNedCov_n_sats <- getWord8
+    _msgVelNedCov_flags <- getWord8
+    pure MsgVelNedCov {..}
+
+  put MsgVelNedCov {..} = do
+    putWord32le _msgVelNedCov_tow
+    (putWord32le . fromIntegral) _msgVelNedCov_n
+    (putWord32le . fromIntegral) _msgVelNedCov_e
+    (putWord32le . fromIntegral) _msgVelNedCov_d
+    putFloat32le _msgVelNedCov_cov_n_n
+    putFloat32le _msgVelNedCov_cov_n_e
+    putFloat32le _msgVelNedCov_cov_n_d
+    putFloat32le _msgVelNedCov_cov_e_e
+    putFloat32le _msgVelNedCov_cov_e_d
+    putFloat32le _msgVelNedCov_cov_d_d
+    putWord8 _msgVelNedCov_n_sats
+    putWord8 _msgVelNedCov_flags
+
+$(makeSBP 'msgVelNedCov ''MsgVelNedCov)
+$(makeJSON "_msgVelNedCov_" ''MsgVelNedCov)
+$(makeLenses ''MsgVelNedCov)
+
+msgPosEcefGnss :: Word16
+msgPosEcefGnss = 0x0229
+
+-- | SBP class for message MSG_POS_ECEF_GNSS (0x0229).
+--
+-- The position solution message reports absolute Earth Centered Earth Fixed
+-- (ECEF) coordinates and the status (single point vs pseudo-absolute RTK) of
+-- the position solution. If the rover receiver knows the surveyed position of
+-- the base station and has an RTK solution, this reports a pseudo-absolute
+-- position solution using the base station position and the rover's RTK
+-- baseline vector. The full GPS time is given by the preceding MSG_GPS_TIME
+-- with the matching time-of-week (tow).
+data MsgPosEcefGnss = MsgPosEcefGnss
+  { _msgPosEcefGnss_tow    :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosEcefGnss_x      :: !Double
+    -- ^ ECEF X coordinate
+  , _msgPosEcefGnss_y      :: !Double
+    -- ^ ECEF Y coordinate
+  , _msgPosEcefGnss_z      :: !Double
+    -- ^ ECEF Z coordinate
+  , _msgPosEcefGnss_accuracy :: !Word16
+    -- ^ Position estimated standard deviation
+  , _msgPosEcefGnss_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgPosEcefGnss_flags  :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosEcefGnss where
+  get = do
+    _msgPosEcefGnss_tow <- getWord32le
+    _msgPosEcefGnss_x <- getFloat64le
+    _msgPosEcefGnss_y <- getFloat64le
+    _msgPosEcefGnss_z <- getFloat64le
+    _msgPosEcefGnss_accuracy <- getWord16le
+    _msgPosEcefGnss_n_sats <- getWord8
+    _msgPosEcefGnss_flags <- getWord8
+    pure MsgPosEcefGnss {..}
+
+  put MsgPosEcefGnss {..} = do
+    putWord32le _msgPosEcefGnss_tow
+    putFloat64le _msgPosEcefGnss_x
+    putFloat64le _msgPosEcefGnss_y
+    putFloat64le _msgPosEcefGnss_z
+    putWord16le _msgPosEcefGnss_accuracy
+    putWord8 _msgPosEcefGnss_n_sats
+    putWord8 _msgPosEcefGnss_flags
+
+$(makeSBP 'msgPosEcefGnss ''MsgPosEcefGnss)
+$(makeJSON "_msgPosEcefGnss_" ''MsgPosEcefGnss)
+$(makeLenses ''MsgPosEcefGnss)
+
+msgPosEcefCovGnss :: Word16
+msgPosEcefCovGnss = 0x0234
+
+-- | SBP class for message MSG_POS_ECEF_COV_GNSS (0x0234).
+--
+-- The position solution message reports absolute Earth Centered Earth Fixed
+-- (ECEF) coordinates and the status (single point vs pseudo-absolute RTK) of
+-- the position solution. The message also reports the upper triangular
+-- portion of the 3x3 covariance matrix. If the receiver knows the surveyed
+-- position of the base station and has an RTK solution, this reports a
+-- pseudo-absolute position solution using the base station position and the
+-- rover's RTK baseline vector. The full GPS time is given by the preceding
+-- MSG_GPS_TIME with the matching time-of-week (tow).
+data MsgPosEcefCovGnss = MsgPosEcefCovGnss
+  { _msgPosEcefCovGnss_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosEcefCovGnss_x     :: !Double
+    -- ^ ECEF X coordinate
+  , _msgPosEcefCovGnss_y     :: !Double
+    -- ^ ECEF Y coordinate
+  , _msgPosEcefCovGnss_z     :: !Double
+    -- ^ ECEF Z coordinate
+  , _msgPosEcefCovGnss_cov_x_x :: !Float
+    -- ^ Estimated variance of x
+  , _msgPosEcefCovGnss_cov_x_y :: !Float
+    -- ^ Estimated covariance of x and y
+  , _msgPosEcefCovGnss_cov_x_z :: !Float
+    -- ^ Estimated covariance of x and z
+  , _msgPosEcefCovGnss_cov_y_y :: !Float
+    -- ^ Estimated variance of y
+  , _msgPosEcefCovGnss_cov_y_z :: !Float
+    -- ^ Estimated covariance of y and z
+  , _msgPosEcefCovGnss_cov_z_z :: !Float
+    -- ^ Estimated variance of z
+  , _msgPosEcefCovGnss_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgPosEcefCovGnss_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosEcefCovGnss where
+  get = do
+    _msgPosEcefCovGnss_tow <- getWord32le
+    _msgPosEcefCovGnss_x <- getFloat64le
+    _msgPosEcefCovGnss_y <- getFloat64le
+    _msgPosEcefCovGnss_z <- getFloat64le
+    _msgPosEcefCovGnss_cov_x_x <- getFloat32le
+    _msgPosEcefCovGnss_cov_x_y <- getFloat32le
+    _msgPosEcefCovGnss_cov_x_z <- getFloat32le
+    _msgPosEcefCovGnss_cov_y_y <- getFloat32le
+    _msgPosEcefCovGnss_cov_y_z <- getFloat32le
+    _msgPosEcefCovGnss_cov_z_z <- getFloat32le
+    _msgPosEcefCovGnss_n_sats <- getWord8
+    _msgPosEcefCovGnss_flags <- getWord8
+    pure MsgPosEcefCovGnss {..}
+
+  put MsgPosEcefCovGnss {..} = do
+    putWord32le _msgPosEcefCovGnss_tow
+    putFloat64le _msgPosEcefCovGnss_x
+    putFloat64le _msgPosEcefCovGnss_y
+    putFloat64le _msgPosEcefCovGnss_z
+    putFloat32le _msgPosEcefCovGnss_cov_x_x
+    putFloat32le _msgPosEcefCovGnss_cov_x_y
+    putFloat32le _msgPosEcefCovGnss_cov_x_z
+    putFloat32le _msgPosEcefCovGnss_cov_y_y
+    putFloat32le _msgPosEcefCovGnss_cov_y_z
+    putFloat32le _msgPosEcefCovGnss_cov_z_z
+    putWord8 _msgPosEcefCovGnss_n_sats
+    putWord8 _msgPosEcefCovGnss_flags
+
+$(makeSBP 'msgPosEcefCovGnss ''MsgPosEcefCovGnss)
+$(makeJSON "_msgPosEcefCovGnss_" ''MsgPosEcefCovGnss)
+$(makeLenses ''MsgPosEcefCovGnss)
+
+msgPosLlhGnss :: Word16
+msgPosLlhGnss = 0x022A
+
+-- | SBP class for message MSG_POS_LLH_GNSS (0x022A).
+--
+-- This position solution message reports the absolute geodetic coordinates
+-- and the status (single point vs pseudo-absolute RTK) of the position
+-- solution. If the rover receiver knows the surveyed position of the base
+-- station and has an RTK solution, this reports a pseudo-absolute position
+-- solution using the base station position and the rover's RTK baseline
+-- vector. The full GPS time is given by the preceding MSG_GPS_TIME with the
+-- matching time-of-week (tow).
+data MsgPosLlhGnss = MsgPosLlhGnss
+  { _msgPosLlhGnss_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosLlhGnss_lat      :: !Double
+    -- ^ Latitude
+  , _msgPosLlhGnss_lon      :: !Double
+    -- ^ Longitude
+  , _msgPosLlhGnss_height   :: !Double
+    -- ^ Height above WGS84 ellipsoid
+  , _msgPosLlhGnss_h_accuracy :: !Word16
+    -- ^ Horizontal position estimated standard deviation
+  , _msgPosLlhGnss_v_accuracy :: !Word16
+    -- ^ Vertical position estimated standard deviation
+  , _msgPosLlhGnss_n_sats   :: !Word8
+    -- ^ Number of satellites used in solution.
+  , _msgPosLlhGnss_flags    :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosLlhGnss where
+  get = do
+    _msgPosLlhGnss_tow <- getWord32le
+    _msgPosLlhGnss_lat <- getFloat64le
+    _msgPosLlhGnss_lon <- getFloat64le
+    _msgPosLlhGnss_height <- getFloat64le
+    _msgPosLlhGnss_h_accuracy <- getWord16le
+    _msgPosLlhGnss_v_accuracy <- getWord16le
+    _msgPosLlhGnss_n_sats <- getWord8
+    _msgPosLlhGnss_flags <- getWord8
+    pure MsgPosLlhGnss {..}
+
+  put MsgPosLlhGnss {..} = do
+    putWord32le _msgPosLlhGnss_tow
+    putFloat64le _msgPosLlhGnss_lat
+    putFloat64le _msgPosLlhGnss_lon
+    putFloat64le _msgPosLlhGnss_height
+    putWord16le _msgPosLlhGnss_h_accuracy
+    putWord16le _msgPosLlhGnss_v_accuracy
+    putWord8 _msgPosLlhGnss_n_sats
+    putWord8 _msgPosLlhGnss_flags
+
+$(makeSBP 'msgPosLlhGnss ''MsgPosLlhGnss)
+$(makeJSON "_msgPosLlhGnss_" ''MsgPosLlhGnss)
+$(makeLenses ''MsgPosLlhGnss)
+
+msgPosLlhCovGnss :: Word16
+msgPosLlhCovGnss = 0x0231
+
+-- | SBP class for message MSG_POS_LLH_COV_GNSS (0x0231).
+--
+-- This position solution message reports the absolute geodetic coordinates
+-- and the status (single point vs pseudo-absolute RTK) of the position
+-- solution as well as the upper triangle of the 3x3 covariance matrix.  The
+-- position information and Fix Mode flags should follow the MSG_POS_LLH
+-- message.  Since the covariance matrix is computed in the local-level North,
+-- East, Down frame, the covariance terms follow with that convention. Thus,
+-- covariances are reported against the "downward" measurement and care should
+-- be taken with the sign convention.
+data MsgPosLlhCovGnss = MsgPosLlhCovGnss
+  { _msgPosLlhCovGnss_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosLlhCovGnss_lat   :: !Double
+    -- ^ Latitude
+  , _msgPosLlhCovGnss_lon   :: !Double
+    -- ^ Longitude
+  , _msgPosLlhCovGnss_height :: !Double
+    -- ^ Height above WGS84 ellipsoid
+  , _msgPosLlhCovGnss_cov_n_n :: !Float
+    -- ^ Estimated variance of northing
+  , _msgPosLlhCovGnss_cov_n_e :: !Float
+    -- ^ Covariance of northing and easting
+  , _msgPosLlhCovGnss_cov_n_d :: !Float
+    -- ^ Covariance of northing and downward measurement
+  , _msgPosLlhCovGnss_cov_e_e :: !Float
+    -- ^ Estimated variance of easting
+  , _msgPosLlhCovGnss_cov_e_d :: !Float
+    -- ^ Covariance of easting and downward measurement
+  , _msgPosLlhCovGnss_cov_d_d :: !Float
+    -- ^ Estimated variance of downward measurement
+  , _msgPosLlhCovGnss_n_sats :: !Word8
+    -- ^ Number of satellites used in solution.
+  , _msgPosLlhCovGnss_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosLlhCovGnss where
+  get = do
+    _msgPosLlhCovGnss_tow <- getWord32le
+    _msgPosLlhCovGnss_lat <- getFloat64le
+    _msgPosLlhCovGnss_lon <- getFloat64le
+    _msgPosLlhCovGnss_height <- getFloat64le
+    _msgPosLlhCovGnss_cov_n_n <- getFloat32le
+    _msgPosLlhCovGnss_cov_n_e <- getFloat32le
+    _msgPosLlhCovGnss_cov_n_d <- getFloat32le
+    _msgPosLlhCovGnss_cov_e_e <- getFloat32le
+    _msgPosLlhCovGnss_cov_e_d <- getFloat32le
+    _msgPosLlhCovGnss_cov_d_d <- getFloat32le
+    _msgPosLlhCovGnss_n_sats <- getWord8
+    _msgPosLlhCovGnss_flags <- getWord8
+    pure MsgPosLlhCovGnss {..}
+
+  put MsgPosLlhCovGnss {..} = do
+    putWord32le _msgPosLlhCovGnss_tow
+    putFloat64le _msgPosLlhCovGnss_lat
+    putFloat64le _msgPosLlhCovGnss_lon
+    putFloat64le _msgPosLlhCovGnss_height
+    putFloat32le _msgPosLlhCovGnss_cov_n_n
+    putFloat32le _msgPosLlhCovGnss_cov_n_e
+    putFloat32le _msgPosLlhCovGnss_cov_n_d
+    putFloat32le _msgPosLlhCovGnss_cov_e_e
+    putFloat32le _msgPosLlhCovGnss_cov_e_d
+    putFloat32le _msgPosLlhCovGnss_cov_d_d
+    putWord8 _msgPosLlhCovGnss_n_sats
+    putWord8 _msgPosLlhCovGnss_flags
+
+$(makeSBP 'msgPosLlhCovGnss ''MsgPosLlhCovGnss)
+$(makeJSON "_msgPosLlhCovGnss_" ''MsgPosLlhCovGnss)
+$(makeLenses ''MsgPosLlhCovGnss)
+
+msgVelEcefGnss :: Word16
+msgVelEcefGnss = 0x022D
+
+-- | SBP class for message MSG_VEL_ECEF_GNSS (0x022D).
+--
+-- This message reports the velocity in Earth Centered Earth Fixed (ECEF)
+-- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
+-- the matching time-of-week (tow).
+data MsgVelEcefGnss = MsgVelEcefGnss
+  { _msgVelEcefGnss_tow    :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelEcefGnss_x      :: !Int32
+    -- ^ Velocity ECEF X coordinate
+  , _msgVelEcefGnss_y      :: !Int32
+    -- ^ Velocity ECEF Y coordinate
+  , _msgVelEcefGnss_z      :: !Int32
+    -- ^ Velocity ECEF Z coordinate
+  , _msgVelEcefGnss_accuracy :: !Word16
+    -- ^ Velocity estimated standard deviation
+  , _msgVelEcefGnss_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelEcefGnss_flags  :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelEcefGnss where
+  get = do
+    _msgVelEcefGnss_tow <- getWord32le
+    _msgVelEcefGnss_x <- (fromIntegral <$> getWord32le)
+    _msgVelEcefGnss_y <- (fromIntegral <$> getWord32le)
+    _msgVelEcefGnss_z <- (fromIntegral <$> getWord32le)
+    _msgVelEcefGnss_accuracy <- getWord16le
+    _msgVelEcefGnss_n_sats <- getWord8
+    _msgVelEcefGnss_flags <- getWord8
+    pure MsgVelEcefGnss {..}
+
+  put MsgVelEcefGnss {..} = do
+    putWord32le _msgVelEcefGnss_tow
+    (putWord32le . fromIntegral) _msgVelEcefGnss_x
+    (putWord32le . fromIntegral) _msgVelEcefGnss_y
+    (putWord32le . fromIntegral) _msgVelEcefGnss_z
+    putWord16le _msgVelEcefGnss_accuracy
+    putWord8 _msgVelEcefGnss_n_sats
+    putWord8 _msgVelEcefGnss_flags
+
+$(makeSBP 'msgVelEcefGnss ''MsgVelEcefGnss)
+$(makeJSON "_msgVelEcefGnss_" ''MsgVelEcefGnss)
+$(makeLenses ''MsgVelEcefGnss)
+
+msgVelEcefCovGnss :: Word16
+msgVelEcefCovGnss = 0x0235
+
+-- | SBP class for message MSG_VEL_ECEF_COV_GNSS (0x0235).
+--
+-- This message reports the velocity in Earth Centered Earth Fixed (ECEF)
+-- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
+-- the matching time-of-week (tow).
+data MsgVelEcefCovGnss = MsgVelEcefCovGnss
+  { _msgVelEcefCovGnss_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelEcefCovGnss_x     :: !Int32
+    -- ^ Velocity ECEF X coordinate
+  , _msgVelEcefCovGnss_y     :: !Int32
+    -- ^ Velocity ECEF Y coordinate
+  , _msgVelEcefCovGnss_z     :: !Int32
+    -- ^ Velocity ECEF Z coordinate
+  , _msgVelEcefCovGnss_cov_x_x :: !Float
+    -- ^ Estimated variance of x
+  , _msgVelEcefCovGnss_cov_x_y :: !Float
+    -- ^ Estimated covariance of x and y
+  , _msgVelEcefCovGnss_cov_x_z :: !Float
+    -- ^ Estimated covariance of x and z
+  , _msgVelEcefCovGnss_cov_y_y :: !Float
+    -- ^ Estimated variance of y
+  , _msgVelEcefCovGnss_cov_y_z :: !Float
+    -- ^ Estimated covariance of y and z
+  , _msgVelEcefCovGnss_cov_z_z :: !Float
+    -- ^ Estimated variance of z
+  , _msgVelEcefCovGnss_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelEcefCovGnss_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelEcefCovGnss where
+  get = do
+    _msgVelEcefCovGnss_tow <- getWord32le
+    _msgVelEcefCovGnss_x <- (fromIntegral <$> getWord32le)
+    _msgVelEcefCovGnss_y <- (fromIntegral <$> getWord32le)
+    _msgVelEcefCovGnss_z <- (fromIntegral <$> getWord32le)
+    _msgVelEcefCovGnss_cov_x_x <- getFloat32le
+    _msgVelEcefCovGnss_cov_x_y <- getFloat32le
+    _msgVelEcefCovGnss_cov_x_z <- getFloat32le
+    _msgVelEcefCovGnss_cov_y_y <- getFloat32le
+    _msgVelEcefCovGnss_cov_y_z <- getFloat32le
+    _msgVelEcefCovGnss_cov_z_z <- getFloat32le
+    _msgVelEcefCovGnss_n_sats <- getWord8
+    _msgVelEcefCovGnss_flags <- getWord8
+    pure MsgVelEcefCovGnss {..}
+
+  put MsgVelEcefCovGnss {..} = do
+    putWord32le _msgVelEcefCovGnss_tow
+    (putWord32le . fromIntegral) _msgVelEcefCovGnss_x
+    (putWord32le . fromIntegral) _msgVelEcefCovGnss_y
+    (putWord32le . fromIntegral) _msgVelEcefCovGnss_z
+    putFloat32le _msgVelEcefCovGnss_cov_x_x
+    putFloat32le _msgVelEcefCovGnss_cov_x_y
+    putFloat32le _msgVelEcefCovGnss_cov_x_z
+    putFloat32le _msgVelEcefCovGnss_cov_y_y
+    putFloat32le _msgVelEcefCovGnss_cov_y_z
+    putFloat32le _msgVelEcefCovGnss_cov_z_z
+    putWord8 _msgVelEcefCovGnss_n_sats
+    putWord8 _msgVelEcefCovGnss_flags
+
+$(makeSBP 'msgVelEcefCovGnss ''MsgVelEcefCovGnss)
+$(makeJSON "_msgVelEcefCovGnss_" ''MsgVelEcefCovGnss)
+$(makeLenses ''MsgVelEcefCovGnss)
+
+msgVelNedGnss :: Word16
+msgVelNedGnss = 0x022E
+
+-- | SBP class for message MSG_VEL_NED_GNSS (0x022E).
+--
+-- This message reports the velocity in local North East Down (NED)
+-- coordinates. 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).
+data MsgVelNedGnss = MsgVelNedGnss
+  { _msgVelNedGnss_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelNedGnss_n        :: !Int32
+    -- ^ Velocity North coordinate
+  , _msgVelNedGnss_e        :: !Int32
+    -- ^ Velocity East coordinate
+  , _msgVelNedGnss_d        :: !Int32
+    -- ^ Velocity Down coordinate
+  , _msgVelNedGnss_h_accuracy :: !Word16
+    -- ^ Horizontal velocity estimated standard deviation
+  , _msgVelNedGnss_v_accuracy :: !Word16
+    -- ^ Vertical velocity estimated standard deviation
+  , _msgVelNedGnss_n_sats   :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelNedGnss_flags    :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelNedGnss where
+  get = do
+    _msgVelNedGnss_tow <- getWord32le
+    _msgVelNedGnss_n <- (fromIntegral <$> getWord32le)
+    _msgVelNedGnss_e <- (fromIntegral <$> getWord32le)
+    _msgVelNedGnss_d <- (fromIntegral <$> getWord32le)
+    _msgVelNedGnss_h_accuracy <- getWord16le
+    _msgVelNedGnss_v_accuracy <- getWord16le
+    _msgVelNedGnss_n_sats <- getWord8
+    _msgVelNedGnss_flags <- getWord8
+    pure MsgVelNedGnss {..}
+
+  put MsgVelNedGnss {..} = do
+    putWord32le _msgVelNedGnss_tow
+    (putWord32le . fromIntegral) _msgVelNedGnss_n
+    (putWord32le . fromIntegral) _msgVelNedGnss_e
+    (putWord32le . fromIntegral) _msgVelNedGnss_d
+    putWord16le _msgVelNedGnss_h_accuracy
+    putWord16le _msgVelNedGnss_v_accuracy
+    putWord8 _msgVelNedGnss_n_sats
+    putWord8 _msgVelNedGnss_flags
+
+$(makeSBP 'msgVelNedGnss ''MsgVelNedGnss)
+$(makeJSON "_msgVelNedGnss_" ''MsgVelNedGnss)
+$(makeLenses ''MsgVelNedGnss)
+
+msgVelNedCovGnss :: Word16
+msgVelNedCovGnss = 0x0232
+
+-- | SBP class for message MSG_VEL_NED_COV_GNSS (0x0232).
+--
+-- This message reports the velocity in local North East Down (NED)
+-- coordinates. 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). This
+-- message is similar to the MSG_VEL_NED, but it includes the upper triangular
+-- portion of the 3x3 covariance matrix.
+data MsgVelNedCovGnss = MsgVelNedCovGnss
+  { _msgVelNedCovGnss_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelNedCovGnss_n     :: !Int32
+    -- ^ Velocity North coordinate
+  , _msgVelNedCovGnss_e     :: !Int32
+    -- ^ Velocity East coordinate
+  , _msgVelNedCovGnss_d     :: !Int32
+    -- ^ Velocity Down coordinate
+  , _msgVelNedCovGnss_cov_n_n :: !Float
+    -- ^ Estimated variance of northward measurement
+  , _msgVelNedCovGnss_cov_n_e :: !Float
+    -- ^ Covariance of northward and eastward measurement
+  , _msgVelNedCovGnss_cov_n_d :: !Float
+    -- ^ Covariance of northward and downward measurement
+  , _msgVelNedCovGnss_cov_e_e :: !Float
+    -- ^ Estimated variance of eastward measurement
+  , _msgVelNedCovGnss_cov_e_d :: !Float
+    -- ^ Covariance of eastward and downward measurement
+  , _msgVelNedCovGnss_cov_d_d :: !Float
+    -- ^ Estimated variance of downward measurement
+  , _msgVelNedCovGnss_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelNedCovGnss_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelNedCovGnss where
+  get = do
+    _msgVelNedCovGnss_tow <- getWord32le
+    _msgVelNedCovGnss_n <- (fromIntegral <$> getWord32le)
+    _msgVelNedCovGnss_e <- (fromIntegral <$> getWord32le)
+    _msgVelNedCovGnss_d <- (fromIntegral <$> getWord32le)
+    _msgVelNedCovGnss_cov_n_n <- getFloat32le
+    _msgVelNedCovGnss_cov_n_e <- getFloat32le
+    _msgVelNedCovGnss_cov_n_d <- getFloat32le
+    _msgVelNedCovGnss_cov_e_e <- getFloat32le
+    _msgVelNedCovGnss_cov_e_d <- getFloat32le
+    _msgVelNedCovGnss_cov_d_d <- getFloat32le
+    _msgVelNedCovGnss_n_sats <- getWord8
+    _msgVelNedCovGnss_flags <- getWord8
+    pure MsgVelNedCovGnss {..}
+
+  put MsgVelNedCovGnss {..} = do
+    putWord32le _msgVelNedCovGnss_tow
+    (putWord32le . fromIntegral) _msgVelNedCovGnss_n
+    (putWord32le . fromIntegral) _msgVelNedCovGnss_e
+    (putWord32le . fromIntegral) _msgVelNedCovGnss_d
+    putFloat32le _msgVelNedCovGnss_cov_n_n
+    putFloat32le _msgVelNedCovGnss_cov_n_e
+    putFloat32le _msgVelNedCovGnss_cov_n_d
+    putFloat32le _msgVelNedCovGnss_cov_e_e
+    putFloat32le _msgVelNedCovGnss_cov_e_d
+    putFloat32le _msgVelNedCovGnss_cov_d_d
+    putWord8 _msgVelNedCovGnss_n_sats
+    putWord8 _msgVelNedCovGnss_flags
+
+$(makeSBP 'msgVelNedCovGnss ''MsgVelNedCovGnss)
+$(makeJSON "_msgVelNedCovGnss_" ''MsgVelNedCovGnss)
+$(makeLenses ''MsgVelNedCovGnss)
+
+msgVelBody :: Word16
+msgVelBody = 0x0213
+
+-- | SBP class for message MSG_VEL_BODY (0x0213).
+--
+-- This message reports the velocity in the Vehicle Body Frame. By convention,
+-- the x-axis should point out the nose of the vehicle and represent the
+-- forward direction, while as the y-axis should point out the right hand side
+-- of the vehicle. Since this is a right handed system, z should point out the
+-- bottom of the vehicle. The orientation and origin of the Vehicle Body Frame
+-- are specified via the device settings. The full GPS time is given by the
+-- preceding MSG_GPS_TIME with the matching time-of-week (tow). This message
+-- is only produced by inertial versions of Swift products and is not
+-- available from Piksi Multi or Duro.
+data MsgVelBody = MsgVelBody
+  { _msgVelBody_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelBody_x     :: !Int32
+    -- ^ Velocity in x direction
+  , _msgVelBody_y     :: !Int32
+    -- ^ Velocity in y direction
+  , _msgVelBody_z     :: !Int32
+    -- ^ Velocity in z direction
+  , _msgVelBody_cov_x_x :: !Float
+    -- ^ Estimated variance of x
+  , _msgVelBody_cov_x_y :: !Float
+    -- ^ Covariance of x and y
+  , _msgVelBody_cov_x_z :: !Float
+    -- ^ Covariance of x and z
+  , _msgVelBody_cov_y_y :: !Float
+    -- ^ Estimated variance of y
+  , _msgVelBody_cov_y_z :: !Float
+    -- ^ Covariance of y and z
+  , _msgVelBody_cov_z_z :: !Float
+    -- ^ Estimated variance of z
+  , _msgVelBody_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelBody_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelBody where
+  get = do
+    _msgVelBody_tow <- getWord32le
+    _msgVelBody_x <- (fromIntegral <$> getWord32le)
+    _msgVelBody_y <- (fromIntegral <$> getWord32le)
+    _msgVelBody_z <- (fromIntegral <$> getWord32le)
+    _msgVelBody_cov_x_x <- getFloat32le
+    _msgVelBody_cov_x_y <- getFloat32le
+    _msgVelBody_cov_x_z <- getFloat32le
+    _msgVelBody_cov_y_y <- getFloat32le
+    _msgVelBody_cov_y_z <- getFloat32le
+    _msgVelBody_cov_z_z <- getFloat32le
+    _msgVelBody_n_sats <- getWord8
+    _msgVelBody_flags <- getWord8
+    pure MsgVelBody {..}
+
+  put MsgVelBody {..} = do
+    putWord32le _msgVelBody_tow
+    (putWord32le . fromIntegral) _msgVelBody_x
+    (putWord32le . fromIntegral) _msgVelBody_y
+    (putWord32le . fromIntegral) _msgVelBody_z
+    putFloat32le _msgVelBody_cov_x_x
+    putFloat32le _msgVelBody_cov_x_y
+    putFloat32le _msgVelBody_cov_x_z
+    putFloat32le _msgVelBody_cov_y_y
+    putFloat32le _msgVelBody_cov_y_z
+    putFloat32le _msgVelBody_cov_z_z
+    putWord8 _msgVelBody_n_sats
+    putWord8 _msgVelBody_flags
+
+$(makeSBP 'msgVelBody ''MsgVelBody)
+$(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
+
+-- | SBP class for message MSG_AGE_CORRECTIONS (0x0210).
+--
+-- This message reports the Age of the corrections used for the current
+-- Differential solution.
+data MsgAgeCorrections = MsgAgeCorrections
+  { _msgAgeCorrections_tow :: !Word32
+    -- ^ GPS Time of Week
+  , _msgAgeCorrections_age :: !Word16
+    -- ^ Age of the corrections (0xFFFF indicates invalid)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgAgeCorrections where
+  get = do
+    _msgAgeCorrections_tow <- getWord32le
+    _msgAgeCorrections_age <- getWord16le
+    pure MsgAgeCorrections {..}
+
+  put MsgAgeCorrections {..} = do
+    putWord32le _msgAgeCorrections_tow
+    putWord16le _msgAgeCorrections_age
+
+$(makeSBP 'msgAgeCorrections ''MsgAgeCorrections)
+$(makeJSON "_msgAgeCorrections_" ''MsgAgeCorrections)
+$(makeLenses ''MsgAgeCorrections)
+
+msgGpsTimeDepA :: Word16
+msgGpsTimeDepA = 0x0100
+
+-- | SBP class for message MSG_GPS_TIME_DEP_A (0x0100).
+--
+-- This message reports the GPS time, representing the time since the GPS
+-- epoch began on midnight January 6, 1980 UTC. GPS time counts the weeks and
+-- seconds of the week. The weeks begin at the Saturday/Sunday transition. GPS
+-- week 0 began at the beginning of the GPS time scale.
+--
+-- Within each week number, the GPS time of the week is between between 0 and
+-- 604800 seconds (=60*60*24*7). Note that GPS time does not accumulate leap
+-- seconds, and as of now, has a small offset from UTC. In a message stream,
+-- this message precedes a set of other navigation messages referenced to the
+-- same time (but lacking the ns field) and indicates a more precise time of
+-- these messages.
+data MsgGpsTimeDepA = MsgGpsTimeDepA
+  { _msgGpsTimeDepA_wn        :: !Word16
+    -- ^ GPS week number
+  , _msgGpsTimeDepA_tow       :: !Word32
+    -- ^ GPS time of week rounded to the nearest millisecond
+  , _msgGpsTimeDepA_ns_residual :: !Int32
+    -- ^ Nanosecond residual of millisecond-rounded TOW (ranges from -500000 to
+    -- 500000)
+  , _msgGpsTimeDepA_flags     :: !Word8
+    -- ^ Status flags (reserved)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgGpsTimeDepA where
+  get = do
+    _msgGpsTimeDepA_wn <- getWord16le
+    _msgGpsTimeDepA_tow <- getWord32le
+    _msgGpsTimeDepA_ns_residual <- (fromIntegral <$> getWord32le)
+    _msgGpsTimeDepA_flags <- getWord8
+    pure MsgGpsTimeDepA {..}
+
+  put MsgGpsTimeDepA {..} = do
+    putWord16le _msgGpsTimeDepA_wn
+    putWord32le _msgGpsTimeDepA_tow
+    (putWord32le . fromIntegral) _msgGpsTimeDepA_ns_residual
+    putWord8 _msgGpsTimeDepA_flags
+
+$(makeSBP 'msgGpsTimeDepA ''MsgGpsTimeDepA)
+$(makeJSON "_msgGpsTimeDepA_" ''MsgGpsTimeDepA)
+$(makeLenses ''MsgGpsTimeDepA)
+
+msgDopsDepA :: Word16
+msgDopsDepA = 0x0206
+
+-- | SBP class for message MSG_DOPS_DEP_A (0x0206).
+--
+-- This dilution of precision (DOP) message describes the effect of navigation
+-- satellite geometry on positional measurement precision.
+data MsgDopsDepA = MsgDopsDepA
+  { _msgDopsDepA_tow :: !Word32
+    -- ^ GPS Time of Week
+  , _msgDopsDepA_gdop :: !Word16
+    -- ^ Geometric Dilution of Precision
+  , _msgDopsDepA_pdop :: !Word16
+    -- ^ Position Dilution of Precision
+  , _msgDopsDepA_tdop :: !Word16
+    -- ^ Time Dilution of Precision
+  , _msgDopsDepA_hdop :: !Word16
+    -- ^ Horizontal Dilution of Precision
+  , _msgDopsDepA_vdop :: !Word16
+    -- ^ Vertical Dilution of Precision
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgDopsDepA where
+  get = do
+    _msgDopsDepA_tow <- getWord32le
+    _msgDopsDepA_gdop <- getWord16le
+    _msgDopsDepA_pdop <- getWord16le
+    _msgDopsDepA_tdop <- getWord16le
+    _msgDopsDepA_hdop <- getWord16le
+    _msgDopsDepA_vdop <- getWord16le
+    pure MsgDopsDepA {..}
+
+  put MsgDopsDepA {..} = do
+    putWord32le _msgDopsDepA_tow
+    putWord16le _msgDopsDepA_gdop
+    putWord16le _msgDopsDepA_pdop
+    putWord16le _msgDopsDepA_tdop
+    putWord16le _msgDopsDepA_hdop
+    putWord16le _msgDopsDepA_vdop
+
+$(makeSBP 'msgDopsDepA ''MsgDopsDepA)
+$(makeJSON "_msgDopsDepA_" ''MsgDopsDepA)
+$(makeLenses ''MsgDopsDepA)
+
+msgPosEcefDepA :: Word16
+msgPosEcefDepA = 0x0200
+
+-- | SBP class for message MSG_POS_ECEF_DEP_A (0x0200).
+--
+-- The position solution message reports absolute Earth Centered Earth Fixed
+-- (ECEF) coordinates and the status (single point vs pseudo-absolute RTK) of
+-- the position solution. If the rover receiver knows the surveyed position of
+-- the base station and has an RTK solution, this reports a pseudo-absolute
+-- position solution using the base station position and the rover's RTK
+-- baseline vector. The full GPS time is given by the preceding MSG_GPS_TIME
+-- with the matching time-of-week (tow).
+data MsgPosEcefDepA = MsgPosEcefDepA
+  { _msgPosEcefDepA_tow    :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosEcefDepA_x      :: !Double
+    -- ^ ECEF X coordinate
+  , _msgPosEcefDepA_y      :: !Double
+    -- ^ ECEF Y coordinate
+  , _msgPosEcefDepA_z      :: !Double
+    -- ^ ECEF Z coordinate
+  , _msgPosEcefDepA_accuracy :: !Word16
+    -- ^ Position accuracy estimate (not implemented). Defaults to 0.
+  , _msgPosEcefDepA_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgPosEcefDepA_flags  :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosEcefDepA where
+  get = do
+    _msgPosEcefDepA_tow <- getWord32le
+    _msgPosEcefDepA_x <- getFloat64le
+    _msgPosEcefDepA_y <- getFloat64le
+    _msgPosEcefDepA_z <- getFloat64le
+    _msgPosEcefDepA_accuracy <- getWord16le
+    _msgPosEcefDepA_n_sats <- getWord8
+    _msgPosEcefDepA_flags <- getWord8
+    pure MsgPosEcefDepA {..}
+
+  put MsgPosEcefDepA {..} = do
+    putWord32le _msgPosEcefDepA_tow
+    putFloat64le _msgPosEcefDepA_x
+    putFloat64le _msgPosEcefDepA_y
+    putFloat64le _msgPosEcefDepA_z
+    putWord16le _msgPosEcefDepA_accuracy
+    putWord8 _msgPosEcefDepA_n_sats
+    putWord8 _msgPosEcefDepA_flags
+
+$(makeSBP 'msgPosEcefDepA ''MsgPosEcefDepA)
+$(makeJSON "_msgPosEcefDepA_" ''MsgPosEcefDepA)
+$(makeLenses ''MsgPosEcefDepA)
+
+msgPosLlhDepA :: Word16
+msgPosLlhDepA = 0x0201
+
+-- | SBP class for message MSG_POS_LLH_DEP_A (0x0201).
+--
+-- This position solution message reports the absolute geodetic coordinates
+-- and the status (single point vs pseudo-absolute RTK) of the position
+-- solution. If the rover receiver knows the surveyed position of the base
+-- station and has an RTK solution, this reports a pseudo-absolute position
+-- solution using the base station position and the rover's RTK baseline
+-- vector. The full GPS time is given by the preceding MSG_GPS_TIME with the
+-- matching time-of-week (tow).
+data MsgPosLlhDepA = MsgPosLlhDepA
+  { _msgPosLlhDepA_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgPosLlhDepA_lat      :: !Double
+    -- ^ Latitude
+  , _msgPosLlhDepA_lon      :: !Double
+    -- ^ Longitude
+  , _msgPosLlhDepA_height   :: !Double
+    -- ^ Height
+  , _msgPosLlhDepA_h_accuracy :: !Word16
+    -- ^ Horizontal position accuracy estimate (not implemented). Defaults to 0.
+  , _msgPosLlhDepA_v_accuracy :: !Word16
+    -- ^ Vertical position accuracy estimate (not implemented). Defaults to 0.
+  , _msgPosLlhDepA_n_sats   :: !Word8
+    -- ^ Number of satellites used in solution.
+  , _msgPosLlhDepA_flags    :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPosLlhDepA where
+  get = do
+    _msgPosLlhDepA_tow <- getWord32le
+    _msgPosLlhDepA_lat <- getFloat64le
+    _msgPosLlhDepA_lon <- getFloat64le
+    _msgPosLlhDepA_height <- getFloat64le
+    _msgPosLlhDepA_h_accuracy <- getWord16le
+    _msgPosLlhDepA_v_accuracy <- getWord16le
+    _msgPosLlhDepA_n_sats <- getWord8
+    _msgPosLlhDepA_flags <- getWord8
+    pure MsgPosLlhDepA {..}
+
+  put MsgPosLlhDepA {..} = do
+    putWord32le _msgPosLlhDepA_tow
+    putFloat64le _msgPosLlhDepA_lat
+    putFloat64le _msgPosLlhDepA_lon
+    putFloat64le _msgPosLlhDepA_height
+    putWord16le _msgPosLlhDepA_h_accuracy
+    putWord16le _msgPosLlhDepA_v_accuracy
+    putWord8 _msgPosLlhDepA_n_sats
+    putWord8 _msgPosLlhDepA_flags
+
+$(makeSBP 'msgPosLlhDepA ''MsgPosLlhDepA)
+$(makeJSON "_msgPosLlhDepA_" ''MsgPosLlhDepA)
+$(makeLenses ''MsgPosLlhDepA)
+
+msgBaselineEcefDepA :: Word16
+msgBaselineEcefDepA = 0x0202
+
+-- | SBP class for message MSG_BASELINE_ECEF_DEP_A (0x0202).
+--
+-- This message reports the baseline solution in Earth Centered Earth Fixed
+-- (ECEF) coordinates. This baseline is the relative vector distance from the
+-- base station to the rover receiver. The full GPS time is given by the
+-- preceding MSG_GPS_TIME with the matching time-of-week (tow).
+data MsgBaselineEcefDepA = MsgBaselineEcefDepA
+  { _msgBaselineEcefDepA_tow    :: !Word32
+    -- ^ GPS Time of Week
+  , _msgBaselineEcefDepA_x      :: !Int32
+    -- ^ Baseline ECEF X coordinate
+  , _msgBaselineEcefDepA_y      :: !Int32
+    -- ^ Baseline ECEF Y coordinate
+  , _msgBaselineEcefDepA_z      :: !Int32
+    -- ^ Baseline ECEF Z coordinate
+  , _msgBaselineEcefDepA_accuracy :: !Word16
+    -- ^ Position accuracy estimate
+  , _msgBaselineEcefDepA_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgBaselineEcefDepA_flags  :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgBaselineEcefDepA where
+  get = do
+    _msgBaselineEcefDepA_tow <- getWord32le
+    _msgBaselineEcefDepA_x <- (fromIntegral <$> getWord32le)
+    _msgBaselineEcefDepA_y <- (fromIntegral <$> getWord32le)
+    _msgBaselineEcefDepA_z <- (fromIntegral <$> getWord32le)
+    _msgBaselineEcefDepA_accuracy <- getWord16le
+    _msgBaselineEcefDepA_n_sats <- getWord8
+    _msgBaselineEcefDepA_flags <- getWord8
+    pure MsgBaselineEcefDepA {..}
+
+  put MsgBaselineEcefDepA {..} = do
+    putWord32le _msgBaselineEcefDepA_tow
+    (putWord32le . fromIntegral) _msgBaselineEcefDepA_x
+    (putWord32le . fromIntegral) _msgBaselineEcefDepA_y
+    (putWord32le . fromIntegral) _msgBaselineEcefDepA_z
+    putWord16le _msgBaselineEcefDepA_accuracy
+    putWord8 _msgBaselineEcefDepA_n_sats
+    putWord8 _msgBaselineEcefDepA_flags
+
+$(makeSBP 'msgBaselineEcefDepA ''MsgBaselineEcefDepA)
+$(makeJSON "_msgBaselineEcefDepA_" ''MsgBaselineEcefDepA)
+$(makeLenses ''MsgBaselineEcefDepA)
+
+msgBaselineNedDepA :: Word16
+msgBaselineNedDepA = 0x0203
+
+-- | SBP class for message MSG_BASELINE_NED_DEP_A (0x0203).
+--
+-- This message reports the baseline solution in North East Down (NED)
+-- coordinates. This baseline is the relative vector distance from the base
+-- station to the rover receiver, and NED coordinate system is defined at the
+-- local WGS84 tangent plane centered at the base station position.  The full
+-- GPS time is given by the preceding MSG_GPS_TIME with the matching time-of-
+-- week (tow).
+data MsgBaselineNedDepA = MsgBaselineNedDepA
+  { _msgBaselineNedDepA_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgBaselineNedDepA_n        :: !Int32
+    -- ^ Baseline North coordinate
+  , _msgBaselineNedDepA_e        :: !Int32
+    -- ^ Baseline East coordinate
+  , _msgBaselineNedDepA_d        :: !Int32
+    -- ^ Baseline Down coordinate
+  , _msgBaselineNedDepA_h_accuracy :: !Word16
+    -- ^ Horizontal position accuracy estimate (not implemented). Defaults to 0.
+  , _msgBaselineNedDepA_v_accuracy :: !Word16
+    -- ^ Vertical position accuracy estimate (not implemented). Defaults to 0.
+  , _msgBaselineNedDepA_n_sats   :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgBaselineNedDepA_flags    :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgBaselineNedDepA where
+  get = do
+    _msgBaselineNedDepA_tow <- getWord32le
+    _msgBaselineNedDepA_n <- (fromIntegral <$> getWord32le)
+    _msgBaselineNedDepA_e <- (fromIntegral <$> getWord32le)
+    _msgBaselineNedDepA_d <- (fromIntegral <$> getWord32le)
+    _msgBaselineNedDepA_h_accuracy <- getWord16le
+    _msgBaselineNedDepA_v_accuracy <- getWord16le
+    _msgBaselineNedDepA_n_sats <- getWord8
+    _msgBaselineNedDepA_flags <- getWord8
+    pure MsgBaselineNedDepA {..}
+
+  put MsgBaselineNedDepA {..} = do
+    putWord32le _msgBaselineNedDepA_tow
+    (putWord32le . fromIntegral) _msgBaselineNedDepA_n
+    (putWord32le . fromIntegral) _msgBaselineNedDepA_e
+    (putWord32le . fromIntegral) _msgBaselineNedDepA_d
+    putWord16le _msgBaselineNedDepA_h_accuracy
+    putWord16le _msgBaselineNedDepA_v_accuracy
+    putWord8 _msgBaselineNedDepA_n_sats
+    putWord8 _msgBaselineNedDepA_flags
+
+$(makeSBP 'msgBaselineNedDepA ''MsgBaselineNedDepA)
+$(makeJSON "_msgBaselineNedDepA_" ''MsgBaselineNedDepA)
+$(makeLenses ''MsgBaselineNedDepA)
+
+msgVelEcefDepA :: Word16
+msgVelEcefDepA = 0x0204
+
+-- | SBP class for message MSG_VEL_ECEF_DEP_A (0x0204).
+--
+-- This message reports the velocity in Earth Centered Earth Fixed (ECEF)
+-- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
+-- the matching time-of-week (tow).
+data MsgVelEcefDepA = MsgVelEcefDepA
+  { _msgVelEcefDepA_tow    :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelEcefDepA_x      :: !Int32
+    -- ^ Velocity ECEF X coordinate
+  , _msgVelEcefDepA_y      :: !Int32
+    -- ^ Velocity ECEF Y coordinate
+  , _msgVelEcefDepA_z      :: !Int32
+    -- ^ Velocity ECEF Z coordinate
+  , _msgVelEcefDepA_accuracy :: !Word16
+    -- ^ Velocity accuracy estimate (not implemented). Defaults to 0.
+  , _msgVelEcefDepA_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelEcefDepA_flags  :: !Word8
+    -- ^ Status flags (reserved)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelEcefDepA where
+  get = do
+    _msgVelEcefDepA_tow <- getWord32le
+    _msgVelEcefDepA_x <- (fromIntegral <$> getWord32le)
+    _msgVelEcefDepA_y <- (fromIntegral <$> getWord32le)
+    _msgVelEcefDepA_z <- (fromIntegral <$> getWord32le)
+    _msgVelEcefDepA_accuracy <- getWord16le
+    _msgVelEcefDepA_n_sats <- getWord8
+    _msgVelEcefDepA_flags <- getWord8
+    pure MsgVelEcefDepA {..}
+
+  put MsgVelEcefDepA {..} = do
+    putWord32le _msgVelEcefDepA_tow
+    (putWord32le . fromIntegral) _msgVelEcefDepA_x
+    (putWord32le . fromIntegral) _msgVelEcefDepA_y
+    (putWord32le . fromIntegral) _msgVelEcefDepA_z
+    putWord16le _msgVelEcefDepA_accuracy
+    putWord8 _msgVelEcefDepA_n_sats
+    putWord8 _msgVelEcefDepA_flags
+
+$(makeSBP 'msgVelEcefDepA ''MsgVelEcefDepA)
+$(makeJSON "_msgVelEcefDepA_" ''MsgVelEcefDepA)
+$(makeLenses ''MsgVelEcefDepA)
+
+msgVelNedDepA :: Word16
+msgVelNedDepA = 0x0205
+
+-- | SBP class for message MSG_VEL_NED_DEP_A (0x0205).
+--
+-- This message reports the velocity in local North East Down (NED)
+-- coordinates. 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).
+data MsgVelNedDepA = MsgVelNedDepA
+  { _msgVelNedDepA_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgVelNedDepA_n        :: !Int32
+    -- ^ Velocity North coordinate
+  , _msgVelNedDepA_e        :: !Int32
+    -- ^ Velocity East coordinate
+  , _msgVelNedDepA_d        :: !Int32
+    -- ^ Velocity Down coordinate
+  , _msgVelNedDepA_h_accuracy :: !Word16
+    -- ^ Horizontal velocity accuracy estimate (not implemented). Defaults to 0.
+  , _msgVelNedDepA_v_accuracy :: !Word16
+    -- ^ Vertical velocity accuracy estimate (not implemented). Defaults to 0.
+  , _msgVelNedDepA_n_sats   :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgVelNedDepA_flags    :: !Word8
+    -- ^ Status flags (reserved)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgVelNedDepA where
+  get = do
+    _msgVelNedDepA_tow <- getWord32le
+    _msgVelNedDepA_n <- (fromIntegral <$> getWord32le)
+    _msgVelNedDepA_e <- (fromIntegral <$> getWord32le)
+    _msgVelNedDepA_d <- (fromIntegral <$> getWord32le)
+    _msgVelNedDepA_h_accuracy <- getWord16le
+    _msgVelNedDepA_v_accuracy <- getWord16le
+    _msgVelNedDepA_n_sats <- getWord8
+    _msgVelNedDepA_flags <- getWord8
+    pure MsgVelNedDepA {..}
+
+  put MsgVelNedDepA {..} = do
+    putWord32le _msgVelNedDepA_tow
+    (putWord32le . fromIntegral) _msgVelNedDepA_n
+    (putWord32le . fromIntegral) _msgVelNedDepA_e
+    (putWord32le . fromIntegral) _msgVelNedDepA_d
+    putWord16le _msgVelNedDepA_h_accuracy
+    putWord16le _msgVelNedDepA_v_accuracy
+    putWord8 _msgVelNedDepA_n_sats
+    putWord8 _msgVelNedDepA_flags
+
+$(makeSBP 'msgVelNedDepA ''MsgVelNedDepA)
+$(makeJSON "_msgVelNedDepA_" ''MsgVelNedDepA)
+$(makeLenses ''MsgVelNedDepA)
+
+msgBaselineHeadingDepA :: Word16
+msgBaselineHeadingDepA = 0x0207
+
+-- | SBP class for message MSG_BASELINE_HEADING_DEP_A (0x0207).
+--
+-- This message reports the baseline heading pointing from the base station to
+-- the rover relative to True North. The full GPS time is given by the
+-- preceding MSG_GPS_TIME with the matching time-of-week (tow).
+data MsgBaselineHeadingDepA = MsgBaselineHeadingDepA
+  { _msgBaselineHeadingDepA_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgBaselineHeadingDepA_heading :: !Word32
+    -- ^ Heading
+  , _msgBaselineHeadingDepA_n_sats :: !Word8
+    -- ^ Number of satellites used in solution
+  , _msgBaselineHeadingDepA_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgBaselineHeadingDepA where
+  get = do
+    _msgBaselineHeadingDepA_tow <- getWord32le
+    _msgBaselineHeadingDepA_heading <- getWord32le
+    _msgBaselineHeadingDepA_n_sats <- getWord8
+    _msgBaselineHeadingDepA_flags <- getWord8
+    pure MsgBaselineHeadingDepA {..}
+
+  put MsgBaselineHeadingDepA {..} = do
+    putWord32le _msgBaselineHeadingDepA_tow
+    putWord32le _msgBaselineHeadingDepA_heading
+    putWord8 _msgBaselineHeadingDepA_n_sats
+    putWord8 _msgBaselineHeadingDepA_flags
+
+$(makeSBP 'msgBaselineHeadingDepA ''MsgBaselineHeadingDepA)
+$(makeJSON "_msgBaselineHeadingDepA_" ''MsgBaselineHeadingDepA)
+$(makeLenses ''MsgBaselineHeadingDepA)
+
+msgProtectionLevelDepA :: Word16
+msgProtectionLevelDepA = 0x0216
+
+-- | SBP class for message MSG_PROTECTION_LEVEL_DEP_A (0x0216).
+--
+-- This message reports the local vertical and horizontal protection levels
+-- associated with a given LLH position solution. The full GPS time is given
+-- by the preceding MSG_GPS_TIME with the matching time-of-week (tow).
+data MsgProtectionLevelDepA = MsgProtectionLevelDepA
+  { _msgProtectionLevelDepA_tow  :: !Word32
+    -- ^ GPS Time of Week
+  , _msgProtectionLevelDepA_vpl  :: !Word16
+    -- ^ Vertical protection level
+  , _msgProtectionLevelDepA_hpl  :: !Word16
+    -- ^ Horizontal protection level
+  , _msgProtectionLevelDepA_lat  :: !Double
+    -- ^ Latitude
+  , _msgProtectionLevelDepA_lon  :: !Double
+    -- ^ Longitude
+  , _msgProtectionLevelDepA_height :: !Double
+    -- ^ Height
+  , _msgProtectionLevelDepA_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgProtectionLevelDepA where
+  get = do
+    _msgProtectionLevelDepA_tow <- getWord32le
+    _msgProtectionLevelDepA_vpl <- getWord16le
+    _msgProtectionLevelDepA_hpl <- getWord16le
+    _msgProtectionLevelDepA_lat <- getFloat64le
+    _msgProtectionLevelDepA_lon <- getFloat64le
+    _msgProtectionLevelDepA_height <- getFloat64le
+    _msgProtectionLevelDepA_flags <- getWord8
+    pure MsgProtectionLevelDepA {..}
+
+  put MsgProtectionLevelDepA {..} = do
+    putWord32le _msgProtectionLevelDepA_tow
+    putWord16le _msgProtectionLevelDepA_vpl
+    putWord16le _msgProtectionLevelDepA_hpl
+    putFloat64le _msgProtectionLevelDepA_lat
+    putFloat64le _msgProtectionLevelDepA_lon
+    putFloat64le _msgProtectionLevelDepA_height
+    putWord8 _msgProtectionLevelDepA_flags
+
+$(makeSBP 'msgProtectionLevelDepA ''MsgProtectionLevelDepA)
+$(makeJSON "_msgProtectionLevelDepA_" ''MsgProtectionLevelDepA)
+$(makeLenses ''MsgProtectionLevelDepA)
+
+msgProtectionLevel :: Word16
+msgProtectionLevel = 0x0217
+
+-- | SBP class for message MSG_PROTECTION_LEVEL (0x0217).
+--
+-- This message reports the protection levels associated to the given state
+-- estimate. The full GPS time is given by the preceding MSG_GPS_TIME with the
+-- matching time-of-week (tow).
+data MsgProtectionLevel = MsgProtectionLevel
+  { _msgProtectionLevel_tow   :: !Word32
+    -- ^ GPS Time of Week
+  , _msgProtectionLevel_wn    :: !Int16
+    -- ^ GPS week number
+  , _msgProtectionLevel_hpl   :: !Word16
+    -- ^ Horizontal protection level
+  , _msgProtectionLevel_vpl   :: !Word16
+    -- ^ Vertical protection level
+  , _msgProtectionLevel_atpl  :: !Word16
+    -- ^ Along-track position error protection level
+  , _msgProtectionLevel_ctpl  :: !Word16
+    -- ^ Cross-track position error protection level
+  , _msgProtectionLevel_hvpl  :: !Word16
+    -- ^ Protection level for the error vector between estimated and true
+    -- along/cross track velocity vector
+  , _msgProtectionLevel_vvpl  :: !Word16
+    -- ^ Protection level for the velocity in vehicle upright direction
+    -- (different from vertical direction if on a slope)
+  , _msgProtectionLevel_hopl  :: !Word16
+    -- ^ Heading orientation protection level
+  , _msgProtectionLevel_popl  :: !Word16
+    -- ^ Pitch orientation protection level
+  , _msgProtectionLevel_ropl  :: !Word16
+    -- ^ Roll orientation protection level
+  , _msgProtectionLevel_lat   :: !Double
+    -- ^ Latitude
+  , _msgProtectionLevel_lon   :: !Double
+    -- ^ Longitude
+  , _msgProtectionLevel_height :: !Double
+    -- ^ Height
+  , _msgProtectionLevel_v_x   :: !Int32
+    -- ^ Velocity in vehicle x direction
+  , _msgProtectionLevel_v_y   :: !Int32
+    -- ^ Velocity in vehicle y direction
+  , _msgProtectionLevel_v_z   :: !Int32
+    -- ^ Velocity in vehicle z direction
+  , _msgProtectionLevel_roll  :: !Int32
+    -- ^ Roll angle
+  , _msgProtectionLevel_pitch :: !Int32
+    -- ^ Pitch angle
+  , _msgProtectionLevel_heading :: !Int32
+    -- ^ Heading angle
+  , _msgProtectionLevel_flags :: !Word32
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgProtectionLevel where
+  get = do
+    _msgProtectionLevel_tow <- getWord32le
+    _msgProtectionLevel_wn <- (fromIntegral <$> getWord16le)
+    _msgProtectionLevel_hpl <- getWord16le
+    _msgProtectionLevel_vpl <- getWord16le
+    _msgProtectionLevel_atpl <- getWord16le
+    _msgProtectionLevel_ctpl <- getWord16le
+    _msgProtectionLevel_hvpl <- getWord16le
+    _msgProtectionLevel_vvpl <- getWord16le
+    _msgProtectionLevel_hopl <- getWord16le
+    _msgProtectionLevel_popl <- getWord16le
+    _msgProtectionLevel_ropl <- getWord16le
+    _msgProtectionLevel_lat <- getFloat64le
+    _msgProtectionLevel_lon <- getFloat64le
+    _msgProtectionLevel_height <- getFloat64le
+    _msgProtectionLevel_v_x <- (fromIntegral <$> getWord32le)
+    _msgProtectionLevel_v_y <- (fromIntegral <$> getWord32le)
+    _msgProtectionLevel_v_z <- (fromIntegral <$> getWord32le)
+    _msgProtectionLevel_roll <- (fromIntegral <$> getWord32le)
+    _msgProtectionLevel_pitch <- (fromIntegral <$> getWord32le)
+    _msgProtectionLevel_heading <- (fromIntegral <$> getWord32le)
+    _msgProtectionLevel_flags <- getWord32le
+    pure MsgProtectionLevel {..}
+
+  put MsgProtectionLevel {..} = do
+    putWord32le _msgProtectionLevel_tow
+    (putWord16le . fromIntegral) _msgProtectionLevel_wn
+    putWord16le _msgProtectionLevel_hpl
+    putWord16le _msgProtectionLevel_vpl
+    putWord16le _msgProtectionLevel_atpl
+    putWord16le _msgProtectionLevel_ctpl
+    putWord16le _msgProtectionLevel_hvpl
+    putWord16le _msgProtectionLevel_vvpl
+    putWord16le _msgProtectionLevel_hopl
+    putWord16le _msgProtectionLevel_popl
+    putWord16le _msgProtectionLevel_ropl
+    putFloat64le _msgProtectionLevel_lat
+    putFloat64le _msgProtectionLevel_lon
+    putFloat64le _msgProtectionLevel_height
+    (putWord32le . fromIntegral) _msgProtectionLevel_v_x
+    (putWord32le . fromIntegral) _msgProtectionLevel_v_y
+    (putWord32le . fromIntegral) _msgProtectionLevel_v_z
+    (putWord32le . fromIntegral) _msgProtectionLevel_roll
+    (putWord32le . fromIntegral) _msgProtectionLevel_pitch
+    (putWord32le . fromIntegral) _msgProtectionLevel_heading
+    putWord32le _msgProtectionLevel_flags
+
+$(makeSBP 'msgProtectionLevel ''MsgProtectionLevel)
+$(makeJSON "_msgProtectionLevel_" ''MsgProtectionLevel)
+$(makeLenses ''MsgProtectionLevel)
diff --git a/src/SwiftNav/SBP/Ndb.hs b/src/SwiftNav/SBP/Ndb.hs
--- a/src/SwiftNav/SBP/Ndb.hs
+++ b/src/SwiftNav/SBP/Ndb.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Ndb
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages for logging NDB events.
+-- \< Messages for logging NDB events. \>
 
 module SwiftNav.SBP.Ndb
   ( module SwiftNav.SBP.Ndb
diff --git a/src/SwiftNav/SBP/Observation.hs b/src/SwiftNav/SBP/Observation.hs
--- a/src/SwiftNav/SBP/Observation.hs
+++ b/src/SwiftNav/SBP/Observation.hs
@@ -6,12 +6,14 @@
 -- |
 -- Module:      SwiftNav.SBP.Observation
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Satellite observation messages from the device.
+-- \< Satellite observation messages from the device. The SBP sender ID of 0
+-- indicates remote observations from a GNSS base station, correction network,
+-- or Skylark, Swift's cloud GNSS correction product. \>
 
 module SwiftNav.SBP.Observation
   ( module SwiftNav.SBP.Observation
@@ -74,7 +76,7 @@
 
 instance Binary Doppler where
   get = do
-    _doppler_i <- fromIntegral <$> getWord16le
+    _doppler_i <- (fromIntegral <$> getWord16le)
     _doppler_f <- getWord8
     pure Doppler {..}
 
@@ -87,11 +89,13 @@
 
 -- | PackedObsContent.
 --
--- Pseudorange and carrier phase observation for a satellite being tracked. The
--- observations are interoperable with 3rd party receivers and conform with
--- typical RTCM 3.1 message GPS/GLO observations.  Carrier phase observations
--- are not guaranteed to be aligned to the RINEX 3 or RTCM 3.3 MSM reference
--- signal and no 1/4 cycle adjustments are currently peformed.
+-- Pseudorange and carrier phase observation for a satellite being tracked.
+-- The observations are interoperable with 3rd party receivers and conform
+-- with typical RTCM 3.1 message GPS/GLO observations.
+--
+-- Carrier phase observations are not guaranteed to be aligned to the RINEX 3
+-- or RTCM 3.3 MSM reference signal and no 1/4 cycle adjustments are currently
+-- peformed.
 data PackedObsContent = PackedObsContent
   { _packedObsContent_P   :: !Word32
     -- ^ Pseudorange observation
@@ -110,8 +114,8 @@
     -- future use.
   , _packedObsContent_flags :: !Word8
     -- ^ Measurement status flags. A bit field of flags providing the status of
-    -- this observation.  If this field is 0 it means only the Cn0 estimate for
-    -- the signal is valid.
+    -- this observation.  If this field is 0 it means only the Cn0 estimate
+    -- for the signal is valid.
   , _packedObsContent_sid :: !GnssSignal
     -- ^ GNSS signal identifier (16 bit)
   } deriving ( Show, Read, Eq )
@@ -199,14 +203,15 @@
 -- The GPS observations message reports all the raw pseudorange and carrier
 -- phase observations for the satellites being tracked by the device. Carrier
 -- phase observation here is represented as a 40-bit fixed point number with
--- Q32.8 layout (i.e. 32-bits of whole cycles and 8-bits of fractional cycles).
--- The observations are be interoperable with 3rd party receivers and conform
--- with typical RTCMv3 GNSS observations.
+-- Q32.8 layout (i.e. 32-bits of whole cycles and 8-bits of fractional
+-- cycles). The observations are be interoperable with 3rd party receivers and
+-- conform with typical RTCMv3 GNSS observations.
 data MsgObs = MsgObs
   { _msgObs_header :: !ObservationHeader
     -- ^ Header of a GPS observation message
   , _msgObs_obs  :: ![PackedObsContent]
-    -- ^ Pseudorange and carrier phase observation for a satellite being tracked.
+    -- ^ Pseudorange and carrier phase observation for a satellite being
+    -- tracked.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgObs where
@@ -304,8 +309,10 @@
   , _ephemerisCommonContent_valid      :: !Word8
     -- ^ Status of ephemeris, 1 = valid, 0 = invalid
   , _ephemerisCommonContent_health_bits :: !Word8
-    -- ^ Satellite health status. GPS: ICD-GPS-200, chapter 20.3.3.3.1.4 SBAS: 0
-    -- = valid, non-zero = invalid GLO: 0 = valid, non-zero = invalid
+    -- ^ Satellite health status.
+    -- GPS: ICD-GPS-200, chapter 20.3.3.3.1.4
+    -- SBAS: 0 = valid, non-zero = invalid
+    -- GLO: 0 = valid, non-zero = invalid
   } deriving ( Show, Read, Eq )
 
 instance Binary EphemerisCommonContent where
@@ -341,8 +348,9 @@
   , _ephemerisCommonContentDepB_valid      :: !Word8
     -- ^ Status of ephemeris, 1 = valid, 0 = invalid
   , _ephemerisCommonContentDepB_health_bits :: !Word8
-    -- ^ Satellite health status. GPS: ICD-GPS-200, chapter 20.3.3.3.1.4 Others:
-    -- 0 = valid, non-zero = invalid
+    -- ^ Satellite health status.
+    -- GPS: ICD-GPS-200, chapter 20.3.3.3.1.4
+    -- Others: 0 = valid, non-zero = invalid
   } deriving ( Show, Read, Eq )
 
 instance Binary EphemerisCommonContentDepB where
@@ -378,8 +386,10 @@
   , _ephemerisCommonContentDepA_valid      :: !Word8
     -- ^ Status of ephemeris, 1 = valid, 0 = invalid
   , _ephemerisCommonContentDepA_health_bits :: !Word8
-    -- ^ Satellite health status. GPS: ICD-GPS-200, chapter 20.3.3.3.1.4 SBAS: 0
-    -- = valid, non-zero = invalid GLO: 0 = valid, non-zero = invalid
+    -- ^ Satellite health status.
+    -- GPS: ICD-GPS-200, chapter 20.3.3.3.1.4
+    -- SBAS: 0 = valid, non-zero = invalid
+    -- GLO: 0 = valid, non-zero = invalid
   } deriving ( Show, Read, Eq )
 
 instance Binary EphemerisCommonContentDepA where
@@ -409,9 +419,9 @@
 -- | SBP class for message MSG_EPHEMERIS_GPS_DEP_E (0x0081).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate GPS satellite position, velocity, and clock offset. Please
--- see the Navstar GPS Space Segment/Navigation user interfaces (ICD-GPS-200,
--- Table 20-III) for more details.
+-- used to calculate GPS satellite position, velocity, and clock offset.
+-- Please see the Navstar GPS Space Segment/Navigation user interfaces (ICD-
+-- GPS-200, Table 20-III) for more details.
 data MsgEphemerisGpsDepE = MsgEphemerisGpsDepE
   { _msgEphemerisGpsDepE_common :: !EphemerisCommonContentDepA
     -- ^ Values common for all ephemeris types
@@ -643,9 +653,9 @@
 -- | SBP class for message MSG_EPHEMERIS_GPS (0x008A).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate GPS satellite position, velocity, and clock offset. Please
--- see the Navstar GPS Space Segment/Navigation user interfaces (ICD-GPS-200,
--- Table 20-III) for more details.
+-- used to calculate GPS satellite position, velocity, and clock offset.
+-- Please see the Navstar GPS Space Segment/Navigation user interfaces (ICD-
+-- GPS-200, Table 20-III) for more details.
 data MsgEphemerisGps = MsgEphemerisGps
   { _msgEphemerisGps_common :: !EphemerisCommonContent
     -- ^ Values common for all ephemeris types
@@ -877,9 +887,9 @@
 -- | SBP class for message MSG_EPHEMERIS_BDS (0x0089).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate BDS satellite position, velocity, and clock offset. Please
--- see the BeiDou Navigation Satellite System SIS-ICD Version 2.1, Table 5-9
--- for more details.
+-- used to calculate BDS satellite position, velocity, and clock offset.
+-- Please see the BeiDou Navigation Satellite System SIS-ICD Version 2.1,
+-- Table 5-9 for more details.
 data MsgEphemerisBds = MsgEphemerisBds
   { _msgEphemerisBds_common :: !EphemerisCommonContent
     -- ^ Values common for all ephemeris types
@@ -931,8 +941,12 @@
     -- ^ Clock reference
   , _msgEphemerisBds_iode   :: !Word8
     -- ^ Issue of ephemeris data
+    -- Calculated from the navigation data parameter t_oe per RTCM/CSNO
+    -- recommendation: IODE = mod (t_oe / 720, 240)
   , _msgEphemerisBds_iodc   :: !Word16
     -- ^ Issue of clock data
+    -- Calculated from the navigation data parameter t_oe per RTCM/CSNO
+    -- recommendation: IODE = mod (t_oc / 720, 240)
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgEphemerisBds where
@@ -1050,9 +1064,9 @@
   , _msgEphemerisGalDepA_toc     :: !GpsTimeSec
     -- ^ Clock reference
   , _msgEphemerisGalDepA_iode    :: !Word16
-    -- ^ Issue of ephemeris data
+    -- ^ Issue of data (IODnav)
   , _msgEphemerisGalDepA_iodc    :: !Word16
-    -- ^ Issue of clock data
+    -- ^ Issue of data (IODnav). Always equal to iode
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgEphemerisGalDepA where
@@ -1172,11 +1186,11 @@
   , _msgEphemerisGal_toc     :: !GpsTimeSec
     -- ^ Clock reference
   , _msgEphemerisGal_iode    :: !Word16
-    -- ^ Issue of ephemeris data
+    -- ^ Issue of data (IODnav)
   , _msgEphemerisGal_iodc    :: !Word16
-    -- ^ Issue of clock data
+    -- ^ Issue of data (IODnav). Always equal to iode
   , _msgEphemerisGal_source  :: !Word8
-    -- ^ 0=I/NAV, 1=F/NAV, ...
+    -- ^ 0=I/NAV, 1=F/NAV
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgEphemerisGal where
@@ -1285,8 +1299,8 @@
 -- | SBP class for message MSG_EPHEMERIS_GLO_DEP_A (0x0083).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate GLO satellite position, velocity, and clock offset. Please
--- see the GLO ICD 5.1 "Table 4.5 Characteristics of words of immediate
+-- used to calculate GLO satellite position, velocity, and clock offset.
+-- Please see the GLO ICD 5.1 "Table 4.5 Characteristics of words of immediate
 -- information (ephemeris parameters)" for more details.
 data MsgEphemerisGloDepA = MsgEphemerisGloDepA
   { _msgEphemerisGloDepA_common :: !EphemerisCommonContentDepA
@@ -1415,8 +1429,8 @@
 -- | SBP class for message MSG_EPHEMERIS_GLO_DEP_B (0x0085).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate GLO satellite position, velocity, and clock offset. Please
--- see the GLO ICD 5.1 "Table 4.5 Characteristics of words of immediate
+-- used to calculate GLO satellite position, velocity, and clock offset.
+-- Please see the GLO ICD 5.1 "Table 4.5 Characteristics of words of immediate
 -- information (ephemeris parameters)" for more details.
 data MsgEphemerisGloDepB = MsgEphemerisGloDepB
   { _msgEphemerisGloDepB_common :: !EphemerisCommonContentDepB
@@ -1461,8 +1475,8 @@
 -- | SBP class for message MSG_EPHEMERIS_GLO_DEP_C (0x0087).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate GLO satellite position, velocity, and clock offset. Please
--- see the GLO ICD 5.1 "Table 4.5 Characteristics of words of immediate
+-- used to calculate GLO satellite position, velocity, and clock offset.
+-- Please see the GLO ICD 5.1 "Table 4.5 Characteristics of words of immediate
 -- information (ephemeris parameters)" for more details.
 data MsgEphemerisGloDepC = MsgEphemerisGloDepC
   { _msgEphemerisGloDepC_common :: !EphemerisCommonContentDepB
@@ -1534,7 +1548,7 @@
   , _msgEphemerisGloDepD_fcn  :: !Word8
     -- ^ Frequency slot. FCN+8 (that is [1..14]). 0 or 0xFF for invalid
   , _msgEphemerisGloDepD_iod  :: !Word8
-    -- ^ Issue of ephemeris data
+    -- ^ Issue of data. Equal to the 7 bits of the immediate data word t_b
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgEphemerisGloDepD where
@@ -1571,8 +1585,8 @@
 -- | SBP class for message MSG_EPHEMERIS_GLO (0x008B).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate GLO satellite position, velocity, and clock offset. Please
--- see the GLO ICD 5.1 "Table 4.5 Characteristics of words of immediate
+-- used to calculate GLO satellite position, velocity, and clock offset.
+-- Please see the GLO ICD 5.1 "Table 4.5 Characteristics of words of immediate
 -- information (ephemeris parameters)" for more details.
 data MsgEphemerisGlo = MsgEphemerisGlo
   { _msgEphemerisGlo_common :: !EphemerisCommonContent
@@ -1592,7 +1606,7 @@
   , _msgEphemerisGlo_fcn  :: !Word8
     -- ^ Frequency slot. FCN+8 (that is [1..14]). 0 or 0xFF for invalid
   , _msgEphemerisGlo_iod  :: !Word8
-    -- ^ Issue of ephemeris data
+    -- ^ Issue of data. Equal to the 7 bits of the immediate data word t_b
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgEphemerisGlo where
@@ -1629,9 +1643,9 @@
 -- | SBP class for message MSG_EPHEMERIS_DEP_D (0x0080).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate GPS satellite position, velocity, and clock offset. Please
--- see the Navstar GPS Space Segment/Navigation user interfaces (ICD-GPS-200,
--- Table 20-III) for more details.
+-- used to calculate GPS satellite position, velocity, and clock offset.
+-- Please see the Navstar GPS Space Segment/Navigation user interfaces (ICD-
+-- GPS-200, Table 20-III) for more details.
 data MsgEphemerisDepD = MsgEphemerisDepD
   { _msgEphemerisDepD_tgd    :: !Double
     -- ^ Group delay differential between L1 and L2
@@ -2029,9 +2043,9 @@
 -- | SBP class for message MSG_EPHEMERIS_DEP_C (0x0047).
 --
 -- The ephemeris message returns a set of satellite orbit parameters that is
--- used to calculate GPS satellite position, velocity, and clock offset. Please
--- see the Navstar GPS Space Segment/Navigation user interfaces (ICD-GPS-200,
--- Table 20-III) for more details.
+-- used to calculate GPS satellite position, velocity, and clock offset.
+-- Please see the Navstar GPS Space Segment/Navigation user interfaces (ICD-
+-- GPS-200, Table 20-III) for more details.
 data MsgEphemerisDepC = MsgEphemerisDepC
   { _msgEphemerisDepC_tgd    :: !Double
     -- ^ Group delay differential between L1 and L2
@@ -2204,7 +2218,7 @@
 
 instance Binary CarrierPhaseDepA where
   get = do
-    _carrierPhaseDepA_i <- fromIntegral <$> getWord32le
+    _carrierPhaseDepA_i <- (fromIntegral <$> getWord32le)
     _carrierPhaseDepA_f <- getWord8
     pure CarrierPhaseDepA {..}
 
@@ -2292,9 +2306,9 @@
 
 -- | PackedObsContentDepC.
 --
--- Pseudorange and carrier phase observation for a satellite being tracked. The
--- observations are be interoperable with 3rd party receivers and conform with
--- typical RTCMv3 GNSS observations.
+-- Pseudorange and carrier phase observation for a satellite being tracked.
+-- The observations are be interoperable with 3rd party receivers and conform
+-- with typical RTCMv3 GNSS observations.
 data PackedObsContentDepC = PackedObsContentDepC
   { _packedObsContentDepC_P  :: !Word32
     -- ^ Pseudorange observation
@@ -2339,7 +2353,8 @@
   { _msgObsDepA_header :: !ObservationHeaderDep
     -- ^ Header of a GPS observation message
   , _msgObsDepA_obs  :: ![PackedObsContentDepA]
-    -- ^ Pseudorange and carrier phase observation for a satellite being tracked.
+    -- ^ Pseudorange and carrier phase observation for a satellite being
+    -- tracked.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgObsDepA where
@@ -2369,7 +2384,8 @@
   { _msgObsDepB_header :: !ObservationHeaderDep
     -- ^ Header of a GPS observation message
   , _msgObsDepB_obs  :: ![PackedObsContentDepB]
-    -- ^ Pseudorange and carrier phase observation for a satellite being tracked.
+    -- ^ Pseudorange and carrier phase observation for a satellite being
+    -- tracked.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgObsDepB where
@@ -2394,14 +2410,15 @@
 -- The GPS observations message reports all the raw pseudorange and carrier
 -- phase observations for the satellites being tracked by the device. Carrier
 -- phase observation here is represented as a 40-bit fixed point number with
--- Q32.8 layout (i.e. 32-bits of whole cycles and 8-bits of fractional cycles).
--- The observations are interoperable with 3rd party receivers and conform with
--- typical RTCMv3 GNSS observations.
+-- Q32.8 layout (i.e. 32-bits of whole cycles and 8-bits of fractional
+-- cycles). The observations are interoperable with 3rd party receivers and
+-- conform with typical RTCMv3 GNSS observations.
 data MsgObsDepC = MsgObsDepC
   { _msgObsDepC_header :: !ObservationHeaderDep
     -- ^ Header of a GPS observation message
   , _msgObsDepC_obs  :: ![PackedObsContentDepC]
-    -- ^ Pseudorange and carrier phase observation for a satellite being tracked.
+    -- ^ Pseudorange and carrier phase observation for a satellite being
+    -- tracked.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgObsDepC where
@@ -2604,8 +2621,8 @@
   , _msgGroupDelayDepA_prn    :: !Word8
     -- ^ Satellite number
   , _msgGroupDelayDepA_valid  :: !Word8
-    -- ^ bit-field indicating validity of the values, LSB indicating tgd validity
-    -- etc. 1 = value is valid, 0 = value is not valid.
+    -- ^ bit-field indicating validity of the values, LSB indicating tgd
+    -- validity etc. 1 = value is valid, 0 = value is not valid.
   , _msgGroupDelayDepA_tgd    :: !Int16
   , _msgGroupDelayDepA_isc_l1ca :: !Int16
   , _msgGroupDelayDepA_isc_l2c :: !Int16
@@ -2616,9 +2633,9 @@
     _msgGroupDelayDepA_t_op <- get
     _msgGroupDelayDepA_prn <- getWord8
     _msgGroupDelayDepA_valid <- getWord8
-    _msgGroupDelayDepA_tgd <- fromIntegral <$> getWord16le
-    _msgGroupDelayDepA_isc_l1ca <- fromIntegral <$> getWord16le
-    _msgGroupDelayDepA_isc_l2c <- fromIntegral <$> getWord16le
+    _msgGroupDelayDepA_tgd <- (fromIntegral <$> getWord16le)
+    _msgGroupDelayDepA_isc_l1ca <- (fromIntegral <$> getWord16le)
+    _msgGroupDelayDepA_isc_l2c <- (fromIntegral <$> getWord16le)
     pure MsgGroupDelayDepA {..}
 
   put MsgGroupDelayDepA {..} = do
@@ -2645,8 +2662,8 @@
   , _msgGroupDelayDepB_sid    :: !GnssSignalDep
     -- ^ GNSS signal identifier
   , _msgGroupDelayDepB_valid  :: !Word8
-    -- ^ bit-field indicating validity of the values, LSB indicating tgd validity
-    -- etc. 1 = value is valid, 0 = value is not valid.
+    -- ^ bit-field indicating validity of the values, LSB indicating tgd
+    -- validity etc. 1 = value is valid, 0 = value is not valid.
   , _msgGroupDelayDepB_tgd    :: !Int16
   , _msgGroupDelayDepB_isc_l1ca :: !Int16
   , _msgGroupDelayDepB_isc_l2c :: !Int16
@@ -2657,9 +2674,9 @@
     _msgGroupDelayDepB_t_op <- get
     _msgGroupDelayDepB_sid <- get
     _msgGroupDelayDepB_valid <- getWord8
-    _msgGroupDelayDepB_tgd <- fromIntegral <$> getWord16le
-    _msgGroupDelayDepB_isc_l1ca <- fromIntegral <$> getWord16le
-    _msgGroupDelayDepB_isc_l2c <- fromIntegral <$> getWord16le
+    _msgGroupDelayDepB_tgd <- (fromIntegral <$> getWord16le)
+    _msgGroupDelayDepB_isc_l1ca <- (fromIntegral <$> getWord16le)
+    _msgGroupDelayDepB_isc_l2c <- (fromIntegral <$> getWord16le)
     pure MsgGroupDelayDepB {..}
 
   put MsgGroupDelayDepB {..} = do
@@ -2686,8 +2703,8 @@
   , _msgGroupDelay_sid    :: !GnssSignal
     -- ^ GNSS signal identifier
   , _msgGroupDelay_valid  :: !Word8
-    -- ^ bit-field indicating validity of the values, LSB indicating tgd validity
-    -- etc. 1 = value is valid, 0 = value is not valid.
+    -- ^ bit-field indicating validity of the values, LSB indicating tgd
+    -- validity etc. 1 = value is valid, 0 = value is not valid.
   , _msgGroupDelay_tgd    :: !Int16
   , _msgGroupDelay_isc_l1ca :: !Int16
   , _msgGroupDelay_isc_l2c :: !Int16
@@ -2698,9 +2715,9 @@
     _msgGroupDelay_t_op <- get
     _msgGroupDelay_sid <- get
     _msgGroupDelay_valid <- getWord8
-    _msgGroupDelay_tgd <- fromIntegral <$> getWord16le
-    _msgGroupDelay_isc_l1ca <- fromIntegral <$> getWord16le
-    _msgGroupDelay_isc_l2c <- fromIntegral <$> getWord16le
+    _msgGroupDelay_tgd <- (fromIntegral <$> getWord16le)
+    _msgGroupDelay_isc_l1ca <- (fromIntegral <$> getWord16le)
+    _msgGroupDelay_isc_l2c <- (fromIntegral <$> getWord16le)
     pure MsgGroupDelay {..}
 
   put MsgGroupDelay {..} = do
@@ -2727,16 +2744,21 @@
   , _almanacCommonContent_valid      :: !Word8
     -- ^ Status of almanac, 1 = valid, 0 = invalid
   , _almanacCommonContent_health_bits :: !Word8
-    -- ^ Satellite health status for GPS:   - bits 5-7: NAV data health status.
-    -- See IS-GPS-200H     Table 20-VII: NAV Data Health Indications.   - bits
-    -- 0-4: Signal health status. See IS-GPS-200H     Table 20-VIII. Codes for
-    -- Health of SV Signal     Components. Satellite health status for GLO:
-    -- See GLO ICD 5.1 table 5.1 for details   - bit 0: C(n), "unhealthy" flag
-    -- that is transmitted within     non-immediate data and indicates overall
-    -- constellation status     at the moment of almanac uploading.     '0'
-    -- indicates malfunction of n-satellite.     '1' indicates that n-satellite
-    -- is operational.   - bit 1: Bn(ln), '0' indicates the satellite is
-    -- operational     and suitable for navigation.
+    -- ^ Satellite health status for GPS:
+    --   - bits 5-7: NAV data health status. See IS-GPS-200H
+    --     Table 20-VII: NAV Data Health Indications.
+    --   - bits 0-4: Signal health status. See IS-GPS-200H
+    --     Table 20-VIII. Codes for Health of SV Signal
+    --     Components.
+    -- Satellite health status for GLO (see GLO ICD 5.1 table 5.1 for
+    -- details):
+    --   - bit 0: C(n), "unhealthy" flag that is transmitted within
+    --     non-immediate data and indicates overall constellation status
+    --     at the moment of almanac uploading.
+    --     '0' indicates malfunction of n-satellite.
+    --     '1' indicates that n-satellite is operational.
+    --   - bit 1: Bn(ln), '0' indicates the satellite is operational
+    --     and suitable for navigation.
   } deriving ( Show, Read, Eq )
 
 instance Binary AlmanacCommonContent where
@@ -2772,16 +2794,21 @@
   , _almanacCommonContentDep_valid      :: !Word8
     -- ^ Status of almanac, 1 = valid, 0 = invalid
   , _almanacCommonContentDep_health_bits :: !Word8
-    -- ^ Satellite health status for GPS:   - bits 5-7: NAV data health status.
-    -- See IS-GPS-200H     Table 20-VII: NAV Data Health Indications.   - bits
-    -- 0-4: Signal health status. See IS-GPS-200H     Table 20-VIII. Codes for
-    -- Health of SV Signal     Components. Satellite health status for GLO:
-    -- See GLO ICD 5.1 table 5.1 for details   - bit 0: C(n), "unhealthy" flag
-    -- that is transmitted within     non-immediate data and indicates overall
-    -- constellation status     at the moment of almanac uploading.     '0'
-    -- indicates malfunction of n-satellite.     '1' indicates that n-satellite
-    -- is operational.   - bit 1: Bn(ln), '0' indicates the satellite is
-    -- operational     and suitable for navigation.
+    -- ^ Satellite health status for GPS:
+    --   - bits 5-7: NAV data health status. See IS-GPS-200H
+    --     Table 20-VII: NAV Data Health Indications.
+    --   - bits 0-4: Signal health status. See IS-GPS-200H
+    --     Table 20-VIII. Codes for Health of SV Signal
+    --     Components.
+    -- Satellite health status for GLO (see GLO ICD 5.1 table 5.1 for
+    -- details):
+    --   - bit 0: C(n), "unhealthy" flag that is transmitted within
+    --     non-immediate data and indicates overall constellation status
+    --     at the moment of almanac uploading.
+    --     '0' indicates malfunction of n-satellite.
+    --     '1' indicates that n-satellite is operational.
+    --   - bit 1: Bn(ln), '0' indicates the satellite is operational
+    --     and suitable for navigation.
   } deriving ( Show, Read, Eq )
 
 instance Binary AlmanacCommonContentDep where
@@ -3046,7 +3073,7 @@
 --
 -- The GLONASS L1/L2 Code-Phase biases allows to perform GPS+GLONASS integer
 -- ambiguity resolution for baselines with mixed receiver types (e.g. receiver
--- of different manufacturers)
+-- of different manufacturers).
 data MsgGloBiases = MsgGloBiases
   { _msgGloBiases_mask    :: !Word8
     -- ^ GLONASS FDMA signals mask
@@ -3063,10 +3090,10 @@
 instance Binary MsgGloBiases where
   get = do
     _msgGloBiases_mask <- getWord8
-    _msgGloBiases_l1ca_bias <- fromIntegral <$> getWord16le
-    _msgGloBiases_l1p_bias <- fromIntegral <$> getWord16le
-    _msgGloBiases_l2ca_bias <- fromIntegral <$> getWord16le
-    _msgGloBiases_l2p_bias <- fromIntegral <$> getWord16le
+    _msgGloBiases_l1ca_bias <- (fromIntegral <$> getWord16le)
+    _msgGloBiases_l1p_bias <- (fromIntegral <$> getWord16le)
+    _msgGloBiases_l2ca_bias <- (fromIntegral <$> getWord16le)
+    _msgGloBiases_l2p_bias <- (fromIntegral <$> getWord16le)
     pure MsgGloBiases {..}
 
   put MsgGloBiases {..} = do
@@ -3096,7 +3123,7 @@
   get = do
     _svAzEl_sid <- get
     _svAzEl_az <- getWord8
-    _svAzEl_el <- fromIntegral <$> getWord8
+    _svAzEl_el <- (fromIntegral <$> getWord8)
     pure SvAzEl {..}
 
   put SvAzEl {..} = do
@@ -3136,7 +3163,7 @@
 
 -- | SBP class for message MSG_OSR (0x0640).
 --
--- The OSR message contains network corrections in an observation-like format
+-- The OSR message contains network corrections in an observation-like format.
 data MsgOsr = MsgOsr
   { _msgOsr_header :: !ObservationHeader
     -- ^ Header of a GPS observation message
diff --git a/src/SwiftNav/SBP/Orientation.hs b/src/SwiftNav/SBP/Orientation.hs
--- a/src/SwiftNav/SBP/Orientation.hs
+++ b/src/SwiftNav/SBP/Orientation.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Orientation
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Orientation Messages
+-- \< Orientation Messages \>
 
 module SwiftNav.SBP.Orientation
   ( module SwiftNav.SBP.Orientation
@@ -80,8 +80,9 @@
 -- This message reports the quaternion vector describing the vehicle body
 -- frame's orientation with respect to a local-level NED frame. The components
 -- of the vector should sum to a unit vector assuming that the LSB of each
--- component as a value of 2^-31. This message will only be available in future
--- INS versions of Swift Products and is not produced by Piksi Multi  or Duro.
+-- component as a value of 2^-31. This message will only be available in
+-- future INS versions of Swift Products and is not produced by Piksi Multi or
+-- Duro.
 data MsgOrientQuat = MsgOrientQuat
   { _msgOrientQuat_tow      :: !Word32
     -- ^ GPS Time of Week
@@ -108,10 +109,10 @@
 instance Binary MsgOrientQuat where
   get = do
     _msgOrientQuat_tow <- getWord32le
-    _msgOrientQuat_w <- fromIntegral <$> getWord32le
-    _msgOrientQuat_x <- fromIntegral <$> getWord32le
-    _msgOrientQuat_y <- fromIntegral <$> getWord32le
-    _msgOrientQuat_z <- fromIntegral <$> getWord32le
+    _msgOrientQuat_w <- (fromIntegral <$> getWord32le)
+    _msgOrientQuat_x <- (fromIntegral <$> getWord32le)
+    _msgOrientQuat_y <- (fromIntegral <$> getWord32le)
+    _msgOrientQuat_z <- (fromIntegral <$> getWord32le)
     _msgOrientQuat_w_accuracy <- getFloat32le
     _msgOrientQuat_x_accuracy <- getFloat32le
     _msgOrientQuat_y_accuracy <- getFloat32le
@@ -142,9 +143,9 @@
 --
 -- This message reports the yaw, pitch, and roll angles of the vehicle body
 -- frame. The rotations should applied intrinsically in the order yaw, pitch,
--- and roll  in order to rotate the from a frame aligned with the local-level
--- NED frame  to the vehicle body frame.  This message will only be available
--- in future  INS versions of Swift Products and is not produced by Piksi Multi
+-- and roll in order to rotate the from a frame aligned with the local-level
+-- NED frame to the vehicle body frame.  This message will only be available
+-- in future INS versions of Swift Products and is not produced by Piksi Multi
 -- or Duro.
 data MsgOrientEuler = MsgOrientEuler
   { _msgOrientEuler_tow          :: !Word32
@@ -168,9 +169,9 @@
 instance Binary MsgOrientEuler where
   get = do
     _msgOrientEuler_tow <- getWord32le
-    _msgOrientEuler_roll <- fromIntegral <$> getWord32le
-    _msgOrientEuler_pitch <- fromIntegral <$> getWord32le
-    _msgOrientEuler_yaw <- fromIntegral <$> getWord32le
+    _msgOrientEuler_roll <- (fromIntegral <$> getWord32le)
+    _msgOrientEuler_pitch <- (fromIntegral <$> getWord32le)
+    _msgOrientEuler_yaw <- (fromIntegral <$> getWord32le)
     _msgOrientEuler_roll_accuracy <- getFloat32le
     _msgOrientEuler_pitch_accuracy <- getFloat32le
     _msgOrientEuler_yaw_accuracy <- getFloat32le
@@ -196,15 +197,15 @@
 
 -- | SBP class for message MSG_ANGULAR_RATE (0x0222).
 --
--- This message reports the orientation rates in the vehicle body frame.  The
--- values represent the measurements a strapped down gyroscope would  make and
+-- This message reports the orientation rates in the vehicle body frame. The
+-- values represent the measurements a strapped down gyroscope would make and
 -- are not equivalent to the time derivative of the Euler angles. The
 -- orientation and origin of the user frame is specified via device settings.
--- By convention, the vehicle x-axis is expected to be aligned with the forward
--- direction, while the vehicle y-axis is expected to be aligned with the right
--- direction, and the vehicle z-axis should be aligned with the down direction.
--- This message will only be available in future INS versions of Swift Products
--- and is not produced by Piksi Multi or Duro.
+-- By convention, the vehicle x-axis is expected to be aligned with the
+-- forward direction, while the vehicle y-axis is expected to be aligned with
+-- the right direction, and the vehicle z-axis should be aligned with the down
+-- direction. This message will only be available in future INS versions of
+-- Swift Products and is not produced by Piksi Multi or Duro.
 data MsgAngularRate = MsgAngularRate
   { _msgAngularRate_tow :: !Word32
     -- ^ GPS Time of Week
@@ -221,9 +222,9 @@
 instance Binary MsgAngularRate where
   get = do
     _msgAngularRate_tow <- getWord32le
-    _msgAngularRate_x <- fromIntegral <$> getWord32le
-    _msgAngularRate_y <- fromIntegral <$> getWord32le
-    _msgAngularRate_z <- fromIntegral <$> getWord32le
+    _msgAngularRate_x <- (fromIntegral <$> getWord32le)
+    _msgAngularRate_y <- (fromIntegral <$> getWord32le)
+    _msgAngularRate_z <- (fromIntegral <$> getWord32le)
     _msgAngularRate_flags <- getWord8
     pure MsgAngularRate {..}
 
diff --git a/src/SwiftNav/SBP/Piksi.hs b/src/SwiftNav/SBP/Piksi.hs
--- a/src/SwiftNav/SBP/Piksi.hs
+++ b/src/SwiftNav/SBP/Piksi.hs
@@ -6,14 +6,14 @@
 -- |
 -- Module:      SwiftNav.SBP.Piksi
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- System health, configuration, and diagnostic messages specific to the Piksi
+-- \< System health, configuration, and diagnostic messages specific to the Piksi
 -- L1 receiver, including a variety of legacy messages that may no longer be
--- used.
+-- used. \>
 
 module SwiftNav.SBP.Piksi
   ( module SwiftNav.SBP.Piksi
@@ -211,14 +211,14 @@
 -- | SBP class for message MSG_THREAD_STATE (0x0017).
 --
 -- The thread usage message from the device reports real-time operating system
--- (RTOS) thread usage statistics for the named thread. The reported percentage
--- values must be normalized.
+-- (RTOS) thread usage statistics for the named thread. The reported
+-- percentage values must be normalized.
 data MsgThreadState = MsgThreadState
   { _msgThreadState_name     :: !Text
     -- ^ Thread name (NULL terminated)
   , _msgThreadState_cpu      :: !Word16
-    -- ^ Percentage cpu use for this thread. Values range from 0 - 1000 and needs
-    -- to be renormalized to 100
+    -- ^ Percentage cpu use for this thread. Values range from 0 - 1000 and
+    -- needs to be renormalized to 100
   , _msgThreadState_stack_free :: !Word32
     -- ^ Free stack space for this thread
   } deriving ( Show, Read, Eq )
@@ -299,10 +299,10 @@
 
 instance Binary Period where
   get = do
-    _period_avg <- fromIntegral <$> getWord32le
-    _period_pmin <- fromIntegral <$> getWord32le
-    _period_pmax <- fromIntegral <$> getWord32le
-    _period_current <- fromIntegral <$> getWord32le
+    _period_avg <- (fromIntegral <$> getWord32le)
+    _period_pmin <- (fromIntegral <$> getWord32le)
+    _period_pmax <- (fromIntegral <$> getWord32le)
+    _period_current <- (fromIntegral <$> getWord32le)
     pure Period {..}
 
   put Period {..} = do
@@ -316,10 +316,10 @@
 
 -- | Latency.
 --
--- Statistics on the latency of observations received from the base station. As
--- observation packets are received their GPS time is compared to the current
--- GPS time calculated locally by the receiver to give a precise measurement of
--- the end-to-end communication latency in the system.
+-- Statistics on the latency of observations received from the base station.
+-- As observation packets are received their GPS time is compared to the
+-- current GPS time calculated locally by the receiver to give a precise
+-- measurement of the end-to-end communication latency in the system.
 data Latency = Latency
   { _latency_avg   :: !Int32
     -- ^ Average latency
@@ -333,10 +333,10 @@
 
 instance Binary Latency where
   get = do
-    _latency_avg <- fromIntegral <$> getWord32le
-    _latency_lmin <- fromIntegral <$> getWord32le
-    _latency_lmax <- fromIntegral <$> getWord32le
-    _latency_current <- fromIntegral <$> getWord32le
+    _latency_avg <- (fromIntegral <$> getWord32le)
+    _latency_lmin <- (fromIntegral <$> getWord32le)
+    _latency_lmax <- (fromIntegral <$> getWord32le)
+    _latency_current <- (fromIntegral <$> getWord32le)
     pure Latency {..}
 
   put Latency {..} = do
@@ -356,11 +356,11 @@
 -- The UART message reports data latency and throughput of the UART channels
 -- providing SBP I/O. On the default Piksi configuration, UARTs A and B are
 -- used for telemetry radios, but can also be host access ports for embedded
--- hosts, or other interfaces in future. The reported percentage values must be
--- normalized. Observations latency and period can be used to assess the health
--- of the differential corrections link. Latency provides the timeliness of
--- received base observations while the period indicates their likelihood of
--- transmission.
+-- hosts, or other interfaces in future. The reported percentage values must
+-- be normalized. Observations latency and period can be used to assess the
+-- health of the differential corrections link. Latency provides the
+-- timeliness of received base observations while the period indicates their
+-- likelihood of transmission.
 data MsgUartState = MsgUartState
   { _msgUartState_uart_a   :: !UARTChannel
     -- ^ State of UART A
@@ -532,11 +532,11 @@
 
 instance Binary MsgDeviceMonitor where
   get = do
-    _msgDeviceMonitor_dev_vin <- fromIntegral <$> getWord16le
-    _msgDeviceMonitor_cpu_vint <- fromIntegral <$> getWord16le
-    _msgDeviceMonitor_cpu_vaux <- fromIntegral <$> getWord16le
-    _msgDeviceMonitor_cpu_temperature <- fromIntegral <$> getWord16le
-    _msgDeviceMonitor_fe_temperature <- fromIntegral <$> getWord16le
+    _msgDeviceMonitor_dev_vin <- (fromIntegral <$> getWord16le)
+    _msgDeviceMonitor_cpu_vint <- (fromIntegral <$> getWord16le)
+    _msgDeviceMonitor_cpu_vaux <- (fromIntegral <$> getWord16le)
+    _msgDeviceMonitor_cpu_temperature <- (fromIntegral <$> getWord16le)
+    _msgDeviceMonitor_fe_temperature <- (fromIntegral <$> getWord16le)
     pure MsgDeviceMonitor {..}
 
   put MsgDeviceMonitor {..} = do
@@ -595,7 +595,7 @@
 instance Binary MsgCommandResp where
   get = do
     _msgCommandResp_sequence <- getWord32le
-    _msgCommandResp_code <- fromIntegral <$> getWord32le
+    _msgCommandResp_code <- (fromIntegral <$> getWord32le)
     pure MsgCommandResp {..}
 
   put MsgCommandResp {..} = do
@@ -612,8 +612,8 @@
 -- | SBP class for message MSG_COMMAND_OUTPUT (0x00BC).
 --
 -- Returns the standard output and standard error of the command requested by
--- MSG_COMMAND_REQ. The sequence number can be used to filter for filtering the
--- correct command.
+-- MSG_COMMAND_REQ. The sequence number can be used to filter for filtering
+-- the correct command.
 data MsgCommandOutput = MsgCommandOutput
   { _msgCommandOutput_sequence :: !Word32
     -- ^ Sequence number
@@ -641,7 +641,7 @@
 -- | SBP class for message MSG_NETWORK_STATE_REQ (0x00BA).
 --
 -- Request state of Piksi network interfaces. Output will be sent in
--- MSG_NETWORK_STATE_RESP messages
+-- MSG_NETWORK_STATE_RESP messages.
 data MsgNetworkStateReq = MsgNetworkStateReq
   deriving ( Show, Read, Eq )
 
@@ -788,7 +788,7 @@
 
 instance Binary MsgCellModemStatus where
   get = do
-    _msgCellModemStatus_signal_strength <- fromIntegral <$> getWord8
+    _msgCellModemStatus_signal_strength <- (fromIntegral <$> getWord8)
     _msgCellModemStatus_signal_error_rate <- getFloat32le
     _msgCellModemStatus_reserved <- whileM (not <$> isEmpty) getWord8
     pure MsgCellModemStatus {..}
@@ -902,10 +902,10 @@
 -- | SBP class for message MSG_FRONT_END_GAIN (0x00BF).
 --
 -- This message describes the gain of each channel in the receiver frontend.
--- Each  gain is encoded as a non-dimensional percentage relative to the
--- maximum range   possible for the gain stage of the frontend. By convention,
--- each gain array  has 8 entries and the index of the array corresponding to
--- the index of the rf channel  in the frontend. A gain of 127 percent encodes
+-- Each gain is encoded as a non-dimensional percentage relative to the
+-- maximum range possible for the gain stage of the frontend. By convention,
+-- each gain array has 8 entries and the index of the array corresponding to
+-- the index of the rf channel in the frontend. A gain of 127 percent encodes
 -- that rf channel is not present in the hardware. A negative value implies an
 -- error for the particular gain stage as reported by the frontend.
 data MsgFrontEndGain = MsgFrontEndGain
@@ -917,8 +917,8 @@
 
 instance Binary MsgFrontEndGain where
   get = do
-    _msgFrontEndGain_rf_gain <- replicateM 8 fromIntegral <$> getWord8
-    _msgFrontEndGain_if_gain <- replicateM 8 fromIntegral <$> getWord8
+    _msgFrontEndGain_rf_gain <- replicateM 8 (fromIntegral <$> getWord8)
+    _msgFrontEndGain_if_gain <- replicateM 8 (fromIntegral <$> getWord8)
     pure MsgFrontEndGain {..}
 
   put MsgFrontEndGain {..} = do
diff --git a/src/SwiftNav/SBP/Sbas.hs b/src/SwiftNav/SBP/Sbas.hs
--- a/src/SwiftNav/SBP/Sbas.hs
+++ b/src/SwiftNav/SBP/Sbas.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Sbas
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- SBAS data
+-- \< SBAS data \>
 
 module SwiftNav.SBP.Sbas
   ( module SwiftNav.SBP.Sbas
diff --git a/src/SwiftNav/SBP/Settings.hs b/src/SwiftNav/SBP/Settings.hs
--- a/src/SwiftNav/SBP/Settings.hs
+++ b/src/SwiftNav/SBP/Settings.hs
@@ -6,33 +6,35 @@
 -- |
 -- Module:      SwiftNav.SBP.Settings
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
---  Messages for reading, writing, and discovering device settings. Settings
+-- \< Messages for reading, writing, and discovering device settings. Settings
 -- with a "string" field have multiple values in this field delimited with a
 -- null character (the c style null terminator).  For instance, when querying
 -- the 'firmware_version' setting in the 'system_info' section, the following
 -- array of characters needs to be sent for the string field in
 -- MSG_SETTINGS_READ: "system_info\0firmware_version\0", where the delimiting
 -- null characters are specified with the escape sequence '\0' and all
--- quotation marks should be omitted.    In the message descriptions below, the
--- generic strings SECTION_SETTING and SETTING are used to refer to the two
--- strings that comprise the identifier of an individual setting.In
--- firmware_version example above, SECTION_SETTING is the 'system_info', and
--- the SETTING portion is 'firmware_version'.   See the "Software Settings
--- Manual" on support.swiftnav.com for detailed documentation about all
--- settings and sections available for each Swift firmware version. Settings
--- manuals are available for each firmware version at the following link:
--- https://support.swiftnav.com/customer/en/portal/articles/2628580-piksi-
--- multi-specifications#settings. The latest settings document is also
--- available at the following link:  http://swiftnav.com/latest/piksi-multi-
--- settings . See lastly https://github.com/swift-
--- nav/piksi_tools/blob/master/piksi_tools/settings.py ,  the open source
--- python command line utility for reading, writing, and saving settings in the
--- piksi_tools repository on github as a helpful reference and example.
+-- quotation marks should be omitted.
+--
+-- In the message descriptions below, the generic strings SECTION_SETTING and
+-- SETTING are used to refer to the two strings that comprise the identifier
+-- of an individual setting.In firmware_version example above, SECTION_SETTING
+-- is the 'system_info', and the SETTING portion is 'firmware_version'.
+-- See the "Software Settings Manual" on support.swiftnav.com for detailed
+-- documentation about all settings and sections available for each Swift
+-- firmware version. Settings manuals are available for each firmware version
+-- at the following link:
+-- https://support.swiftnav.com/support/solutions/articles/44001850753-piksi-multi-specification.
+-- The latest settings document is also available at the following link:
+-- http://swiftnav.com/latest/piksi-multi-settings . See lastly
+-- https://github.com/swift-nav/piksi_tools/blob/master/piksi_tools/settings.py
+-- , the open source python command line utility for reading, writing, and
+-- saving settings in the piksi_tools repository on github as a helpful
+-- reference and example. \>
 
 module SwiftNav.SBP.Settings
   ( module SwiftNav.SBP.Settings
@@ -81,12 +83,13 @@
 
 -- | SBP class for message MSG_SETTINGS_WRITE (0x00A0).
 --
--- The setting message writes the device configuration for a particular setting
--- via A NULL-terminated and NULL-delimited string with contents
+-- The setting message writes the device configuration for a particular
+-- setting via A NULL-terminated and NULL-delimited string with contents
 -- "SECTION_SETTING\0SETTING\0VALUE\0" where the '\0' escape sequence denotes
--- the NULL character and where quotation marks are omitted. A device will only
--- process to this message when it is received from sender ID 0x42. An example
--- string that could be sent to a device is "solution\0soln_freq\010\0".
+-- the NULL character and where quotation marks are omitted. A device will
+-- only process to this message when it is received from sender ID 0x42. An
+-- example string that could be sent to a device is
+-- "solution\0soln_freq\010\0".
 data MsgSettingsWrite = MsgSettingsWrite
   { _msgSettingsWrite_setting :: !Text
     -- ^ A NULL-terminated and NULL-delimited string with contents
@@ -143,13 +146,14 @@
 
 -- | SBP class for message MSG_SETTINGS_READ_REQ (0x00A4).
 --
--- The setting message that reads the device configuration. The string field is
--- a NULL-terminated and NULL-delimited string with contents
--- "SECTION_SETTING\0SETTING\0" where the '\0' escape sequence denotes the NULL
--- character and where quotation marks are omitted. An example string that
--- could be sent to a device is "solution\0soln_freq\0". A device will only
--- respond to this message when it is received from sender ID 0x42. A device
--- should respond with a MSG_SETTINGS_READ_RESP message (msg_id 0x00A5).
+-- The setting message that reads the device configuration. The string field
+-- is a NULL-terminated and NULL-delimited string with contents
+-- "SECTION_SETTING\0SETTING\0" where the '\0' escape sequence denotes the
+-- NULL character and where quotation marks are omitted. An example string
+-- that could be sent to a device is "solution\0soln_freq\0". A device will
+-- only respond to this message when it is received from sender ID 0x42. A
+-- device should respond with a MSG_SETTINGS_READ_RESP message (msg_id
+-- 0x00A5).
 data MsgSettingsReadReq = MsgSettingsReadReq
   { _msgSettingsReadReq_setting :: !Text
     -- ^ A NULL-terminated and NULL-delimited string with contents
@@ -203,11 +207,11 @@
 -- | SBP class for message MSG_SETTINGS_READ_BY_INDEX_REQ (0x00A2).
 --
 -- The settings message for iterating through the settings values. A device
--- will respond to this message with a  "MSG_SETTINGS_READ_BY_INDEX_RESP".
+-- will respond to this message with a "MSG_SETTINGS_READ_BY_INDEX_RESP".
 data MsgSettingsReadByIndexReq = MsgSettingsReadByIndexReq
   { _msgSettingsReadByIndexReq_index :: !Word16
     -- ^ An index into the device settings, with values ranging from 0 to
-    -- length(settings)
+    -- length(settings).
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSettingsReadByIndexReq where
@@ -227,15 +231,16 @@
 
 -- | SBP class for message MSG_SETTINGS_READ_BY_INDEX_RESP (0x00A7).
 --
--- The settings message that reports the value of a setting at an index.  In
--- the string field, it reports NULL-terminated and delimited string with
+-- The settings message that reports the value of a setting at an index.
+--
+-- In the string field, it reports NULL-terminated and delimited string with
 -- contents "SECTION_SETTING\0SETTING\0VALUE\0FORMAT_TYPE\0". where the '\0'
 -- escape sequence denotes the NULL character and where quotation marks are
 -- omitted. The FORMAT_TYPE field is optional and denotes possible string
 -- values of the setting as a hint to the user. If included, the format type
--- portion of the string has the format "enum:value1,value2,value3". An example
--- string that could be sent from the device is
--- "simulator\0enabled\0True\0enum:True,False\0"
+-- portion of the string has the format "enum:value1,value2,value3". An
+-- example string that could be sent from the device is
+-- "simulator\0enabled\0True\0enum:True,False\0".
 data MsgSettingsReadByIndexResp = MsgSettingsReadByIndexResp
   { _msgSettingsReadByIndexResp_index :: !Word16
     -- ^ An index into the device settings, with values ranging from 0 to
diff --git a/src/SwiftNav/SBP/SolutionMeta.hs b/src/SwiftNav/SBP/SolutionMeta.hs
new file mode 100644
--- /dev/null
+++ b/src/SwiftNav/SBP/SolutionMeta.hs
@@ -0,0 +1,256 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+{-# LANGUAGE NoImplicitPrelude           #-}
+{-# LANGUAGE TemplateHaskell             #-}
+{-# LANGUAGE RecordWildCards             #-}
+
+-- |
+-- Module:      SwiftNav.SBP.SolutionMeta
+-- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
+-- Stability:   experimental
+-- Portability: portable
+--
+-- \< Standardized Metadata messages for Fuzed Solution from Swift Navigation
+-- devices. \>
+
+module SwiftNav.SBP.SolutionMeta
+  ( module SwiftNav.SBP.SolutionMeta
+  ) where
+
+import BasicPrelude
+import Control.Lens
+import Control.Monad.Loops
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.IEEE754
+import Data.Binary.Put
+import Data.ByteString.Lazy    hiding (ByteString)
+import Data.Int
+import Data.Word
+import SwiftNav.SBP.TH
+import SwiftNav.SBP.Types
+
+{-# ANN module ("HLint: ignore Use camelCase"::String) #-}
+{-# ANN module ("HLint: ignore Redundant do"::String) #-}
+{-# ANN module ("HLint: ignore Use newtype instead of data"::String) #-}
+
+
+-- | SolutionInputType.
+--
+-- Metadata describing which sensors were involved in the solution. The
+-- structure is fixed no matter what the actual sensor type is. The
+-- sensor_type field tells you which sensor we are talking about. It also
+-- tells you whether the sensor data was actually used or not. The flags
+-- field, always a u8, contains the sensor-specific data. The content of
+-- flags, for each sensor type, is described in the relevant structures in
+-- this section.
+data SolutionInputType = SolutionInputType
+  { _solutionInputType_sensor_type :: !Word8
+    -- ^ The type of sensor
+  , _solutionInputType_flags     :: !Word8
+    -- ^ Refer to each InputType description
+  } deriving ( Show, Read, Eq )
+
+instance Binary SolutionInputType where
+  get = do
+    _solutionInputType_sensor_type <- getWord8
+    _solutionInputType_flags <- getWord8
+    pure SolutionInputType {..}
+
+  put SolutionInputType {..} = do
+    putWord8 _solutionInputType_sensor_type
+    putWord8 _solutionInputType_flags
+
+$(makeJSON "_solutionInputType_" ''SolutionInputType)
+$(makeLenses ''SolutionInputType)
+
+msgSolnMetaDepA :: Word16
+msgSolnMetaDepA = 0xFF0F
+
+-- | SBP class for message MSG_SOLN_META_DEP_A (0xFF0F).
+--
+-- Deprecated.
+--
+-- This message contains all metadata about the sensors received and/or used
+-- in computing the Fuzed Solution. It focuses primarly, but not only, on GNSS
+-- metadata.
+data MsgSolnMetaDepA = MsgSolnMetaDepA
+  { _msgSolnMetaDepA_pdop                 :: !Word16
+    -- ^ Position Dilution of Precision as per last available DOPS from PVT
+    -- engine (0xFFFF indicates invalid)
+  , _msgSolnMetaDepA_hdop                 :: !Word16
+    -- ^ Horizontal Dilution of Precision as per last available DOPS from PVT
+    -- engine (0xFFFF indicates invalid)
+  , _msgSolnMetaDepA_vdop                 :: !Word16
+    -- ^ Vertical Dilution of Precision as per last available DOPS from PVT
+    -- engine (0xFFFF indicates invalid)
+  , _msgSolnMetaDepA_n_sats               :: !Word8
+    -- ^ Number of satellites as per last available solution from PVT engine
+  , _msgSolnMetaDepA_age_corrections      :: !Word16
+    -- ^ Age of corrections as per last available AGE_CORRECTIONS from PVT
+    -- engine (0xFFFF indicates invalid)
+  , _msgSolnMetaDepA_alignment_status     :: !Word8
+    -- ^ State of alignment and the status and receipt of the alignment inputs
+  , _msgSolnMetaDepA_last_used_gnss_pos_tow :: !Word32
+    -- ^ Tow of last-used GNSS position measurement
+  , _msgSolnMetaDepA_last_used_gnss_vel_tow :: !Word32
+    -- ^ Tow of last-used GNSS velocity measurement
+  , _msgSolnMetaDepA_sol_in               :: ![SolutionInputType]
+    -- ^ Array of Metadata describing the sensors potentially involved in the
+    -- solution. Each element in the array represents a single sensor type and
+    -- consists of flags containing (meta)data pertaining to that specific
+    -- single sensor. Refer to each (XX)InputType descriptor in the present
+    -- doc.
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgSolnMetaDepA where
+  get = do
+    _msgSolnMetaDepA_pdop <- getWord16le
+    _msgSolnMetaDepA_hdop <- getWord16le
+    _msgSolnMetaDepA_vdop <- getWord16le
+    _msgSolnMetaDepA_n_sats <- getWord8
+    _msgSolnMetaDepA_age_corrections <- getWord16le
+    _msgSolnMetaDepA_alignment_status <- getWord8
+    _msgSolnMetaDepA_last_used_gnss_pos_tow <- getWord32le
+    _msgSolnMetaDepA_last_used_gnss_vel_tow <- getWord32le
+    _msgSolnMetaDepA_sol_in <- whileM (not <$> isEmpty) get
+    pure MsgSolnMetaDepA {..}
+
+  put MsgSolnMetaDepA {..} = do
+    putWord16le _msgSolnMetaDepA_pdop
+    putWord16le _msgSolnMetaDepA_hdop
+    putWord16le _msgSolnMetaDepA_vdop
+    putWord8 _msgSolnMetaDepA_n_sats
+    putWord16le _msgSolnMetaDepA_age_corrections
+    putWord8 _msgSolnMetaDepA_alignment_status
+    putWord32le _msgSolnMetaDepA_last_used_gnss_pos_tow
+    putWord32le _msgSolnMetaDepA_last_used_gnss_vel_tow
+    mapM_ put _msgSolnMetaDepA_sol_in
+
+$(makeSBP 'msgSolnMetaDepA ''MsgSolnMetaDepA)
+$(makeJSON "_msgSolnMetaDepA_" ''MsgSolnMetaDepA)
+$(makeLenses ''MsgSolnMetaDepA)
+
+msgSolnMeta :: Word16
+msgSolnMeta = 0xFF0E
+
+-- | SBP class for message MSG_SOLN_META (0xFF0E).
+--
+-- This message contains all metadata about the sensors received and/or used
+-- in computing the sensorfusion solution. It focuses primarly, but not only,
+-- on GNSS metadata. Regarding the age of the last received valid GNSS
+-- solution, the highest two bits are time status, indicating whether age gnss
+-- can or can not be used to retrieve time of measurement (noted TOM, also
+-- known as time of validity) If it can, substract 'age gnss' from 'tow' in
+-- navigation messages to get TOM. Can be used before alignment is complete in
+-- the Fusion Engine, when output solution is the last received valid GNSS
+-- solution and its tow is not a TOM.
+data MsgSolnMeta = MsgSolnMeta
+  { _msgSolnMeta_tow           :: !Word32
+    -- ^ GPS time of week rounded to the nearest millisecond
+  , _msgSolnMeta_pdop          :: !Word16
+    -- ^ Position Dilution of Precision as per last available DOPS from PVT
+    -- engine (0xFFFF indicates invalid)
+  , _msgSolnMeta_hdop          :: !Word16
+    -- ^ Horizontal Dilution of Precision as per last available DOPS from PVT
+    -- engine (0xFFFF indicates invalid)
+  , _msgSolnMeta_vdop          :: !Word16
+    -- ^ Vertical Dilution of Precision as per last available DOPS from PVT
+    -- engine (0xFFFF indicates invalid)
+  , _msgSolnMeta_age_corrections :: !Word16
+    -- ^ Age of corrections as per last available AGE_CORRECTIONS from PVT
+    -- engine (0xFFFF indicates invalid)
+  , _msgSolnMeta_age_gnss      :: !Word32
+    -- ^ Age and Time Status of the last received valid GNSS solution.
+  , _msgSolnMeta_sol_in        :: ![SolutionInputType]
+    -- ^ Array of Metadata describing the sensors potentially involved in the
+    -- solution. Each element in the array represents a single sensor type and
+    -- consists of flags containing (meta)data pertaining to that specific
+    -- single sensor. Refer to each (XX)InputType descriptor in the present
+    -- doc.
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgSolnMeta where
+  get = do
+    _msgSolnMeta_tow <- getWord32le
+    _msgSolnMeta_pdop <- getWord16le
+    _msgSolnMeta_hdop <- getWord16le
+    _msgSolnMeta_vdop <- getWord16le
+    _msgSolnMeta_age_corrections <- getWord16le
+    _msgSolnMeta_age_gnss <- getWord32le
+    _msgSolnMeta_sol_in <- whileM (not <$> isEmpty) get
+    pure MsgSolnMeta {..}
+
+  put MsgSolnMeta {..} = do
+    putWord32le _msgSolnMeta_tow
+    putWord16le _msgSolnMeta_pdop
+    putWord16le _msgSolnMeta_hdop
+    putWord16le _msgSolnMeta_vdop
+    putWord16le _msgSolnMeta_age_corrections
+    putWord32le _msgSolnMeta_age_gnss
+    mapM_ put _msgSolnMeta_sol_in
+
+$(makeSBP 'msgSolnMeta ''MsgSolnMeta)
+$(makeJSON "_msgSolnMeta_" ''MsgSolnMeta)
+$(makeLenses ''MsgSolnMeta)
+
+-- | GNSSInputType.
+--
+-- Metadata around the GNSS sensors involved in the fuzed solution. Accessible
+-- through sol_in[N].flags in a MSG_SOLN_META.
+data GNSSInputType = GNSSInputType
+  { _gNSSInputType_flags :: !Word8
+    -- ^ flags that store all relevant info specific to this sensor type.
+  } deriving ( Show, Read, Eq )
+
+instance Binary GNSSInputType where
+  get = do
+    _gNSSInputType_flags <- getWord8
+    pure GNSSInputType {..}
+
+  put GNSSInputType {..} = do
+    putWord8 _gNSSInputType_flags
+
+$(makeJSON "_gNSSInputType_" ''GNSSInputType)
+$(makeLenses ''GNSSInputType)
+
+-- | IMUInputType.
+--
+-- Metadata around the IMU sensors involved in the fuzed solution. Accessible
+-- through sol_in[N].flags in a MSG_SOLN_META.
+data IMUInputType = IMUInputType
+  { _iMUInputType_flags :: !Word8
+    -- ^ Instrument time, grade, and architecture for a sensor.
+  } deriving ( Show, Read, Eq )
+
+instance Binary IMUInputType where
+  get = do
+    _iMUInputType_flags <- getWord8
+    pure IMUInputType {..}
+
+  put IMUInputType {..} = do
+    putWord8 _iMUInputType_flags
+
+$(makeJSON "_iMUInputType_" ''IMUInputType)
+$(makeLenses ''IMUInputType)
+
+-- | OdoInputType.
+--
+-- Metadata around the Odometry sensors involved in the fuzed solution.
+-- Accessible through sol_in[N].flags in a MSG_SOLN_META.
+data OdoInputType = OdoInputType
+  { _odoInputType_flags :: !Word8
+    -- ^ Instrument ODO rate, grade, and quality.
+  } deriving ( Show, Read, Eq )
+
+instance Binary OdoInputType where
+  get = do
+    _odoInputType_flags <- getWord8
+    pure OdoInputType {..}
+
+  put OdoInputType {..} = do
+    putWord8 _odoInputType_flags
+
+$(makeJSON "_odoInputType_" ''OdoInputType)
+$(makeLenses ''OdoInputType)
diff --git a/src/SwiftNav/SBP/Ssr.hs b/src/SwiftNav/SBP/Ssr.hs
--- a/src/SwiftNav/SBP/Ssr.hs
+++ b/src/SwiftNav/SBP/Ssr.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Ssr
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Precise State Space Representation (SSR) corrections format
+-- \< Precise State Space Representation (SSR) corrections format \>
 
 module SwiftNav.SBP.Ssr
   ( module SwiftNav.SBP.Ssr
@@ -38,11 +38,12 @@
 
 -- | CodeBiasesContent.
 --
--- Code biases are to be added to pseudorange. The corrections are conform with
--- typical RTCMv3 MT1059 and 1065.
+-- Code biases are to be added to pseudorange. The corrections conform with
+-- RTCMv3 MT 1059 / 1065.
 data CodeBiasesContent = CodeBiasesContent
   { _codeBiasesContent_code :: !Word8
-    -- ^ Signal constellation, band and code
+    -- ^ Signal encoded following RTCM specifications (DF380, DF381, DF382 and
+    -- DF467).
   , _codeBiasesContent_value :: !Int16
     -- ^ Code bias value
   } deriving ( Show, Read, Eq )
@@ -50,7 +51,7 @@
 instance Binary CodeBiasesContent where
   get = do
     _codeBiasesContent_code <- getWord8
-    _codeBiasesContent_value <- fromIntegral <$> getWord16le
+    _codeBiasesContent_value <- (fromIntegral <$> getWord16le)
     pure CodeBiasesContent {..}
 
   put CodeBiasesContent {..} = do
@@ -62,18 +63,18 @@
 
 -- | PhaseBiasesContent.
 --
--- Phase biases are to be added to carrier phase measurements. The corrections
--- are conform with typical RTCMv3 MT1059 and 1065.
+-- Phase biases are to be added to carrier phase measurements.
 data PhaseBiasesContent = PhaseBiasesContent
   { _phaseBiasesContent_code                     :: !Word8
-    -- ^ Signal constellation, band and code
+    -- ^ Signal encoded following RTCM specifications (DF380, DF381, DF382 and
+    -- DF467)
   , _phaseBiasesContent_integer_indicator        :: !Word8
     -- ^ Indicator for integer property
   , _phaseBiasesContent_widelane_integer_indicator :: !Word8
     -- ^ Indicator for two groups of Wide-Lane(s) integer property
   , _phaseBiasesContent_discontinuity_counter    :: !Word8
-    -- ^ Signal phase discontinuity counter. Increased for every discontinuity in
-    -- phase.
+    -- ^ Signal phase discontinuity counter. Increased for every discontinuity
+    -- in phase.
   , _phaseBiasesContent_bias                     :: !Int32
     -- ^ Phase bias for specified signal
   } deriving ( Show, Read, Eq )
@@ -84,7 +85,7 @@
     _phaseBiasesContent_integer_indicator <- getWord8
     _phaseBiasesContent_widelane_integer_indicator <- getWord8
     _phaseBiasesContent_discontinuity_counter <- getWord8
-    _phaseBiasesContent_bias <- fromIntegral <$> getWord32le
+    _phaseBiasesContent_bias <- (fromIntegral <$> getWord32le)
     pure PhaseBiasesContent {..}
 
   put PhaseBiasesContent {..} = do
@@ -99,98 +100,118 @@
 
 -- | STECHeader.
 --
--- A full set of STEC information will likely span multiple SBP messages, since
--- SBP message a limited to 255 bytes.  The header is used to tie multiple SBP
--- messages into a sequence.
+-- A full set of STEC information will likely span multiple SBP messages,
+-- since SBP message a limited to 255 bytes.  The header is used to tie
+-- multiple SBP messages into a sequence.
 data STECHeader = STECHeader
-  { _sTECHeader_time              :: !GpsTime
-    -- ^ GNSS time of the STEC data
-  , _sTECHeader_num_msgs          :: !Word8
+  { _sTECHeader_tile_set_id   :: !Word16
+    -- ^ Unique identifier of the tile set this tile belongs to.
+  , _sTECHeader_tile_id       :: !Word16
+    -- ^ Unique identifier of this tile in the tile set.
+  , _sTECHeader_time          :: !GpsTimeSec
+    -- ^ GNSS reference time of the correction
+  , _sTECHeader_num_msgs      :: !Word8
     -- ^ Number of messages in the dataset
-  , _sTECHeader_seq_num           :: !Word8
+  , _sTECHeader_seq_num       :: !Word8
     -- ^ Position of this message in the dataset
-  , _sTECHeader_ssr_update_interval :: !Word16
-    -- ^ update interval in seconds
-  , _sTECHeader_iod_ssr           :: !Word8
-    -- ^ range 0 - 15
+  , _sTECHeader_update_interval :: !Word8
+    -- ^ Update interval between consecutive corrections. Encoded following RTCM
+    -- DF391 specification.
+  , _sTECHeader_iod_atmo      :: !Word8
+    -- ^ IOD of the SSR atmospheric correction
   } deriving ( Show, Read, Eq )
 
 instance Binary STECHeader where
   get = do
+    _sTECHeader_tile_set_id <- getWord16le
+    _sTECHeader_tile_id <- getWord16le
     _sTECHeader_time <- get
     _sTECHeader_num_msgs <- getWord8
     _sTECHeader_seq_num <- getWord8
-    _sTECHeader_ssr_update_interval <- getWord16le
-    _sTECHeader_iod_ssr <- getWord8
+    _sTECHeader_update_interval <- getWord8
+    _sTECHeader_iod_atmo <- getWord8
     pure STECHeader {..}
 
   put STECHeader {..} = do
+    putWord16le _sTECHeader_tile_set_id
+    putWord16le _sTECHeader_tile_id
     put _sTECHeader_time
     putWord8 _sTECHeader_num_msgs
     putWord8 _sTECHeader_seq_num
-    putWord16le _sTECHeader_ssr_update_interval
-    putWord8 _sTECHeader_iod_ssr
+    putWord8 _sTECHeader_update_interval
+    putWord8 _sTECHeader_iod_atmo
 
 $(makeJSON "_sTECHeader_" ''STECHeader)
 $(makeLenses ''STECHeader)
 
 -- | GriddedCorrectionHeader.
 --
--- The 3GPP message contains nested variable length arrays which are not
+-- The LPP message contains nested variable length arrays which are not
 -- suppported in SBP, so each grid point will be identified by the index.
 data GriddedCorrectionHeader = GriddedCorrectionHeader
-  { _griddedCorrectionHeader_time              :: !GpsTime
-    -- ^ GNSS time of the STEC data
-  , _griddedCorrectionHeader_num_msgs          :: !Word16
+  { _griddedCorrectionHeader_tile_set_id           :: !Word16
+    -- ^ Unique identifier of the tile set this tile belongs to.
+  , _griddedCorrectionHeader_tile_id               :: !Word16
+    -- ^ Unique identifier of this tile in the tile set.
+  , _griddedCorrectionHeader_time                  :: !GpsTimeSec
+    -- ^ GNSS reference time of the correction
+  , _griddedCorrectionHeader_num_msgs              :: !Word16
     -- ^ Number of messages in the dataset
-  , _griddedCorrectionHeader_seq_num           :: !Word16
+  , _griddedCorrectionHeader_seq_num               :: !Word16
     -- ^ Position of this message in the dataset
-  , _griddedCorrectionHeader_ssr_update_interval :: !Word16
-    -- ^ update interval in seconds
-  , _griddedCorrectionHeader_iod_ssr           :: !Word8
-    -- ^ range 0 - 15
-  , _griddedCorrectionHeader_tropo_quality     :: !Word8
-    -- ^ troposphere quality indicator
+  , _griddedCorrectionHeader_update_interval       :: !Word8
+    -- ^ Update interval between consecutive corrections. Encoded following RTCM
+    -- DF391 specification.
+  , _griddedCorrectionHeader_iod_atmo              :: !Word8
+    -- ^ IOD of the SSR atmospheric correction
+  , _griddedCorrectionHeader_tropo_quality_indicator :: !Word8
+    -- ^ Quality of the troposphere data. Encoded following RTCM DF389
+    -- specification in units of m.
   } deriving ( Show, Read, Eq )
 
 instance Binary GriddedCorrectionHeader where
   get = do
+    _griddedCorrectionHeader_tile_set_id <- getWord16le
+    _griddedCorrectionHeader_tile_id <- getWord16le
     _griddedCorrectionHeader_time <- get
     _griddedCorrectionHeader_num_msgs <- getWord16le
     _griddedCorrectionHeader_seq_num <- getWord16le
-    _griddedCorrectionHeader_ssr_update_interval <- getWord16le
-    _griddedCorrectionHeader_iod_ssr <- getWord8
-    _griddedCorrectionHeader_tropo_quality <- getWord8
+    _griddedCorrectionHeader_update_interval <- getWord8
+    _griddedCorrectionHeader_iod_atmo <- getWord8
+    _griddedCorrectionHeader_tropo_quality_indicator <- getWord8
     pure GriddedCorrectionHeader {..}
 
   put GriddedCorrectionHeader {..} = do
+    putWord16le _griddedCorrectionHeader_tile_set_id
+    putWord16le _griddedCorrectionHeader_tile_id
     put _griddedCorrectionHeader_time
     putWord16le _griddedCorrectionHeader_num_msgs
     putWord16le _griddedCorrectionHeader_seq_num
-    putWord16le _griddedCorrectionHeader_ssr_update_interval
-    putWord8 _griddedCorrectionHeader_iod_ssr
-    putWord8 _griddedCorrectionHeader_tropo_quality
+    putWord8 _griddedCorrectionHeader_update_interval
+    putWord8 _griddedCorrectionHeader_iod_atmo
+    putWord8 _griddedCorrectionHeader_tropo_quality_indicator
 
 $(makeJSON "_griddedCorrectionHeader_" ''GriddedCorrectionHeader)
 $(makeLenses ''GriddedCorrectionHeader)
 
 -- | STECSatElement.
 --
--- STEC for the given satellite.
+-- STEC polynomial for the given satellite.
 data STECSatElement = STECSatElement
   { _sTECSatElement_sv_id                :: !SvId
     -- ^ Unique space vehicle identifier
   , _sTECSatElement_stec_quality_indicator :: !Word8
-    -- ^ quality of STEC data
+    -- ^ Quality of the STEC data. Encoded following RTCM DF389 specification
+    -- but in units of TECU instead of m.
   , _sTECSatElement_stec_coeff           :: ![Int16]
-    -- ^ coefficents of the STEC polynomial
+    -- ^ Coefficents of the STEC polynomial in the order of C00, C01, C10, C11
   } deriving ( Show, Read, Eq )
 
 instance Binary STECSatElement where
   get = do
     _sTECSatElement_sv_id <- get
     _sTECSatElement_stec_quality_indicator <- getWord8
-    _sTECSatElement_stec_coeff <- replicateM 4 fromIntegral <$> getWord16le
+    _sTECSatElement_stec_coeff <- replicateM 4 (fromIntegral <$> getWord16le)
     pure STECSatElement {..}
 
   put STECSatElement {..} = do
@@ -201,134 +222,180 @@
 $(makeJSON "_sTECSatElement_" ''STECSatElement)
 $(makeLenses ''STECSatElement)
 
+-- | TroposphericDelayCorrectionNoStd.
+--
+-- Troposphere vertical delays at the grid point.
+data TroposphericDelayCorrectionNoStd = TroposphericDelayCorrectionNoStd
+  { _troposphericDelayCorrectionNoStd_hydro :: !Int16
+    -- ^ Hydrostatic vertical delay
+  , _troposphericDelayCorrectionNoStd_wet :: !Int8
+    -- ^ Wet vertical delay
+  } deriving ( Show, Read, Eq )
+
+instance Binary TroposphericDelayCorrectionNoStd where
+  get = do
+    _troposphericDelayCorrectionNoStd_hydro <- (fromIntegral <$> getWord16le)
+    _troposphericDelayCorrectionNoStd_wet <- (fromIntegral <$> getWord8)
+    pure TroposphericDelayCorrectionNoStd {..}
+
+  put TroposphericDelayCorrectionNoStd {..} = do
+    (putWord16le . fromIntegral) _troposphericDelayCorrectionNoStd_hydro
+    (putWord8 . fromIntegral) _troposphericDelayCorrectionNoStd_wet
+
+$(makeJSON "_troposphericDelayCorrectionNoStd_" ''TroposphericDelayCorrectionNoStd)
+$(makeLenses ''TroposphericDelayCorrectionNoStd)
+
 -- | TroposphericDelayCorrection.
 --
--- Contains wet vertical and hydrostatic vertical delay
+-- Troposphere vertical delays (mean and standard deviation) at the grid
+-- point.
 data TroposphericDelayCorrection = TroposphericDelayCorrection
   { _troposphericDelayCorrection_hydro :: !Int16
-    -- ^ hydrostatic vertical delay
-  , _troposphericDelayCorrection_wet :: !Int8
-    -- ^ wet vertical delay
+    -- ^ Hydrostatic vertical delay
+  , _troposphericDelayCorrection_wet  :: !Int8
+    -- ^ Wet vertical delay
+  , _troposphericDelayCorrection_stddev :: !Word8
+    -- ^ stddev
   } deriving ( Show, Read, Eq )
 
 instance Binary TroposphericDelayCorrection where
   get = do
-    _troposphericDelayCorrection_hydro <- fromIntegral <$> getWord16le
-    _troposphericDelayCorrection_wet <- fromIntegral <$> getWord8
+    _troposphericDelayCorrection_hydro <- (fromIntegral <$> getWord16le)
+    _troposphericDelayCorrection_wet <- (fromIntegral <$> getWord8)
+    _troposphericDelayCorrection_stddev <- getWord8
     pure TroposphericDelayCorrection {..}
 
   put TroposphericDelayCorrection {..} = do
     (putWord16le . fromIntegral) _troposphericDelayCorrection_hydro
     (putWord8 . fromIntegral) _troposphericDelayCorrection_wet
+    putWord8 _troposphericDelayCorrection_stddev
 
 $(makeJSON "_troposphericDelayCorrection_" ''TroposphericDelayCorrection)
 $(makeLenses ''TroposphericDelayCorrection)
 
+-- | STECResidualNoStd.
+--
+-- STEC residual for the given satellite at the grid point.
+data STECResidualNoStd = STECResidualNoStd
+  { _sTECResidualNoStd_sv_id  :: !SvId
+    -- ^ space vehicle identifier
+  , _sTECResidualNoStd_residual :: !Int16
+    -- ^ STEC residual
+  } deriving ( Show, Read, Eq )
+
+instance Binary STECResidualNoStd where
+  get = do
+    _sTECResidualNoStd_sv_id <- get
+    _sTECResidualNoStd_residual <- (fromIntegral <$> getWord16le)
+    pure STECResidualNoStd {..}
+
+  put STECResidualNoStd {..} = do
+    put _sTECResidualNoStd_sv_id
+    (putWord16le . fromIntegral) _sTECResidualNoStd_residual
+
+$(makeJSON "_sTECResidualNoStd_" ''STECResidualNoStd)
+$(makeLenses ''STECResidualNoStd)
+
 -- | STECResidual.
 --
--- STEC residual
+-- STEC residual (mean and standard deviation) for the given satellite at the
+-- grid point.
 data STECResidual = STECResidual
   { _sTECResidual_sv_id  :: !SvId
     -- ^ space vehicle identifier
   , _sTECResidual_residual :: !Int16
     -- ^ STEC residual
+  , _sTECResidual_stddev :: !Word8
+    -- ^ stddev
   } deriving ( Show, Read, Eq )
 
 instance Binary STECResidual where
   get = do
     _sTECResidual_sv_id <- get
-    _sTECResidual_residual <- fromIntegral <$> getWord16le
+    _sTECResidual_residual <- (fromIntegral <$> getWord16le)
+    _sTECResidual_stddev <- getWord8
     pure STECResidual {..}
 
   put STECResidual {..} = do
     put _sTECResidual_sv_id
     (putWord16le . fromIntegral) _sTECResidual_residual
+    putWord8 _sTECResidual_stddev
 
 $(makeJSON "_sTECResidual_" ''STECResidual)
 $(makeLenses ''STECResidual)
 
+-- | GridElementNoStd.
+--
+-- Contains one tropo delay, plus STEC residuals for each satellite at the
+-- grid point.
+data GridElementNoStd = GridElementNoStd
+  { _gridElementNoStd_index                :: !Word16
+    -- ^ Index of the grid point
+  , _gridElementNoStd_tropo_delay_correction :: !TroposphericDelayCorrectionNoStd
+    -- ^ Wet and hydrostatic vertical delays
+  , _gridElementNoStd_stec_residuals       :: ![STECResidualNoStd]
+    -- ^ STEC residuals for each satellite
+  } deriving ( Show, Read, Eq )
+
+instance Binary GridElementNoStd where
+  get = do
+    _gridElementNoStd_index <- getWord16le
+    _gridElementNoStd_tropo_delay_correction <- get
+    _gridElementNoStd_stec_residuals <- whileM (not <$> isEmpty) get
+    pure GridElementNoStd {..}
+
+  put GridElementNoStd {..} = do
+    putWord16le _gridElementNoStd_index
+    put _gridElementNoStd_tropo_delay_correction
+    mapM_ put _gridElementNoStd_stec_residuals
+
+$(makeJSON "_gridElementNoStd_" ''GridElementNoStd)
+$(makeLenses ''GridElementNoStd)
+
 -- | GridElement.
 --
--- Contains one tropo datum, plus STEC residuals for each space vehicle
+-- Contains one tropo delay (mean and stddev), plus STEC residuals (mean and
+-- stddev) for each satellite at the grid point.
 data GridElement = GridElement
   { _gridElement_index                :: !Word16
-    -- ^ index of the grid point
+    -- ^ Index of the grid point
   , _gridElement_tropo_delay_correction :: !TroposphericDelayCorrection
-    -- ^ Wet and Hydrostatic Vertical Delay
-  , _gridElement_STEC_residuals       :: ![STECResidual]
-    -- ^ STEC Residual for the given space vehicle
+    -- ^ Wet and hydrostatic vertical delays (mean, stddev)
+  , _gridElement_stec_residuals       :: ![STECResidual]
+    -- ^ STEC residuals for each satellite (mean, stddev)
   } deriving ( Show, Read, Eq )
 
 instance Binary GridElement where
   get = do
     _gridElement_index <- getWord16le
     _gridElement_tropo_delay_correction <- get
-    _gridElement_STEC_residuals <- whileM (not <$> isEmpty) get
+    _gridElement_stec_residuals <- whileM (not <$> isEmpty) get
     pure GridElement {..}
 
   put GridElement {..} = do
     putWord16le _gridElement_index
     put _gridElement_tropo_delay_correction
-    mapM_ put _gridElement_STEC_residuals
+    mapM_ put _gridElement_stec_residuals
 
 $(makeJSON "_gridElement_" ''GridElement)
 $(makeLenses ''GridElement)
 
--- | GridDefinitionHeader.
---
--- Defines the grid for STEC and tropo grid messages. Also includes an RLE
--- encoded validity list.
-data GridDefinitionHeader = GridDefinitionHeader
-  { _gridDefinitionHeader_region_size_inverse :: !Word8
-    -- ^ inverse of region size
-  , _gridDefinitionHeader_area_width        :: !Word16
-    -- ^ area width; see spec for details
-  , _gridDefinitionHeader_lat_nw_corner_enc :: !Word16
-    -- ^ encoded latitude of the northwest corner of the grid
-  , _gridDefinitionHeader_lon_nw_corner_enc :: !Word16
-    -- ^ encoded longitude of the northwest corner of the grid
-  , _gridDefinitionHeader_num_msgs          :: !Word8
-    -- ^ Number of messages in the dataset
-  , _gridDefinitionHeader_seq_num           :: !Word8
-    -- ^ Postion of this message in the dataset
-  } deriving ( Show, Read, Eq )
-
-instance Binary GridDefinitionHeader where
-  get = do
-    _gridDefinitionHeader_region_size_inverse <- getWord8
-    _gridDefinitionHeader_area_width <- getWord16le
-    _gridDefinitionHeader_lat_nw_corner_enc <- getWord16le
-    _gridDefinitionHeader_lon_nw_corner_enc <- getWord16le
-    _gridDefinitionHeader_num_msgs <- getWord8
-    _gridDefinitionHeader_seq_num <- getWord8
-    pure GridDefinitionHeader {..}
-
-  put GridDefinitionHeader {..} = do
-    putWord8 _gridDefinitionHeader_region_size_inverse
-    putWord16le _gridDefinitionHeader_area_width
-    putWord16le _gridDefinitionHeader_lat_nw_corner_enc
-    putWord16le _gridDefinitionHeader_lon_nw_corner_enc
-    putWord8 _gridDefinitionHeader_num_msgs
-    putWord8 _gridDefinitionHeader_seq_num
-
-$(makeJSON "_gridDefinitionHeader_" ''GridDefinitionHeader)
-$(makeLenses ''GridDefinitionHeader)
-
 msgSsrOrbitClock :: Word16
 msgSsrOrbitClock = 0x05DD
 
 -- | SBP class for message MSG_SSR_ORBIT_CLOCK (0x05DD).
 --
 -- The precise orbit and clock correction message is to be applied as a delta
--- correction to broadcast ephemeris and is typically an equivalent to the 1060
--- and 1066 RTCM message types
+-- correction to broadcast ephemeris and is an equivalent to the 1060 /1066
+-- RTCM message types.
 data MsgSsrOrbitClock = MsgSsrOrbitClock
   { _msgSsrOrbitClock_time          :: !GpsTimeSec
     -- ^ GNSS reference time of the correction
   , _msgSsrOrbitClock_sid           :: !GnssSignal
     -- ^ GNSS signal identifier (16 bit)
   , _msgSsrOrbitClock_update_interval :: !Word8
-    -- ^ Update interval between consecutive corrections
+    -- ^ Update interval between consecutive corrections. Encoded following RTCM
+    -- DF391 specification.
   , _msgSsrOrbitClock_iod_ssr       :: !Word8
     -- ^ IOD of the SSR correction. A change of Issue Of Data SSR is used to
     -- indicate a change in the SSR generating configuration
@@ -361,15 +428,15 @@
     _msgSsrOrbitClock_update_interval <- getWord8
     _msgSsrOrbitClock_iod_ssr <- getWord8
     _msgSsrOrbitClock_iod <- getWord32le
-    _msgSsrOrbitClock_radial <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClock_along <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClock_cross <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClock_dot_radial <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClock_dot_along <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClock_dot_cross <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClock_c0 <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClock_c1 <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClock_c2 <- fromIntegral <$> getWord32le
+    _msgSsrOrbitClock_radial <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClock_along <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClock_cross <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClock_dot_radial <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClock_dot_along <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClock_dot_cross <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClock_c0 <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClock_c1 <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClock_c2 <- (fromIntegral <$> getWord32le)
     pure MsgSsrOrbitClock {..}
 
   put MsgSsrOrbitClock {..} = do
@@ -392,99 +459,22 @@
 $(makeJSON "_msgSsrOrbitClock_" ''MsgSsrOrbitClock)
 $(makeLenses ''MsgSsrOrbitClock)
 
-msgSsrOrbitClockDepA :: Word16
-msgSsrOrbitClockDepA = 0x05DC
-
--- | SBP class for message MSG_SSR_ORBIT_CLOCK_DEP_A (0x05DC).
---
--- The precise orbit and clock correction message is to be applied as a delta
--- correction to broadcast ephemeris and is typically an equivalent to the 1060
--- and 1066 RTCM message types
-data MsgSsrOrbitClockDepA = MsgSsrOrbitClockDepA
-  { _msgSsrOrbitClockDepA_time          :: !GpsTimeSec
-    -- ^ GNSS reference time of the correction
-  , _msgSsrOrbitClockDepA_sid           :: !GnssSignal
-    -- ^ GNSS signal identifier (16 bit)
-  , _msgSsrOrbitClockDepA_update_interval :: !Word8
-    -- ^ Update interval between consecutive corrections
-  , _msgSsrOrbitClockDepA_iod_ssr       :: !Word8
-    -- ^ IOD of the SSR correction. A change of Issue Of Data SSR is used to
-    -- indicate a change in the SSR generating configuration
-  , _msgSsrOrbitClockDepA_iod           :: !Word8
-    -- ^ Issue of broadcast ephemeris data
-  , _msgSsrOrbitClockDepA_radial        :: !Int32
-    -- ^ Orbit radial delta correction
-  , _msgSsrOrbitClockDepA_along         :: !Int32
-    -- ^ Orbit along delta correction
-  , _msgSsrOrbitClockDepA_cross         :: !Int32
-    -- ^ Orbit along delta correction
-  , _msgSsrOrbitClockDepA_dot_radial    :: !Int32
-    -- ^ Velocity of orbit radial delta correction
-  , _msgSsrOrbitClockDepA_dot_along     :: !Int32
-    -- ^ Velocity of orbit along delta correction
-  , _msgSsrOrbitClockDepA_dot_cross     :: !Int32
-    -- ^ Velocity of orbit cross delta correction
-  , _msgSsrOrbitClockDepA_c0            :: !Int32
-    -- ^ C0 polynomial coefficient for correction of broadcast satellite clock
-  , _msgSsrOrbitClockDepA_c1            :: !Int32
-    -- ^ C1 polynomial coefficient for correction of broadcast satellite clock
-  , _msgSsrOrbitClockDepA_c2            :: !Int32
-    -- ^ C2 polynomial coefficient for correction of broadcast satellite clock
-  } deriving ( Show, Read, Eq )
-
-instance Binary MsgSsrOrbitClockDepA where
-  get = do
-    _msgSsrOrbitClockDepA_time <- get
-    _msgSsrOrbitClockDepA_sid <- get
-    _msgSsrOrbitClockDepA_update_interval <- getWord8
-    _msgSsrOrbitClockDepA_iod_ssr <- getWord8
-    _msgSsrOrbitClockDepA_iod <- getWord8
-    _msgSsrOrbitClockDepA_radial <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClockDepA_along <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClockDepA_cross <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClockDepA_dot_radial <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClockDepA_dot_along <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClockDepA_dot_cross <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClockDepA_c0 <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClockDepA_c1 <- fromIntegral <$> getWord32le
-    _msgSsrOrbitClockDepA_c2 <- fromIntegral <$> getWord32le
-    pure MsgSsrOrbitClockDepA {..}
-
-  put MsgSsrOrbitClockDepA {..} = do
-    put _msgSsrOrbitClockDepA_time
-    put _msgSsrOrbitClockDepA_sid
-    putWord8 _msgSsrOrbitClockDepA_update_interval
-    putWord8 _msgSsrOrbitClockDepA_iod_ssr
-    putWord8 _msgSsrOrbitClockDepA_iod
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_radial
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_along
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_cross
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_dot_radial
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_dot_along
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_dot_cross
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_c0
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_c1
-    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_c2
-
-$(makeSBP 'msgSsrOrbitClockDepA ''MsgSsrOrbitClockDepA)
-$(makeJSON "_msgSsrOrbitClockDepA_" ''MsgSsrOrbitClockDepA)
-$(makeLenses ''MsgSsrOrbitClockDepA)
-
 msgSsrCodeBiases :: Word16
 msgSsrCodeBiases = 0x05E1
 
 -- | SBP class for message MSG_SSR_CODE_BIASES (0x05E1).
 --
 -- The precise code biases message is to be added to the pseudorange of the
--- corresponding signal to get corrected pseudorange. It is typically an
--- equivalent to the 1059 and 1065 RTCM message types
+-- corresponding signal to get corrected pseudorange. It is an equivalent to
+-- the 1059 / 1065 RTCM message types.
 data MsgSsrCodeBiases = MsgSsrCodeBiases
   { _msgSsrCodeBiases_time          :: !GpsTimeSec
     -- ^ GNSS reference time of the correction
   , _msgSsrCodeBiases_sid           :: !GnssSignal
     -- ^ GNSS signal identifier (16 bit)
   , _msgSsrCodeBiases_update_interval :: !Word8
-    -- ^ Update interval between consecutive corrections
+    -- ^ Update interval between consecutive corrections. Encoded following RTCM
+    -- DF391 specification.
   , _msgSsrCodeBiases_iod_ssr       :: !Word8
     -- ^ IOD of the SSR correction. A change of Issue Of Data SSR is used to
     -- indicate a change in the SSR generating configuration
@@ -519,16 +509,17 @@
 --
 -- The precise phase biases message contains the biases to be added to the
 -- carrier phase of the corresponding signal to get corrected carrier phase
--- measurement, as well as the satellite yaw angle to be applied to compute the
--- phase wind-up correction. It is typically an equivalent to the 1265 RTCM
--- message types
+-- measurement, as well as the satellite yaw angle to be applied to compute
+-- the phase wind-up correction. It is typically an equivalent to the 1265
+-- RTCM message types.
 data MsgSsrPhaseBiases = MsgSsrPhaseBiases
   { _msgSsrPhaseBiases_time          :: !GpsTimeSec
     -- ^ GNSS reference time of the correction
   , _msgSsrPhaseBiases_sid           :: !GnssSignal
     -- ^ GNSS signal identifier (16 bit)
   , _msgSsrPhaseBiases_update_interval :: !Word8
-    -- ^ Update interval between consecutive corrections
+    -- ^ Update interval between consecutive corrections. Encoded following RTCM
+    -- DF391 specification.
   , _msgSsrPhaseBiases_iod_ssr       :: !Word8
     -- ^ IOD of the SSR correction. A change of Issue Of Data SSR is used to
     -- indicate a change in the SSR generating configuration
@@ -553,7 +544,7 @@
     _msgSsrPhaseBiases_dispersive_bias <- getWord8
     _msgSsrPhaseBiases_mw_consistency <- getWord8
     _msgSsrPhaseBiases_yaw <- getWord16le
-    _msgSsrPhaseBiases_yaw_rate <- fromIntegral <$> getWord8
+    _msgSsrPhaseBiases_yaw_rate <- (fromIntegral <$> getWord8)
     _msgSsrPhaseBiases_biases <- whileM (not <$> isEmpty) get
     pure MsgSsrPhaseBiases {..}
 
@@ -573,18 +564,21 @@
 $(makeLenses ''MsgSsrPhaseBiases)
 
 msgSsrStecCorrection :: Word16
-msgSsrStecCorrection = 0x05EB
+msgSsrStecCorrection = 0x05FB
 
--- | SBP class for message MSG_SSR_STEC_CORRECTION (0x05EB).
+-- | SBP class for message MSG_SSR_STEC_CORRECTION (0x05FB).
 --
--- The STEC per space vehicle, given as polynomial approximation for a given
--- grid.  This should be combined with SSR-GriddedCorrection message to get the
--- state space representation of the atmospheric delay.
+-- The Slant Total Electron Content per space vehicle, given as polynomial
+-- approximation for a given tile. This should be combined with the
+-- MSG_SSR_GRIDDED_CORRECTION message to get the state space representation of
+-- the atmospheric delay.
+--
+-- It is typically equivalent to the QZSS CLAS Sub Type 8 messages.
 data MsgSsrStecCorrection = MsgSsrStecCorrection
   { _msgSsrStecCorrection_header      :: !STECHeader
-    -- ^ Header of a STEC message
+    -- ^ Header of a STEC polynomial coeffcient message.
   , _msgSsrStecCorrection_stec_sat_list :: ![STECSatElement]
-    -- ^ Array of STEC information for each space vehicle
+    -- ^ Array of STEC polynomial coeffcients for each space vehicle.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSsrStecCorrection where
@@ -602,16 +596,18 @@
 $(makeLenses ''MsgSsrStecCorrection)
 
 msgSsrGriddedCorrection :: Word16
-msgSsrGriddedCorrection = 0x05F0
+msgSsrGriddedCorrection = 0x05FC
 
--- | SBP class for message MSG_SSR_GRIDDED_CORRECTION (0x05F0).
+-- | SBP class for message MSG_SSR_GRIDDED_CORRECTION (0x05FC).
 --
--- STEC residuals are per space vehicle, tropo is not.
+-- STEC residuals are per space vehicle, troposphere is not.
+--
+-- It is typically equivalent to the QZSS CLAS Sub Type 9 messages.
 data MsgSsrGriddedCorrection = MsgSsrGriddedCorrection
   { _msgSsrGriddedCorrection_header :: !GriddedCorrectionHeader
-    -- ^ Header of a Gridded Correction message
+    -- ^ Header of a gridded correction message
   , _msgSsrGriddedCorrection_element :: !GridElement
-    -- ^ Tropo and STEC residuals for the given grid point
+    -- ^ Tropo and STEC residuals for the given grid point.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSsrGriddedCorrection where
@@ -628,32 +624,455 @@
 $(makeJSON "_msgSsrGriddedCorrection_" ''MsgSsrGriddedCorrection)
 $(makeLenses ''MsgSsrGriddedCorrection)
 
-msgSsrGridDefinition :: Word16
-msgSsrGridDefinition = 0x05F5
+msgSsrTileDefinition :: Word16
+msgSsrTileDefinition = 0x05F6
 
--- | SBP class for message MSG_SSR_GRID_DEFINITION (0x05F5).
+-- | SBP class for message MSG_SSR_TILE_DEFINITION (0x05F6).
 --
--- Definition of the grid for STEC and tropo messages
-data MsgSsrGridDefinition = MsgSsrGridDefinition
-  { _msgSsrGridDefinition_header :: !GridDefinitionHeader
+-- Provides the correction point coordinates for the atmospheric correction
+-- values in the MSG_SSR_STEC_CORRECTION and MSG_SSR_GRIDDED_CORRECTION
+-- messages.
+--
+-- Based on ETSI TS 137 355 V16.1.0 (LTE Positioning Protocol) information
+-- element GNSS-SSR-CorrectionPoints. SBP only supports gridded arrays of
+-- correction points, not lists of points.
+data MsgSsrTileDefinition = MsgSsrTileDefinition
+  { _msgSsrTileDefinition_tile_set_id :: !Word16
+    -- ^ Unique identifier of the tile set this tile belongs to.
+  , _msgSsrTileDefinition_tile_id     :: !Word16
+    -- ^ Unique identifier of this tile in the tile set.
+    -- See GNSS-SSR-ArrayOfCorrectionPoints field correctionPointSetID.
+  , _msgSsrTileDefinition_corner_nw_lat :: !Int16
+    -- ^ North-West corner correction point latitude.
+    --
+    -- The relation between the latitude X in the range [-90, 90] and the
+    -- coded number N is:
+    --
+    -- N = floor((X / 90) * 2^14)
+    --
+    -- See GNSS-SSR-ArrayOfCorrectionPoints field referencePointLatitude.
+  , _msgSsrTileDefinition_corner_nw_lon :: !Int16
+    -- ^ North-West corner correction point longitude.
+    --
+    -- The relation between the longitude X in the range [-180, 180] and the
+    -- coded number N is:
+    --
+    -- N = floor((X / 180) * 2^15)
+    --
+    -- See GNSS-SSR-ArrayOfCorrectionPoints field referencePointLongitude.
+  , _msgSsrTileDefinition_spacing_lat :: !Word16
+    -- ^ Spacing of the correction points in the latitude direction.
+    --
+    -- See GNSS-SSR-ArrayOfCorrectionPoints field stepOfLatitude.
+  , _msgSsrTileDefinition_spacing_lon :: !Word16
+    -- ^ Spacing of the correction points in the longitude direction.
+    --
+    -- See GNSS-SSR-ArrayOfCorrectionPoints field stepOfLongitude.
+  , _msgSsrTileDefinition_rows        :: !Word16
+    -- ^ Number of steps in the latitude direction.
+    --
+    -- See GNSS-SSR-ArrayOfCorrectionPoints field numberOfStepsLatitude.
+  , _msgSsrTileDefinition_cols        :: !Word16
+    -- ^ Number of steps in the longitude direction.
+    --
+    -- See GNSS-SSR-ArrayOfCorrectionPoints field numberOfStepsLongitude.
+  , _msgSsrTileDefinition_bitmask     :: !Word64
+    -- ^ Specifies the availability of correction data at the correction points
+    -- in the array.
+    --
+    -- If a specific bit is enabled (set to 1), the correction is not
+    -- available. Only the first rows * cols bits are used, the remainder are
+    -- set to 0. If there are more then 64 correction points the remaining
+    -- corrections are always available.
+    --
+    -- Starting with the northwest corner of the array (top left on a north
+    -- oriented map) the correction points are enumerated with row precedence
+    -- - first row west to east, second row west to east, until last row west
+    -- to east - ending with the southeast corner of the array.
+    --
+    -- See GNSS-SSR-ArrayOfCorrectionPoints field bitmaskOfGrids but note the
+    -- definition of the bits is inverted.
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgSsrTileDefinition where
+  get = do
+    _msgSsrTileDefinition_tile_set_id <- getWord16le
+    _msgSsrTileDefinition_tile_id <- getWord16le
+    _msgSsrTileDefinition_corner_nw_lat <- (fromIntegral <$> getWord16le)
+    _msgSsrTileDefinition_corner_nw_lon <- (fromIntegral <$> getWord16le)
+    _msgSsrTileDefinition_spacing_lat <- getWord16le
+    _msgSsrTileDefinition_spacing_lon <- getWord16le
+    _msgSsrTileDefinition_rows <- getWord16le
+    _msgSsrTileDefinition_cols <- getWord16le
+    _msgSsrTileDefinition_bitmask <- getWord64le
+    pure MsgSsrTileDefinition {..}
+
+  put MsgSsrTileDefinition {..} = do
+    putWord16le _msgSsrTileDefinition_tile_set_id
+    putWord16le _msgSsrTileDefinition_tile_id
+    (putWord16le . fromIntegral) _msgSsrTileDefinition_corner_nw_lat
+    (putWord16le . fromIntegral) _msgSsrTileDefinition_corner_nw_lon
+    putWord16le _msgSsrTileDefinition_spacing_lat
+    putWord16le _msgSsrTileDefinition_spacing_lon
+    putWord16le _msgSsrTileDefinition_rows
+    putWord16le _msgSsrTileDefinition_cols
+    putWord64le _msgSsrTileDefinition_bitmask
+
+$(makeSBP 'msgSsrTileDefinition ''MsgSsrTileDefinition)
+$(makeJSON "_msgSsrTileDefinition_" ''MsgSsrTileDefinition)
+$(makeLenses ''MsgSsrTileDefinition)
+
+-- | SatelliteAPC.
+--
+-- Contains phase center offset and elevation variation corrections for one
+-- signal on a satellite.
+data SatelliteAPC = SatelliteAPC
+  { _satelliteAPC_sid    :: !GnssSignal
+    -- ^ GNSS signal identifier (16 bit)
+  , _satelliteAPC_sat_info :: !Word8
+    -- ^ Additional satellite information
+  , _satelliteAPC_svn    :: !Word16
+    -- ^ Satellite Code, as defined by IGS. Typically the space vehicle number.
+  , _satelliteAPC_pco    :: ![Int16]
+    -- ^ Mean phase center offset, X Y and Z axises. See IGS ANTEX file format
+    -- description for coordinate system definition.
+  , _satelliteAPC_pcv    :: ![Int8]
+    -- ^ Elevation dependent phase center variations. First element is 0 degrees
+    -- separation from the Z axis, subsequent elements represent elevation
+    -- variations in 1 degree increments.
+  } deriving ( Show, Read, Eq )
+
+instance Binary SatelliteAPC where
+  get = do
+    _satelliteAPC_sid <- get
+    _satelliteAPC_sat_info <- getWord8
+    _satelliteAPC_svn <- getWord16le
+    _satelliteAPC_pco <- replicateM 3 (fromIntegral <$> getWord16le)
+    _satelliteAPC_pcv <- replicateM 21 (fromIntegral <$> getWord8)
+    pure SatelliteAPC {..}
+
+  put SatelliteAPC {..} = do
+    put _satelliteAPC_sid
+    putWord8 _satelliteAPC_sat_info
+    putWord16le _satelliteAPC_svn
+    mapM_ (putWord16le . fromIntegral) _satelliteAPC_pco
+    mapM_ (putWord8 . fromIntegral) _satelliteAPC_pcv
+
+$(makeJSON "_satelliteAPC_" ''SatelliteAPC)
+$(makeLenses ''SatelliteAPC)
+
+msgSsrSatelliteApc :: Word16
+msgSsrSatelliteApc = 0x0604
+
+data MsgSsrSatelliteApc = MsgSsrSatelliteApc
+  { _msgSsrSatelliteApc_apc :: ![SatelliteAPC]
+    -- ^ Satellite antenna phase center corrections
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgSsrSatelliteApc where
+  get = do
+    _msgSsrSatelliteApc_apc <- whileM (not <$> isEmpty) get
+    pure MsgSsrSatelliteApc {..}
+
+  put MsgSsrSatelliteApc {..} = do
+    mapM_ put _msgSsrSatelliteApc_apc
+
+$(makeSBP 'msgSsrSatelliteApc ''MsgSsrSatelliteApc)
+$(makeJSON "_msgSsrSatelliteApc_" ''MsgSsrSatelliteApc)
+$(makeLenses ''MsgSsrSatelliteApc)
+
+msgSsrOrbitClockDepA :: Word16
+msgSsrOrbitClockDepA = 0x05DC
+
+data MsgSsrOrbitClockDepA = MsgSsrOrbitClockDepA
+  { _msgSsrOrbitClockDepA_time          :: !GpsTimeSec
+    -- ^ GNSS reference time of the correction
+  , _msgSsrOrbitClockDepA_sid           :: !GnssSignal
+    -- ^ GNSS signal identifier (16 bit)
+  , _msgSsrOrbitClockDepA_update_interval :: !Word8
+    -- ^ Update interval between consecutive corrections. Encoded following RTCM
+    -- DF391 specification.
+  , _msgSsrOrbitClockDepA_iod_ssr       :: !Word8
+    -- ^ IOD of the SSR correction. A change of Issue Of Data SSR is used to
+    -- indicate a change in the SSR generating configuration
+  , _msgSsrOrbitClockDepA_iod           :: !Word8
+    -- ^ Issue of broadcast ephemeris data
+  , _msgSsrOrbitClockDepA_radial        :: !Int32
+    -- ^ Orbit radial delta correction
+  , _msgSsrOrbitClockDepA_along         :: !Int32
+    -- ^ Orbit along delta correction
+  , _msgSsrOrbitClockDepA_cross         :: !Int32
+    -- ^ Orbit along delta correction
+  , _msgSsrOrbitClockDepA_dot_radial    :: !Int32
+    -- ^ Velocity of orbit radial delta correction
+  , _msgSsrOrbitClockDepA_dot_along     :: !Int32
+    -- ^ Velocity of orbit along delta correction
+  , _msgSsrOrbitClockDepA_dot_cross     :: !Int32
+    -- ^ Velocity of orbit cross delta correction
+  , _msgSsrOrbitClockDepA_c0            :: !Int32
+    -- ^ C0 polynomial coefficient for correction of broadcast satellite clock
+  , _msgSsrOrbitClockDepA_c1            :: !Int32
+    -- ^ C1 polynomial coefficient for correction of broadcast satellite clock
+  , _msgSsrOrbitClockDepA_c2            :: !Int32
+    -- ^ C2 polynomial coefficient for correction of broadcast satellite clock
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgSsrOrbitClockDepA where
+  get = do
+    _msgSsrOrbitClockDepA_time <- get
+    _msgSsrOrbitClockDepA_sid <- get
+    _msgSsrOrbitClockDepA_update_interval <- getWord8
+    _msgSsrOrbitClockDepA_iod_ssr <- getWord8
+    _msgSsrOrbitClockDepA_iod <- getWord8
+    _msgSsrOrbitClockDepA_radial <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClockDepA_along <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClockDepA_cross <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClockDepA_dot_radial <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClockDepA_dot_along <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClockDepA_dot_cross <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClockDepA_c0 <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClockDepA_c1 <- (fromIntegral <$> getWord32le)
+    _msgSsrOrbitClockDepA_c2 <- (fromIntegral <$> getWord32le)
+    pure MsgSsrOrbitClockDepA {..}
+
+  put MsgSsrOrbitClockDepA {..} = do
+    put _msgSsrOrbitClockDepA_time
+    put _msgSsrOrbitClockDepA_sid
+    putWord8 _msgSsrOrbitClockDepA_update_interval
+    putWord8 _msgSsrOrbitClockDepA_iod_ssr
+    putWord8 _msgSsrOrbitClockDepA_iod
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_radial
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_along
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_cross
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_dot_radial
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_dot_along
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_dot_cross
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_c0
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_c1
+    (putWord32le . fromIntegral) _msgSsrOrbitClockDepA_c2
+
+$(makeSBP 'msgSsrOrbitClockDepA ''MsgSsrOrbitClockDepA)
+$(makeJSON "_msgSsrOrbitClockDepA_" ''MsgSsrOrbitClockDepA)
+$(makeLenses ''MsgSsrOrbitClockDepA)
+
+-- | STECHeaderDepA.
+--
+-- A full set of STEC information will likely span multiple SBP messages,
+-- since SBP message a limited to 255 bytes.  The header is used to tie
+-- multiple SBP messages into a sequence.
+data STECHeaderDepA = STECHeaderDepA
+  { _sTECHeaderDepA_time          :: !GpsTimeSec
+    -- ^ GNSS reference time of the correction
+  , _sTECHeaderDepA_num_msgs      :: !Word8
+    -- ^ Number of messages in the dataset
+  , _sTECHeaderDepA_seq_num       :: !Word8
+    -- ^ Position of this message in the dataset
+  , _sTECHeaderDepA_update_interval :: !Word8
+    -- ^ Update interval between consecutive corrections. Encoded following RTCM
+    -- DF391 specification.
+  , _sTECHeaderDepA_iod_atmo      :: !Word8
+    -- ^ IOD of the SSR atmospheric correction
+  } deriving ( Show, Read, Eq )
+
+instance Binary STECHeaderDepA where
+  get = do
+    _sTECHeaderDepA_time <- get
+    _sTECHeaderDepA_num_msgs <- getWord8
+    _sTECHeaderDepA_seq_num <- getWord8
+    _sTECHeaderDepA_update_interval <- getWord8
+    _sTECHeaderDepA_iod_atmo <- getWord8
+    pure STECHeaderDepA {..}
+
+  put STECHeaderDepA {..} = do
+    put _sTECHeaderDepA_time
+    putWord8 _sTECHeaderDepA_num_msgs
+    putWord8 _sTECHeaderDepA_seq_num
+    putWord8 _sTECHeaderDepA_update_interval
+    putWord8 _sTECHeaderDepA_iod_atmo
+
+$(makeJSON "_sTECHeaderDepA_" ''STECHeaderDepA)
+$(makeLenses ''STECHeaderDepA)
+
+-- | GriddedCorrectionHeaderDepA.
+--
+-- The 3GPP message contains nested variable length arrays which are not
+-- suppported in SBP, so each grid point will be identified by the index.
+data GriddedCorrectionHeaderDepA = GriddedCorrectionHeaderDepA
+  { _griddedCorrectionHeaderDepA_time                  :: !GpsTimeSec
+    -- ^ GNSS reference time of the correction
+  , _griddedCorrectionHeaderDepA_num_msgs              :: !Word16
+    -- ^ Number of messages in the dataset
+  , _griddedCorrectionHeaderDepA_seq_num               :: !Word16
+    -- ^ Position of this message in the dataset
+  , _griddedCorrectionHeaderDepA_update_interval       :: !Word8
+    -- ^ Update interval between consecutive corrections. Encoded following RTCM
+    -- DF391 specification.
+  , _griddedCorrectionHeaderDepA_iod_atmo              :: !Word8
+    -- ^ IOD of the SSR atmospheric correction
+  , _griddedCorrectionHeaderDepA_tropo_quality_indicator :: !Word8
+    -- ^ Quality of the troposphere data. Encoded following RTCM DF389
+    -- specifcation in units of m.
+  } deriving ( Show, Read, Eq )
+
+instance Binary GriddedCorrectionHeaderDepA where
+  get = do
+    _griddedCorrectionHeaderDepA_time <- get
+    _griddedCorrectionHeaderDepA_num_msgs <- getWord16le
+    _griddedCorrectionHeaderDepA_seq_num <- getWord16le
+    _griddedCorrectionHeaderDepA_update_interval <- getWord8
+    _griddedCorrectionHeaderDepA_iod_atmo <- getWord8
+    _griddedCorrectionHeaderDepA_tropo_quality_indicator <- getWord8
+    pure GriddedCorrectionHeaderDepA {..}
+
+  put GriddedCorrectionHeaderDepA {..} = do
+    put _griddedCorrectionHeaderDepA_time
+    putWord16le _griddedCorrectionHeaderDepA_num_msgs
+    putWord16le _griddedCorrectionHeaderDepA_seq_num
+    putWord8 _griddedCorrectionHeaderDepA_update_interval
+    putWord8 _griddedCorrectionHeaderDepA_iod_atmo
+    putWord8 _griddedCorrectionHeaderDepA_tropo_quality_indicator
+
+$(makeJSON "_griddedCorrectionHeaderDepA_" ''GriddedCorrectionHeaderDepA)
+$(makeLenses ''GriddedCorrectionHeaderDepA)
+
+-- | GridDefinitionHeaderDepA.
+--
+-- Defines the grid for MSG_SSR_GRIDDED_CORRECTION messages. Also includes an
+-- RLE encoded validity list.
+data GridDefinitionHeaderDepA = GridDefinitionHeaderDepA
+  { _gridDefinitionHeaderDepA_region_size_inverse :: !Word8
+    -- ^ region_size (deg) = 10 / region_size_inverse 0 is an invalid value.
+  , _gridDefinitionHeaderDepA_area_width        :: !Word16
+    -- ^ grid height (deg) = grid width (deg) = area_width / region_size 0 is an
+    -- invalid value.
+  , _gridDefinitionHeaderDepA_lat_nw_corner_enc :: !Word16
+    -- ^ North-West corner latitude (deg) = region_size * lat_nw_corner_enc - 90
+  , _gridDefinitionHeaderDepA_lon_nw_corner_enc :: !Word16
+    -- ^ North-West corner longitude (deg) = region_size * lon_nw_corner_enc -
+    -- 180
+  , _gridDefinitionHeaderDepA_num_msgs          :: !Word8
+    -- ^ Number of messages in the dataset
+  , _gridDefinitionHeaderDepA_seq_num           :: !Word8
+    -- ^ Postion of this message in the dataset
+  } deriving ( Show, Read, Eq )
+
+instance Binary GridDefinitionHeaderDepA where
+  get = do
+    _gridDefinitionHeaderDepA_region_size_inverse <- getWord8
+    _gridDefinitionHeaderDepA_area_width <- getWord16le
+    _gridDefinitionHeaderDepA_lat_nw_corner_enc <- getWord16le
+    _gridDefinitionHeaderDepA_lon_nw_corner_enc <- getWord16le
+    _gridDefinitionHeaderDepA_num_msgs <- getWord8
+    _gridDefinitionHeaderDepA_seq_num <- getWord8
+    pure GridDefinitionHeaderDepA {..}
+
+  put GridDefinitionHeaderDepA {..} = do
+    putWord8 _gridDefinitionHeaderDepA_region_size_inverse
+    putWord16le _gridDefinitionHeaderDepA_area_width
+    putWord16le _gridDefinitionHeaderDepA_lat_nw_corner_enc
+    putWord16le _gridDefinitionHeaderDepA_lon_nw_corner_enc
+    putWord8 _gridDefinitionHeaderDepA_num_msgs
+    putWord8 _gridDefinitionHeaderDepA_seq_num
+
+$(makeJSON "_gridDefinitionHeaderDepA_" ''GridDefinitionHeaderDepA)
+$(makeLenses ''GridDefinitionHeaderDepA)
+
+msgSsrStecCorrectionDepA :: Word16
+msgSsrStecCorrectionDepA = 0x05EB
+
+data MsgSsrStecCorrectionDepA = MsgSsrStecCorrectionDepA
+  { _msgSsrStecCorrectionDepA_header      :: !STECHeaderDepA
+    -- ^ Header of a STEC message
+  , _msgSsrStecCorrectionDepA_stec_sat_list :: ![STECSatElement]
+    -- ^ Array of STEC information for each space vehicle
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgSsrStecCorrectionDepA where
+  get = do
+    _msgSsrStecCorrectionDepA_header <- get
+    _msgSsrStecCorrectionDepA_stec_sat_list <- whileM (not <$> isEmpty) get
+    pure MsgSsrStecCorrectionDepA {..}
+
+  put MsgSsrStecCorrectionDepA {..} = do
+    put _msgSsrStecCorrectionDepA_header
+    mapM_ put _msgSsrStecCorrectionDepA_stec_sat_list
+
+$(makeSBP 'msgSsrStecCorrectionDepA ''MsgSsrStecCorrectionDepA)
+$(makeJSON "_msgSsrStecCorrectionDepA_" ''MsgSsrStecCorrectionDepA)
+$(makeLenses ''MsgSsrStecCorrectionDepA)
+
+msgSsrGriddedCorrectionNoStdDepA :: Word16
+msgSsrGriddedCorrectionNoStdDepA = 0x05F0
+
+data MsgSsrGriddedCorrectionNoStdDepA = MsgSsrGriddedCorrectionNoStdDepA
+  { _msgSsrGriddedCorrectionNoStdDepA_header :: !GriddedCorrectionHeaderDepA
     -- ^ Header of a Gridded Correction message
-  , _msgSsrGridDefinition_rle_list :: ![Word8]
+  , _msgSsrGriddedCorrectionNoStdDepA_element :: !GridElementNoStd
+    -- ^ Tropo and STEC residuals for the given grid point
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgSsrGriddedCorrectionNoStdDepA where
+  get = do
+    _msgSsrGriddedCorrectionNoStdDepA_header <- get
+    _msgSsrGriddedCorrectionNoStdDepA_element <- get
+    pure MsgSsrGriddedCorrectionNoStdDepA {..}
+
+  put MsgSsrGriddedCorrectionNoStdDepA {..} = do
+    put _msgSsrGriddedCorrectionNoStdDepA_header
+    put _msgSsrGriddedCorrectionNoStdDepA_element
+
+$(makeSBP 'msgSsrGriddedCorrectionNoStdDepA ''MsgSsrGriddedCorrectionNoStdDepA)
+$(makeJSON "_msgSsrGriddedCorrectionNoStdDepA_" ''MsgSsrGriddedCorrectionNoStdDepA)
+$(makeLenses ''MsgSsrGriddedCorrectionNoStdDepA)
+
+msgSsrGriddedCorrectionDepA :: Word16
+msgSsrGriddedCorrectionDepA = 0x05FA
+
+data MsgSsrGriddedCorrectionDepA = MsgSsrGriddedCorrectionDepA
+  { _msgSsrGriddedCorrectionDepA_header :: !GriddedCorrectionHeaderDepA
+    -- ^ Header of a Gridded Correction message
+  , _msgSsrGriddedCorrectionDepA_element :: !GridElement
+    -- ^ Tropo and STEC residuals for the given grid point (mean and standard
+    -- deviation)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgSsrGriddedCorrectionDepA where
+  get = do
+    _msgSsrGriddedCorrectionDepA_header <- get
+    _msgSsrGriddedCorrectionDepA_element <- get
+    pure MsgSsrGriddedCorrectionDepA {..}
+
+  put MsgSsrGriddedCorrectionDepA {..} = do
+    put _msgSsrGriddedCorrectionDepA_header
+    put _msgSsrGriddedCorrectionDepA_element
+
+$(makeSBP 'msgSsrGriddedCorrectionDepA ''MsgSsrGriddedCorrectionDepA)
+$(makeJSON "_msgSsrGriddedCorrectionDepA_" ''MsgSsrGriddedCorrectionDepA)
+$(makeLenses ''MsgSsrGriddedCorrectionDepA)
+
+msgSsrGridDefinitionDepA :: Word16
+msgSsrGridDefinitionDepA = 0x05F5
+
+data MsgSsrGridDefinitionDepA = MsgSsrGridDefinitionDepA
+  { _msgSsrGridDefinitionDepA_header :: !GridDefinitionHeaderDepA
+    -- ^ Header of a Gridded Correction message
+  , _msgSsrGridDefinitionDepA_rle_list :: ![Word8]
     -- ^ Run Length Encode list of quadrants that contain valid data. The spec
     -- describes the encoding scheme in detail, but essentially the index of
     -- the quadrants that contain transitions between valid and invalid (and
     -- vice versa) are encoded as u8 integers.
   } deriving ( Show, Read, Eq )
 
-instance Binary MsgSsrGridDefinition where
+instance Binary MsgSsrGridDefinitionDepA where
   get = do
-    _msgSsrGridDefinition_header <- get
-    _msgSsrGridDefinition_rle_list <- whileM (not <$> isEmpty) getWord8
-    pure MsgSsrGridDefinition {..}
+    _msgSsrGridDefinitionDepA_header <- get
+    _msgSsrGridDefinitionDepA_rle_list <- whileM (not <$> isEmpty) getWord8
+    pure MsgSsrGridDefinitionDepA {..}
 
-  put MsgSsrGridDefinition {..} = do
-    put _msgSsrGridDefinition_header
-    mapM_ putWord8 _msgSsrGridDefinition_rle_list
+  put MsgSsrGridDefinitionDepA {..} = do
+    put _msgSsrGridDefinitionDepA_header
+    mapM_ putWord8 _msgSsrGridDefinitionDepA_rle_list
 
-$(makeSBP 'msgSsrGridDefinition ''MsgSsrGridDefinition)
-$(makeJSON "_msgSsrGridDefinition_" ''MsgSsrGridDefinition)
-$(makeLenses ''MsgSsrGridDefinition)
+$(makeSBP 'msgSsrGridDefinitionDepA ''MsgSsrGridDefinitionDepA)
+$(makeJSON "_msgSsrGridDefinitionDepA_" ''MsgSsrGridDefinitionDepA)
+$(makeLenses ''MsgSsrGridDefinitionDepA)
diff --git a/src/SwiftNav/SBP/System.hs b/src/SwiftNav/SBP/System.hs
--- a/src/SwiftNav/SBP/System.hs
+++ b/src/SwiftNav/SBP/System.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.System
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Standardized system messages from Swift Navigation devices.
+-- \< Standardized system messages from Swift Navigation devices. \>
 
 module SwiftNav.SBP.System
   ( module SwiftNav.SBP.System
@@ -40,9 +40,9 @@
 
 -- | SBP class for message MSG_STARTUP (0xFF00).
 --
--- The system start-up message is sent once on system start-up. It notifies the
--- host or other attached devices that the system has started and is now ready
--- to respond to commands or configuration requests.
+-- The system start-up message is sent once on system start-up. It notifies
+-- the host or other attached devices that the system has started and is now
+-- ready to respond to commands or configuration requests.
 data MsgStartup = MsgStartup
   { _msgStartup_cause      :: !Word8
     -- ^ Cause of startup
@@ -114,9 +114,11 @@
 -- attached devices that the system is running. It is used to monitor system
 -- malfunctions. It also contains status flags that indicate to the host the
 -- status of the system and whether it is operating correctly. Currently, the
--- expected heartbeat interval is 1 sec.  The system error flag is used to
--- indicate that an error has occurred in the system. To determine the source
--- of the error, the remaining error flags should be inspected.
+-- expected heartbeat interval is 1 sec.
+--
+-- The system error flag is used to indicate that an error has occurred in the
+-- system. To determine the source of the error, the remaining error flags
+-- should be inspected.
 data MsgHeartbeat = MsgHeartbeat
   { _msgHeartbeat_flags :: !Word32
     -- ^ Status flags
@@ -134,6 +136,80 @@
 $(makeJSON "_msgHeartbeat_" ''MsgHeartbeat)
 $(makeLenses ''MsgHeartbeat)
 
+-- | SubSystemReport.
+--
+-- Report the general and specific state of a sub-system.  If the generic
+-- state is reported as initializing, the specific state should be ignored.
+data SubSystemReport = SubSystemReport
+  { _subSystemReport_component :: !Word16
+    -- ^ Identity of reporting subsystem
+  , _subSystemReport_generic :: !Word8
+    -- ^ Generic form status report
+  , _subSystemReport_specific :: !Word8
+    -- ^ Subsystem specific status code
+  } deriving ( Show, Read, Eq )
+
+instance Binary SubSystemReport where
+  get = do
+    _subSystemReport_component <- getWord16le
+    _subSystemReport_generic <- getWord8
+    _subSystemReport_specific <- getWord8
+    pure SubSystemReport {..}
+
+  put SubSystemReport {..} = do
+    putWord16le _subSystemReport_component
+    putWord8 _subSystemReport_generic
+    putWord8 _subSystemReport_specific
+
+$(makeJSON "_subSystemReport_" ''SubSystemReport)
+$(makeLenses ''SubSystemReport)
+
+msgStatusReport :: Word16
+msgStatusReport = 0xFFFE
+
+-- | SBP class for message MSG_STATUS_REPORT (0xFFFE).
+--
+-- The status report is sent periodically to inform the host or other attached
+-- devices that the system is running. It is used to monitor system
+-- malfunctions. It contains status reports that indicate to the host the
+-- status of each sub-system and whether it is operating correctly.
+--
+-- Interpretation of the subsystem specific status code is product dependent,
+-- but if the generic status code is initializing, it should be ignored.
+-- Refer to product documentation for details.
+data MsgStatusReport = MsgStatusReport
+  { _msgStatusReport_reporting_system :: !Word16
+    -- ^ Identity of reporting system
+  , _msgStatusReport_sbp_version    :: !Word16
+    -- ^ SBP protocol version
+  , _msgStatusReport_sequence       :: !Word32
+    -- ^ Increments on each status report sent
+  , _msgStatusReport_uptime         :: !Word32
+    -- ^ Number of seconds since system start-up
+  , _msgStatusReport_status         :: ![SubSystemReport]
+    -- ^ Reported status of individual subsystems
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgStatusReport where
+  get = do
+    _msgStatusReport_reporting_system <- getWord16le
+    _msgStatusReport_sbp_version <- getWord16le
+    _msgStatusReport_sequence <- getWord32le
+    _msgStatusReport_uptime <- getWord32le
+    _msgStatusReport_status <- whileM (not <$> isEmpty) get
+    pure MsgStatusReport {..}
+
+  put MsgStatusReport {..} = do
+    putWord16le _msgStatusReport_reporting_system
+    putWord16le _msgStatusReport_sbp_version
+    putWord32le _msgStatusReport_sequence
+    putWord32le _msgStatusReport_uptime
+    mapM_ put _msgStatusReport_status
+
+$(makeSBP 'msgStatusReport ''MsgStatusReport)
+$(makeJSON "_msgStatusReport_" ''MsgStatusReport)
+$(makeLenses ''MsgStatusReport)
+
 msgInsStatus :: Word16
 msgInsStatus = 0xFF03
 
@@ -164,8 +240,8 @@
 -- | SBP class for message MSG_CSAC_TELEMETRY (0xFF04).
 --
 -- The CSAC telemetry message has an implementation defined telemetry string
--- from a device. It is not produced or available on general Swift Products. It
--- is intended to be a low rate message for status purposes.
+-- from a device. It is not produced or available on general Swift Products.
+-- It is intended to be a low rate message for status purposes.
 data MsgCsacTelemetry = MsgCsacTelemetry
   { _msgCsacTelemetry_id      :: !Word8
     -- ^ Index representing the type of telemetry in use.  It is implemention
@@ -194,8 +270,8 @@
 -- | SBP class for message MSG_CSAC_TELEMETRY_LABELS (0xFF05).
 --
 -- The CSAC telemetry message provides labels for each member of the string
--- produced by MSG_CSAC_TELEMETRY. It should be provided by a device at a lower
--- rate than the MSG_CSAC_TELEMETRY.
+-- produced by MSG_CSAC_TELEMETRY. It should be provided by a device at a
+-- lower rate than the MSG_CSAC_TELEMETRY.
 data MsgCsacTelemetryLabels = MsgCsacTelemetryLabels
   { _msgCsacTelemetryLabels_id             :: !Word8
     -- ^ Index representing the type of telemetry in use.  It is implemention
@@ -217,3 +293,164 @@
 $(makeSBP 'msgCsacTelemetryLabels ''MsgCsacTelemetryLabels)
 $(makeJSON "_msgCsacTelemetryLabels_" ''MsgCsacTelemetryLabels)
 $(makeLenses ''MsgCsacTelemetryLabels)
+
+msgInsUpdates :: Word16
+msgInsUpdates = 0xFF06
+
+-- | SBP class for message MSG_INS_UPDATES (0xFF06).
+--
+-- The INS update status message contains informations about executed and
+-- rejected INS updates. This message is expected to be extended in the future
+-- as new types of measurements are being added.
+data MsgInsUpdates = MsgInsUpdates
+  { _msgInsUpdates_tow      :: !Word32
+    -- ^ GPS Time of Week
+  , _msgInsUpdates_gnsspos  :: !Word8
+    -- ^ GNSS position update status flags
+  , _msgInsUpdates_gnssvel  :: !Word8
+    -- ^ GNSS velocity update status flags
+  , _msgInsUpdates_wheelticks :: !Word8
+    -- ^ Wheelticks update status flags
+  , _msgInsUpdates_speed    :: !Word8
+    -- ^ Wheelticks update status flags
+  , _msgInsUpdates_nhc      :: !Word8
+    -- ^ NHC update status flags
+  , _msgInsUpdates_zerovel  :: !Word8
+    -- ^ Zero velocity update status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgInsUpdates where
+  get = do
+    _msgInsUpdates_tow <- getWord32le
+    _msgInsUpdates_gnsspos <- getWord8
+    _msgInsUpdates_gnssvel <- getWord8
+    _msgInsUpdates_wheelticks <- getWord8
+    _msgInsUpdates_speed <- getWord8
+    _msgInsUpdates_nhc <- getWord8
+    _msgInsUpdates_zerovel <- getWord8
+    pure MsgInsUpdates {..}
+
+  put MsgInsUpdates {..} = do
+    putWord32le _msgInsUpdates_tow
+    putWord8 _msgInsUpdates_gnsspos
+    putWord8 _msgInsUpdates_gnssvel
+    putWord8 _msgInsUpdates_wheelticks
+    putWord8 _msgInsUpdates_speed
+    putWord8 _msgInsUpdates_nhc
+    putWord8 _msgInsUpdates_zerovel
+
+$(makeSBP 'msgInsUpdates ''MsgInsUpdates)
+$(makeJSON "_msgInsUpdates_" ''MsgInsUpdates)
+$(makeLenses ''MsgInsUpdates)
+
+msgGnssTimeOffset :: Word16
+msgGnssTimeOffset = 0xFF07
+
+-- | SBP class for message MSG_GNSS_TIME_OFFSET (0xFF07).
+--
+-- The GNSS time offset message contains the information that is needed to
+-- translate messages tagged with a local timestamp (e.g. IMU or wheeltick
+-- messages) to GNSS time for the sender producing this message.
+data MsgGnssTimeOffset = MsgGnssTimeOffset
+  { _msgGnssTimeOffset_weeks      :: !Int16
+    -- ^ Weeks portion of the time offset
+  , _msgGnssTimeOffset_milliseconds :: !Int32
+    -- ^ Milliseconds portion of the time offset
+  , _msgGnssTimeOffset_microseconds :: !Int16
+    -- ^ Microseconds portion of the time offset
+  , _msgGnssTimeOffset_flags      :: !Word8
+    -- ^ Status flags (reserved)
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgGnssTimeOffset where
+  get = do
+    _msgGnssTimeOffset_weeks <- (fromIntegral <$> getWord16le)
+    _msgGnssTimeOffset_milliseconds <- (fromIntegral <$> getWord32le)
+    _msgGnssTimeOffset_microseconds <- (fromIntegral <$> getWord16le)
+    _msgGnssTimeOffset_flags <- getWord8
+    pure MsgGnssTimeOffset {..}
+
+  put MsgGnssTimeOffset {..} = do
+    (putWord16le . fromIntegral) _msgGnssTimeOffset_weeks
+    (putWord32le . fromIntegral) _msgGnssTimeOffset_milliseconds
+    (putWord16le . fromIntegral) _msgGnssTimeOffset_microseconds
+    putWord8 _msgGnssTimeOffset_flags
+
+$(makeSBP 'msgGnssTimeOffset ''MsgGnssTimeOffset)
+$(makeJSON "_msgGnssTimeOffset_" ''MsgGnssTimeOffset)
+$(makeLenses ''MsgGnssTimeOffset)
+
+msgPpsTime :: Word16
+msgPpsTime = 0xFF08
+
+-- | SBP class for message MSG_PPS_TIME (0xFF08).
+--
+-- The PPS time message contains the value of the sender's local time in
+-- microseconds at the moment a pulse is detected on the PPS input. This is to
+-- be used for syncronisation of sensor data sampled with a local timestamp
+-- (e.g. IMU or wheeltick messages) where GNSS time is unknown to the sender.
+--
+-- The local time used to timestamp the PPS pulse must be generated by the
+-- same clock which is used to timestamp the IMU/wheel sensor data and should
+-- follow the same roll-over rules.  A separate MSG_PPS_TIME message should be
+-- sent for each source of sensor data which uses PPS-relative timestamping.
+-- The sender ID for each of these MSG_PPS_TIME messages should match the
+-- sender ID of the respective sensor data.
+data MsgPpsTime = MsgPpsTime
+  { _msgPpsTime_time :: !Word64
+    -- ^ Local time in microseconds
+  , _msgPpsTime_flags :: !Word8
+    -- ^ Status flags
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgPpsTime where
+  get = do
+    _msgPpsTime_time <- getWord64le
+    _msgPpsTime_flags <- getWord8
+    pure MsgPpsTime {..}
+
+  put MsgPpsTime {..} = do
+    putWord64le _msgPpsTime_time
+    putWord8 _msgPpsTime_flags
+
+$(makeSBP 'msgPpsTime ''MsgPpsTime)
+$(makeJSON "_msgPpsTime_" ''MsgPpsTime)
+$(makeLenses ''MsgPpsTime)
+
+msgGroupMeta :: Word16
+msgGroupMeta = 0xFF0A
+
+-- | SBP class for message MSG_GROUP_META (0xFF0A).
+--
+-- This leading message lists the time metadata of the Solution Group. It also
+-- lists the atomic contents (i.e. types of messages included) of the Solution
+-- Group.
+data MsgGroupMeta = MsgGroupMeta
+  { _msgGroupMeta_group_id   :: !Word8
+    -- ^ Id of the Msgs Group, 0 is Unknown, 1 is Bestpos, 2 is Gnss
+  , _msgGroupMeta_flags      :: !Word8
+    -- ^ Status flags (reserved)
+  , _msgGroupMeta_n_group_msgs :: !Word8
+    -- ^ Size of list group_msgs
+  , _msgGroupMeta_group_msgs :: ![Word16]
+    -- ^ An inorder list of message types included in the Solution Group,
+    -- including GROUP_META itself
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgGroupMeta where
+  get = do
+    _msgGroupMeta_group_id <- getWord8
+    _msgGroupMeta_flags <- getWord8
+    _msgGroupMeta_n_group_msgs <- getWord8
+    _msgGroupMeta_group_msgs <- whileM (not <$> isEmpty) getWord16le
+    pure MsgGroupMeta {..}
+
+  put MsgGroupMeta {..} = do
+    putWord8 _msgGroupMeta_group_id
+    putWord8 _msgGroupMeta_flags
+    putWord8 _msgGroupMeta_n_group_msgs
+    mapM_ putWord16le _msgGroupMeta_group_msgs
+
+$(makeSBP 'msgGroupMeta ''MsgGroupMeta)
+$(makeJSON "_msgGroupMeta_" ''MsgGroupMeta)
+$(makeLenses ''MsgGroupMeta)
diff --git a/src/SwiftNav/SBP/TH.hs b/src/SwiftNav/SBP/TH.hs
--- a/src/SwiftNav/SBP/TH.hs
+++ b/src/SwiftNav/SBP/TH.hs
@@ -5,7 +5,7 @@
 -- Module:      SwiftNav.SBP.TH
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
 -- License:     LGPL-3
--- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
diff --git a/src/SwiftNav/SBP/Tracking.hs b/src/SwiftNav/SBP/Tracking.hs
--- a/src/SwiftNav/SBP/Tracking.hs
+++ b/src/SwiftNav/SBP/Tracking.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Tracking
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Satellite code and carrier-phase tracking messages from the device.
+-- \< Satellite code and carrier-phase tracking messages from the device. \>
 
 module SwiftNav.SBP.Tracking
   ( module SwiftNav.SBP.Tracking
@@ -70,8 +70,8 @@
   , _msgTrackingStateDetailedDepA_doppler_std :: !Word16
     -- ^ Carrier Doppler frequency standard deviation.
   , _msgTrackingStateDetailedDepA_uptime     :: !Word32
-    -- ^ Number of seconds of continuous tracking. Specifies how much time signal
-    -- is in continuous track.
+    -- ^ Number of seconds of continuous tracking. Specifies how much time
+    -- signal is in continuous track.
   , _msgTrackingStateDetailedDepA_clock_offset :: !Int16
     -- ^ TCXO clock offset. Valid only when valid clock valid flag is set.
   , _msgTrackingStateDetailedDepA_clock_drift :: !Int16
@@ -104,13 +104,13 @@
     _msgTrackingStateDetailedDepA_cn0 <- getWord8
     _msgTrackingStateDetailedDepA_lock <- getWord16le
     _msgTrackingStateDetailedDepA_sid <- get
-    _msgTrackingStateDetailedDepA_doppler <- fromIntegral <$> getWord32le
+    _msgTrackingStateDetailedDepA_doppler <- (fromIntegral <$> getWord32le)
     _msgTrackingStateDetailedDepA_doppler_std <- getWord16le
     _msgTrackingStateDetailedDepA_uptime <- getWord32le
-    _msgTrackingStateDetailedDepA_clock_offset <- fromIntegral <$> getWord16le
-    _msgTrackingStateDetailedDepA_clock_drift <- fromIntegral <$> getWord16le
+    _msgTrackingStateDetailedDepA_clock_offset <- (fromIntegral <$> getWord16le)
+    _msgTrackingStateDetailedDepA_clock_drift <- (fromIntegral <$> getWord16le)
     _msgTrackingStateDetailedDepA_corr_spacing <- getWord16le
-    _msgTrackingStateDetailedDepA_acceleration <- fromIntegral <$> getWord8
+    _msgTrackingStateDetailedDepA_acceleration <- (fromIntegral <$> getWord8)
     _msgTrackingStateDetailedDepA_sync_flags <- getWord8
     _msgTrackingStateDetailedDepA_tow_flags <- getWord8
     _msgTrackingStateDetailedDepA_track_flags <- getWord8
@@ -179,8 +179,8 @@
   , _msgTrackingStateDetailedDep_doppler_std :: !Word16
     -- ^ Carrier Doppler frequency standard deviation.
   , _msgTrackingStateDetailedDep_uptime     :: !Word32
-    -- ^ Number of seconds of continuous tracking. Specifies how much time signal
-    -- is in continuous track.
+    -- ^ Number of seconds of continuous tracking. Specifies how much time
+    -- signal is in continuous track.
   , _msgTrackingStateDetailedDep_clock_offset :: !Int16
     -- ^ TCXO clock offset. Valid only when valid clock valid flag is set.
   , _msgTrackingStateDetailedDep_clock_drift :: !Int16
@@ -213,13 +213,13 @@
     _msgTrackingStateDetailedDep_cn0 <- getWord8
     _msgTrackingStateDetailedDep_lock <- getWord16le
     _msgTrackingStateDetailedDep_sid <- get
-    _msgTrackingStateDetailedDep_doppler <- fromIntegral <$> getWord32le
+    _msgTrackingStateDetailedDep_doppler <- (fromIntegral <$> getWord32le)
     _msgTrackingStateDetailedDep_doppler_std <- getWord16le
     _msgTrackingStateDetailedDep_uptime <- getWord32le
-    _msgTrackingStateDetailedDep_clock_offset <- fromIntegral <$> getWord16le
-    _msgTrackingStateDetailedDep_clock_drift <- fromIntegral <$> getWord16le
+    _msgTrackingStateDetailedDep_clock_offset <- (fromIntegral <$> getWord16le)
+    _msgTrackingStateDetailedDep_clock_drift <- (fromIntegral <$> getWord16le)
     _msgTrackingStateDetailedDep_corr_spacing <- getWord16le
-    _msgTrackingStateDetailedDep_acceleration <- fromIntegral <$> getWord8
+    _msgTrackingStateDetailedDep_acceleration <- (fromIntegral <$> getWord8)
     _msgTrackingStateDetailedDep_sync_flags <- getWord8
     _msgTrackingStateDetailedDep_tow_flags <- getWord8
     _msgTrackingStateDetailedDep_track_flags <- getWord8
@@ -312,11 +312,11 @@
 --
 -- Measurement Engine tracking channel state for a specific satellite signal
 -- and measured signal power. The mesid field for Glonass can either carry the
--- FCN as 100 + FCN where FCN is in [-7, +6] or the Slot ID (from 1 to 28)
+-- FCN as 100 + FCN where FCN is in [-7, +6] or the Slot ID (from 1 to 28).
 data MeasurementState = MeasurementState
   { _measurementState_mesid :: !GnssSignal
-    -- ^ Measurement Engine GNSS signal being tracked (carries either Glonass FCN
-    -- or SLOT)
+    -- ^ Measurement Engine GNSS signal being tracked (carries either Glonass
+    -- FCN or SLOT)
   , _measurementState_cn0 :: !Word8
     -- ^ Carrier-to-Noise density.  Zero implies invalid cn0.
   } deriving ( Show, Read, Eq )
@@ -371,8 +371,8 @@
 
 instance Binary TrackingChannelCorrelation where
   get = do
-    _trackingChannelCorrelation_I <- fromIntegral <$> getWord16le
-    _trackingChannelCorrelation_Q <- fromIntegral <$> getWord16le
+    _trackingChannelCorrelation_I <- (fromIntegral <$> getWord16le)
+    _trackingChannelCorrelation_Q <- (fromIntegral <$> getWord16le)
     pure TrackingChannelCorrelation {..}
 
   put TrackingChannelCorrelation {..} = do
@@ -426,8 +426,8 @@
 
 instance Binary TrackingChannelCorrelationDep where
   get = do
-    _trackingChannelCorrelationDep_I <- fromIntegral <$> getWord32le
-    _trackingChannelCorrelationDep_Q <- fromIntegral <$> getWord32le
+    _trackingChannelCorrelationDep_I <- (fromIntegral <$> getWord32le)
+    _trackingChannelCorrelationDep_Q <- (fromIntegral <$> getWord32le)
     pure TrackingChannelCorrelationDep {..}
 
   put TrackingChannelCorrelationDep {..} = do
diff --git a/src/SwiftNav/SBP/Types.hs b/src/SwiftNav/SBP/Types.hs
--- a/src/SwiftNav/SBP/Types.hs
+++ b/src/SwiftNav/SBP/Types.hs
@@ -7,7 +7,7 @@
 -- Module:      SwiftNav.SBP.Types
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
 -- License:     LGPL-3
--- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
diff --git a/src/SwiftNav/SBP/User.hs b/src/SwiftNav/SBP/User.hs
--- a/src/SwiftNav/SBP/User.hs
+++ b/src/SwiftNav/SBP/User.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.User
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages reserved for use by the user.
+-- \< Messages reserved for use by the user. \>
 
 module SwiftNav.SBP.User
   ( module SwiftNav.SBP.User
diff --git a/src/SwiftNav/SBP/Vehicle.hs b/src/SwiftNav/SBP/Vehicle.hs
--- a/src/SwiftNav/SBP/Vehicle.hs
+++ b/src/SwiftNav/SBP/Vehicle.hs
@@ -6,12 +6,12 @@
 -- |
 -- Module:      SwiftNav.SBP.Vehicle
 -- Copyright:   Copyright (C) 2015-2018 Swift Navigation, Inc.
--- License:     LGPL-3
--- Maintainer:  Swift Navigation <dev@swiftnav.com>
+-- License:     MIT
+-- Contact:     https://support.swiftnav.com
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages from a vehicle.
+-- \< Messages from a vehicle. \>
 
 module SwiftNav.SBP.Vehicle
   ( module SwiftNav.SBP.Vehicle
@@ -40,16 +40,19 @@
 -- | SBP class for message MSG_ODOMETRY (0x0903).
 --
 -- Message representing the x component of vehicle velocity in the user frame
--- at the odometry reference point(s) specified by the user. The offset for the
--- odometry reference point and  the definition and origin of the user frame
--- are defined through the device settings interface. There are 4 possible
--- user-defined sources of this message  which are labeled arbitrarily  source
--- 0 through 3.
+-- at the odometry reference point(s) specified by the user. The offset for
+-- the odometry reference point and the definition and origin of the user
+-- frame are defined through the device settings interface. There are 4
+-- possible user-defined sources of this message which are labeled arbitrarily
+-- source 0 through 3. If using "processor time" time tags, the receiving end
+-- will expect a `MSG_GNSS_TIME_OFFSET` when a PVT fix becomes available to
+-- synchronise odometry measurements with GNSS. Processor time shall roll over
+-- to zero after one week.
 data MsgOdometry = MsgOdometry
   { _msgOdometry_tow    :: !Word32
-    -- ^ Time field representing either milliseconds in the GPS Week or local CPU
-    -- time from the producing system in milliseconds.  See the tow_source flag
-    -- for the exact source of this timestamp.
+    -- ^ Time field representing either milliseconds in the GPS Week or local
+    -- CPU time from the producing system in milliseconds.  See the tow_source
+    -- flag for the exact source of this timestamp.
   , _msgOdometry_velocity :: !Int32
     -- ^ The signed forward component of vehicle velocity.
   , _msgOdometry_flags  :: !Word8
@@ -59,7 +62,7 @@
 instance Binary MsgOdometry where
   get = do
     _msgOdometry_tow <- getWord32le
-    _msgOdometry_velocity <- fromIntegral <$> getWord32le
+    _msgOdometry_velocity <- (fromIntegral <$> getWord32le)
     _msgOdometry_flags <- getWord8
     pure MsgOdometry {..}
 
@@ -71,3 +74,53 @@
 $(makeSBP 'msgOdometry ''MsgOdometry)
 $(makeJSON "_msgOdometry_" ''MsgOdometry)
 $(makeLenses ''MsgOdometry)
+
+msgWheeltick :: Word16
+msgWheeltick = 0x0904
+
+-- | SBP class for message MSG_WHEELTICK (0x0904).
+--
+-- Message containing the accumulated distance travelled by a wheel located at
+-- an odometry reference point defined by the user. The offset for the
+-- odometry reference point and the definition and origin of the user frame
+-- are defined through the device settings interface. The source of this
+-- message is identified by the source field, which is an integer ranging from
+-- 0 to 255. The timestamp associated with this message should represent the
+-- time when the accumulated tick count reached the value given by the
+-- contents of this message as accurately as possible. If using "local CPU
+-- time" time tags, the receiving end will expect a `MSG_GNSS_TIME_OFFSET`
+-- when a PVT fix becomes available to synchronise wheeltick measurements with
+-- GNSS. Local CPU time shall roll over to zero after one week.
+data MsgWheeltick = MsgWheeltick
+  { _msgWheeltick_time :: !Word64
+    -- ^ Time field representing either microseconds since the last PPS,
+    -- microseconds in the GPS Week or local CPU time from the producing
+    -- system in microseconds. See the synch_type field for the exact meaning
+    -- of this timestamp.
+  , _msgWheeltick_flags :: !Word8
+    -- ^ Field indicating the type of timestamp contained in the time field.
+  , _msgWheeltick_source :: !Word8
+    -- ^ ID of the sensor producing this message
+  , _msgWheeltick_ticks :: !Int32
+    -- ^ Free-running counter of the accumulated distance for this sensor. The
+    -- counter should be incrementing if travelling into one direction and
+    -- decrementing when travelling in the opposite direction.
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgWheeltick where
+  get = do
+    _msgWheeltick_time <- getWord64le
+    _msgWheeltick_flags <- getWord8
+    _msgWheeltick_source <- getWord8
+    _msgWheeltick_ticks <- (fromIntegral <$> getWord32le)
+    pure MsgWheeltick {..}
+
+  put MsgWheeltick {..} = do
+    putWord64le _msgWheeltick_time
+    putWord8 _msgWheeltick_flags
+    putWord8 _msgWheeltick_source
+    (putWord32le . fromIntegral) _msgWheeltick_ticks
+
+$(makeSBP 'msgWheeltick ''MsgWheeltick)
+$(makeJSON "_msgWheeltick_" ''MsgWheeltick)
+$(makeLenses ''MsgWheeltick)
