diff --git a/main/JSON2SBP.hs b/main/JSON2SBP.hs
--- a/main/JSON2SBP.hs
+++ b/main/JSON2SBP.hs
@@ -30,7 +30,7 @@
 
 -- | Decode a SBPMsg from JSON.
 decodeSBPMsg :: ByteString -> Maybe SBPMsg
-decodeSBPMsg v = decodeStrict v <|> (fmap sbpMsgData $ decodeStrict v)
+decodeSBPMsg v = decodeStrict v <|> sbpMsgData <$> decodeStrict v
 
 main :: IO ()
 main = runResourceT $
diff --git a/main/SBP2YAML.hs b/main/SBP2YAML.hs
new file mode 100644
--- /dev/null
+++ b/main/SBP2YAML.hs
@@ -0,0 +1,27 @@
+-- |
+-- Module:      SBP2YAML
+-- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
+-- License:     LGPL-3
+-- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- SBP to YAML tool - reads SBP binary from stdin and sends SBP YAML
+-- to stdout.
+
+import           BasicPrelude
+import           Control.Monad.Trans.Resource
+import           Data.Conduit
+import           Data.Conduit.Binary
+import qualified Data.Conduit.List as CL
+import           Data.Conduit.Serialization.Binary
+import           Data.Yaml
+import           SwiftNav.SBP
+import           System.IO
+
+main :: IO ()
+main = runResourceT $
+  sourceHandle stdin =$=
+  conduitDecode =$=
+  CL.map (encode :: SBPMsg -> ByteString) $$
+  sinkHandle stdout
diff --git a/sbp.cabal b/sbp.cabal
--- a/sbp.cabal
+++ b/sbp.cabal
@@ -1,5 +1,5 @@
 name:                  sbp
-version:               0.51.2
+version:               0.51.5
 synopsis:              SwiftNav's SBP Library
 homepage:              https://github.com/swift-nav/libsbp
 license:               LGPL-3
@@ -30,6 +30,7 @@
                      , SwiftNav.SBP.ExtEvents
                      , SwiftNav.SBP.FileIo
                      , SwiftNav.SBP.Flash
+                     , SwiftNav.SBP.GnssSignal
                      , SwiftNav.SBP.Logging
                      , SwiftNav.SBP.Navigation
                      , SwiftNav.SBP.Observation
@@ -37,6 +38,7 @@
                      , SwiftNav.SBP.Settings
                      , SwiftNav.SBP.System
                      , SwiftNav.SBP.Tracking
+                     , SwiftNav.SBP.User
                      , SwiftNav.SBP.Encoding
                      , SwiftNav.SBP.Types
   other-modules:       SwiftNav.SBP.TH
@@ -96,6 +98,23 @@
   default-language:    Haskell2010
   default-extensions:  NoImplicitPrelude
                        OverloadedStrings
+executable sbp2yaml
+  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
+  default-extensions:  NoImplicitPrelude
+                       OverloadedStrings
+
 benchmark bench
   type:                exitcode-stdio-1.0
   hs-source-dirs:      bench
diff --git a/src/SwiftNav/SBP.hs b/src/SwiftNav/SBP.hs
--- a/src/SwiftNav/SBP.hs
+++ b/src/SwiftNav/SBP.hs
@@ -16,6 +16,7 @@
   , module SwiftNav.SBP.ExtEvents
   , module SwiftNav.SBP.FileIo
   , module SwiftNav.SBP.Flash
+  , module SwiftNav.SBP.GnssSignal
   , module SwiftNav.SBP.Logging
   , module SwiftNav.SBP.Navigation
   , module SwiftNav.SBP.Observation
@@ -23,6 +24,7 @@
   , module SwiftNav.SBP.Settings
   , module SwiftNav.SBP.System
   , module SwiftNav.SBP.Tracking
+  , module SwiftNav.SBP.User
   ) where
 
 import BasicPrelude hiding (lookup)
@@ -40,6 +42,7 @@
 import SwiftNav.SBP.ExtEvents
 import SwiftNav.SBP.FileIo
 import SwiftNav.SBP.Flash
+import SwiftNav.SBP.GnssSignal
 import SwiftNav.SBP.Logging
 import SwiftNav.SBP.Navigation
 import SwiftNav.SBP.Observation
@@ -47,6 +50,7 @@
 import SwiftNav.SBP.Settings
 import SwiftNav.SBP.System
 import SwiftNav.SBP.Tracking
+import SwiftNav.SBP.User
 import SwiftNav.SBP.Types
 
 
@@ -120,6 +124,7 @@
    | SBPMsgTrackingStateDepA MsgTrackingStateDepA Msg
    | SBPMsgTweet MsgTweet Msg
    | SBPMsgUartState MsgUartState Msg
+   | SBPMsgUserData MsgUserData Msg
    | SBPMsgVelEcef MsgVelEcef Msg
    | SBPMsgVelNed MsgVelNed Msg
    | SBPMsgBadCrc Msg
@@ -199,6 +204,7 @@
           | _msgSBPType == msgTrackingStateDepA = SBPMsgTrackingStateDepA (decode (fromStrict _msgSBPPayload)) sbp
           | _msgSBPType == msgTweet = SBPMsgTweet (decode (fromStrict _msgSBPPayload)) sbp
           | _msgSBPType == msgUartState = SBPMsgUartState (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgUserData = SBPMsgUserData (decode (fromStrict _msgSBPPayload)) sbp
           | _msgSBPType == msgVelEcef = SBPMsgVelEcef (decode (fromStrict _msgSBPPayload)) sbp
           | _msgSBPType == msgVelNed = SBPMsgVelNed (decode (fromStrict _msgSBPPayload)) sbp
           | otherwise = SBPMsgUnknown sbp
@@ -272,6 +278,7 @@
       encode' (SBPMsgTrackingStateDepA _ sbp) = sbp
       encode' (SBPMsgTweet _ sbp) = sbp
       encode' (SBPMsgUartState _ sbp) = sbp
+      encode' (SBPMsgUserData _ sbp) = sbp
       encode' (SBPMsgVelEcef _ sbp) = sbp
       encode' (SBPMsgVelNed _ sbp) = sbp
       encode' (SBPMsgUnknown sbp) = sbp
@@ -347,6 +354,7 @@
         | msgType == msgTrackingStateDepA = SBPMsgTrackingStateDepA <$> parseJSON obj <*> parseJSON obj
         | msgType == msgTweet = SBPMsgTweet <$> parseJSON obj <*> parseJSON obj
         | msgType == msgUartState = SBPMsgUartState <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgUserData = SBPMsgUserData <$> parseJSON obj <*> parseJSON obj
         | msgType == msgVelEcef = SBPMsgVelEcef <$> parseJSON obj <*> parseJSON obj
         | msgType == msgVelNed = SBPMsgVelNed <$> parseJSON obj <*> parseJSON obj
         | otherwise = SBPMsgUnknown <$> parseJSON obj
@@ -424,6 +432,7 @@
    toJSON (SBPMsgTrackingStateDepA msg sbp) = toJSON msg `merge` toJSON sbp
    toJSON (SBPMsgTweet msg sbp) = toJSON msg `merge` toJSON sbp
    toJSON (SBPMsgUartState msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgUserData msg sbp) = toJSON msg `merge` toJSON sbp
    toJSON (SBPMsgVelEcef msg sbp) = toJSON msg `merge` toJSON sbp
    toJSON (SBPMsgVelNed msg sbp) = toJSON msg `merge` toJSON sbp
    toJSON (SBPMsgBadCrc sbp) = toJSON sbp
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
@@ -25,6 +25,7 @@
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
+import SwiftNav.SBP.GnssSignal
 
 msgAcqResult :: Word16
 msgAcqResult = 0x0014
@@ -37,16 +38,14 @@
 -- the best signal-to-noise (SNR) ratio.
 data MsgAcqResult = MsgAcqResult
   { _msgAcqResult_snr :: Float
-    -- ^ SNR of best point. Currently dimensonless, but will have units of dB Hz
-    -- in the revision of this message.
+    -- ^ SNR of best point. Currently in arbitrary SNR points, but will be in
+    -- units of dB Hz in a later revision of this message.
   , _msgAcqResult_cp :: Float
     -- ^ Code phase of best point
   , _msgAcqResult_cf :: Float
     -- ^ Carrier frequency of best point
-  , _msgAcqResult_sid :: Word32
-    -- ^ Signal identifier of the satellite signal for which acquisition was
-    -- attempted - values 0x00 through 0x1F represent GPS PRNs 1 through 32
-    -- respectively (PRN-1 notation); other values reserved for future use.
+  , _msgAcqResult_sid :: SBPGnssSignal
+    -- ^ GNSS signal for which acquisition was attempted
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgAcqResult where
@@ -54,14 +53,14 @@
     _msgAcqResult_snr <- getFloat32le
     _msgAcqResult_cp <- getFloat32le
     _msgAcqResult_cf <- getFloat32le
-    _msgAcqResult_sid <- getWord32le
+    _msgAcqResult_sid <- get
     return MsgAcqResult {..}
 
   put MsgAcqResult {..} = do
     putFloat32le _msgAcqResult_snr
     putFloat32le _msgAcqResult_cp
     putFloat32le _msgAcqResult_cf
-    putWord32le _msgAcqResult_sid
+    put _msgAcqResult_sid
 
 $(deriveSBP 'msgAcqResult ''MsgAcqResult)
 
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,10 +6,9 @@
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages for the bootloading configuration on the device.  These are in the
--- implementation-defined range (0x0000-0x00FF), and are intended for internal
--- use only. 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 on the device.  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 where
 
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
@@ -10,10 +10,8 @@
 -- 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.  These are in the implementation-defined range
--- (0x0000-0x00FF), and intended for internal-use only. 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 where
 
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
@@ -9,8 +9,6 @@
 -- 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.
--- These are in the implementation-defined range (0x0000-0x00FF), and are
--- intended for internal-use only.
 
 module SwiftNav.SBP.Flash where
 
diff --git a/src/SwiftNav/SBP/GnssSignal.hs b/src/SwiftNav/SBP/GnssSignal.hs
new file mode 100644
--- /dev/null
+++ b/src/SwiftNav/SBP/GnssSignal.hs
@@ -0,0 +1,54 @@
+-- |
+-- Module:      SwiftNav.SBP.GnssSignal
+-- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
+-- License:     LGPL-3
+-- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- Struct to represent a signal (constellation, band, satellite identifier)
+
+module SwiftNav.SBP.GnssSignal where
+
+import BasicPrelude
+import Control.Lens
+import Control.Monad.Loops
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.IEEE754
+import Data.Binary.Put
+import Data.ByteString
+import Data.ByteString.Lazy hiding ( ByteString )
+import Data.Int
+import Data.Word
+import SwiftNav.SBP.Encoding
+import SwiftNav.SBP.TH
+import SwiftNav.SBP.Types
+
+-- | SBPGnssSignal.
+--
+-- Signal identifier containing constellation, band, and satellite identifier
+data SBPGnssSignal = SBPGnssSignal
+  { _sBPGnssSignal_sat         :: Word16
+    -- ^ Constellation-specific satellite identifier
+  , _sBPGnssSignal_band        :: Word8
+    -- ^ Signal band
+  , _sBPGnssSignal_constellation :: Word8
+    -- ^ Constellation to which the satellite belongs
+  } deriving ( Show, Read, Eq )
+
+instance Binary SBPGnssSignal where
+  get = do
+    _sBPGnssSignal_sat <- getWord16le
+    _sBPGnssSignal_band <- getWord8
+    _sBPGnssSignal_constellation <- getWord8
+    return SBPGnssSignal {..}
+
+  put SBPGnssSignal {..} = do
+    putWord16le _sBPGnssSignal_sat
+    putWord8 _sBPGnssSignal_band
+    putWord8 _sBPGnssSignal_constellation
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_sBPGnssSignal_" . stripPrefix "_sBPGnssSignal_"}
+             ''SBPGnssSignal)
+$(makeLenses ''SBPGnssSignal)
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,8 +6,7 @@
 -- Stability:   experimental
 -- Portability: portable
 --
--- Logging and debugging messages from the device. These are in the
--- implementation-defined range (0x0000-0x00FF).
+-- Logging and debugging messages from the device.
 
 module SwiftNav.SBP.Logging where
 
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
@@ -300,9 +300,9 @@
 -- 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 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).
+-- 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
@@ -407,8 +407,9 @@
 -- | SBP class for message MSG_VEL_NED (0x0205).
 --
 -- This message reports the velocity in local North East Down (NED)
--- coordinates. The full GPS time is given by the preceding MSG_GPS_TIME with
--- the matching time-of-week (tow).
+-- 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
@@ -462,8 +463,8 @@
 -- | SBP class for message MSG_BASELINE_HEADING (0x0207).
 --
 -- This message reports the baseline heading pointing from the base station to
--- the rover relative to North. The full GPS time is given by the preceding
--- MSG_GPS_TIME with the matching time-of-week (tow).
+-- 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 MsgBaselineHeading = MsgBaselineHeading
   { _msgBaselineHeading_tow   :: Word32
     -- ^ GPS Time of Week
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
@@ -25,6 +25,7 @@
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
+import SwiftNav.SBP.GnssSignal
 
 -- | ObsGPSTime.
 --
@@ -113,10 +114,8 @@
     -- ^ Lock indicator. This value changes whenever a satellite signal has lost
     -- and regained lock, indicating that the carrier phase ambiguity may have
     -- changed.
-  , _packedObsContent_sid :: Word32
-    -- ^ Signal identifier of the satellite signal - values 0x00 through 0x1F
-    -- represent GPS PRNs 1 through 32 respectively (PRN-1 notation); other
-    -- values reserved for future use.
+  , _packedObsContent_sid :: SBPGnssSignal
+    -- ^ GNSS signal identifier
   } deriving ( Show, Read, Eq )
 
 instance Binary PackedObsContent where
@@ -125,7 +124,7 @@
     _packedObsContent_L <- get
     _packedObsContent_cn0 <- getWord8
     _packedObsContent_lock <- getWord16le
-    _packedObsContent_sid <- getWord32le
+    _packedObsContent_sid <- get
     return PackedObsContent {..}
 
   put PackedObsContent {..} = do
@@ -133,7 +132,7 @@
     put _packedObsContent_L
     putWord8 _packedObsContent_cn0
     putWord16le _packedObsContent_lock
-    putWord32le _packedObsContent_sid
+    put _packedObsContent_sid
 $(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_packedObsContent_" . stripPrefix "_packedObsContent_"}
              ''PackedObsContent)
 $(makeLenses ''PackedObsContent)
@@ -270,10 +269,8 @@
     -- ^ Is valid?
   , _msgEphemeris_healthy :: Word8
     -- ^ Satellite is healthy?
-  , _msgEphemeris_sid    :: Word32
-    -- ^ Signal identifier being tracked - values 0x00 through 0x1F represent GPS
-    -- PRNs 1 through 32 respectively (PRN-1 notation); other values reserved
-    -- for future use
+  , _msgEphemeris_sid    :: SBPGnssSignal
+    -- ^ GNSS signal identifier
   , _msgEphemeris_iode   :: Word8
     -- ^ Issue of ephemeris data
   , _msgEphemeris_iodc   :: Word16
@@ -309,7 +306,7 @@
     _msgEphemeris_toc_wn <- getWord16le
     _msgEphemeris_valid <- getWord8
     _msgEphemeris_healthy <- getWord8
-    _msgEphemeris_sid <- getWord32le
+    _msgEphemeris_sid <- get
     _msgEphemeris_iode <- getWord8
     _msgEphemeris_iodc <- getWord16le
     _msgEphemeris_reserved <- getWord32le
@@ -341,7 +338,7 @@
     putWord16le _msgEphemeris_toc_wn
     putWord8 _msgEphemeris_valid
     putWord8 _msgEphemeris_healthy
-    putWord32le _msgEphemeris_sid
+    put _msgEphemeris_sid
     putWord8 _msgEphemeris_iode
     putWord16le _msgEphemeris_iodc
     putWord32le _msgEphemeris_reserved
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
@@ -8,8 +8,7 @@
 --
 -- System health, configuration, and diagnostic messages specific to the Piksi
 -- L1 receiver, including a variety of legacy messages that may no longer be
--- used.  These messages are in the implementation-defined range
--- (0x0000-0x00FF), and largely intended for internal-use only.
+-- used.
 
 module SwiftNav.SBP.Piksi where
 
@@ -28,6 +27,7 @@
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
+import SwiftNav.SBP.GnssSignal
 
 msgAlmanac :: Word16
 msgAlmanac = 0x0069
@@ -126,7 +126,7 @@
 
 -- | SBP class for message MSG_CW_START (0x00C1).
 --
--- This is an unused legacy message from those host for starting the CW
+-- This is an unused legacy message from the host for starting the CW
 -- interference channel on the SwiftNAP. This message will be removed in a
 -- future release.
 data MsgCwStart = MsgCwStart
@@ -203,7 +203,7 @@
 --
 -- The thread usage message from the device reports real-time operating system
 -- (RTOS) thread usage statistics for the named thread. The reported percentage
--- values require to be normalized.
+-- values must be normalized.
 data MsgThreadState = MsgThreadState
   { _msgThreadState_name     :: ByteString
     -- ^ Thread name (NULL terminated)
@@ -235,7 +235,7 @@
 -- | UARTChannel.
 --
 -- Throughput, utilization, and error counts on the RX/TX buffers of this UART
--- channel. The reported percentage values require to be normalized.
+-- channel. The reported percentage values must be normalized.
 data UARTChannel = UARTChannel
   { _uARTChannel_tx_throughput :: Float
     -- ^ UART transmit throughput
@@ -246,7 +246,7 @@
   , _uARTChannel_io_error_count :: Word16
     -- ^ UART IO error count
   , _uARTChannel_tx_buffer_level :: Word8
-    -- ^ UART transmit buffer percentage utilization (ranges from 0 - 255)
+    -- ^ UART transmit buffer percentage utilization (ranges from 0 to 255)
   , _uARTChannel_rx_buffer_level :: Word8
     -- ^ UART receive buffer percentage utilization (ranges from 0 to 255)
   } deriving ( Show, Read, Eq )
@@ -314,8 +314,8 @@
 -- 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 require
--- to be normalized.
+-- hosts, or other interfaces in future. The reported percentage values must be
+-- normalized.
 data MsgUartState = MsgUartState
   { _msgUartState_uart_a  :: UARTChannel
     -- ^ State of UART A
@@ -384,19 +384,19 @@
 data MsgMaskSatellite = MsgMaskSatellite
   { _msgMaskSatellite_mask :: Word8
     -- ^ Mask of systems that should ignore this satellite.
-  , _msgMaskSatellite_sid :: Word32
-    -- ^ Signal identifier for which the mask is applied
+  , _msgMaskSatellite_sid :: SBPGnssSignal
+    -- ^ GNSS signal for which the mask is applied
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgMaskSatellite where
   get = do
     _msgMaskSatellite_mask <- getWord8
-    _msgMaskSatellite_sid <- getWord32le
+    _msgMaskSatellite_sid <- get
     return MsgMaskSatellite {..}
 
   put MsgMaskSatellite {..} = do
     putWord8 _msgMaskSatellite_mask
-    putWord32le _msgMaskSatellite_sid
+    put _msgMaskSatellite_sid
 
 $(deriveSBP 'msgMaskSatellite ''MsgMaskSatellite)
 
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,12 +6,11 @@
 -- Stability:   experimental
 -- Portability: portable
 --
--- Messages for reading and writing the device's device settings.  These are in
--- the implementation-defined range (0x0000-0x00FF). Note that some of these
--- messages share the same message type ID for both the host request and the
--- device response. See the accompanying document for descriptions of settings
--- configurations and examples: https://github.com/swift-
--- nav/piksi\_firmware/blob/master/docs/settings.pdf
+-- Messages for reading and writing the device's device settings.  Note that
+-- some of these messages share the same message type ID for both the host
+-- request and the device response. See the accompanying document for
+-- descriptions of settings configurations and examples:  https://github.com
+-- /swift-nav/piksi\_firmware/blob/master/docs/settings.pdf
 
 module SwiftNav.SBP.Settings where
 
@@ -86,7 +85,7 @@
 
 -- | SBP class for message MSG_SETTINGS_READ_REQ (0x00A4).
 --
--- The setting message reads the devices configuration.
+-- The setting message reads the device configuration.
 data MsgSettingsReadReq = MsgSettingsReadReq
   { _msgSettingsReadReq_setting :: ByteString
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
@@ -113,7 +112,7 @@
 
 -- | SBP class for message MSG_SETTINGS_READ_RESP (0x00A5).
 --
--- The setting message reads the devices configuration.
+-- The setting message reads the device configuration.
 data MsgSettingsReadResp = MsgSettingsReadResp
   { _msgSettingsReadResp_setting :: ByteString
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
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
@@ -25,18 +25,17 @@
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
+import SwiftNav.SBP.GnssSignal
 
 -- | TrackingChannelState.
 --
--- Tracking channel state for a specific satellite PRN and measured signal
+-- Tracking channel state for a specific satellite signal and measured signal
 -- power.
 data TrackingChannelState = TrackingChannelState
   { _trackingChannelState_state :: Word8
     -- ^ Status of tracking channel
-  , _trackingChannelState_sid :: Word32
-    -- ^ Signal identifier being tracked - values 0x00 through 0x1F represent GPS
-    -- PRNs 1 through 32 respectively (PRN-1 notation); other values reserved
-    -- for future use
+  , _trackingChannelState_sid :: SBPGnssSignal
+    -- ^ GNSS signal being tracked
   , _trackingChannelState_cn0 :: Float
     -- ^ Carrier-to-noise density
   } deriving ( Show, Read, Eq )
@@ -44,13 +43,13 @@
 instance Binary TrackingChannelState where
   get = do
     _trackingChannelState_state <- getWord8
-    _trackingChannelState_sid <- getWord32le
+    _trackingChannelState_sid <- get
     _trackingChannelState_cn0 <- getFloat32le
     return TrackingChannelState {..}
 
   put TrackingChannelState {..} = do
     putWord8 _trackingChannelState_state
-    putWord32le _trackingChannelState_sid
+    put _trackingChannelState_sid
     putFloat32le _trackingChannelState_cn0
 $(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_trackingChannelState_" . stripPrefix "_trackingChannelState_"}
              ''TrackingChannelState)
@@ -62,8 +61,8 @@
 -- | SBP class for message MSG_TRACKING_STATE (0x0013).
 --
 -- The tracking message returns a variable-length array of tracking channel
--- states. It reports status and snr power measurements for all tracked
--- satellites.
+-- states. It reports status and carrier-to-noise density measurements for all
+-- tracked satellites.
 data MsgTrackingState = MsgTrackingState
   { _msgTrackingState_states :: [TrackingChannelState]
     -- ^ Satellite tracking channel state
@@ -116,10 +115,8 @@
 data MsgTrackingIq = MsgTrackingIq
   { _msgTrackingIq_channel :: Word8
     -- ^ Tracking channel of origin
-  , _msgTrackingIq_sid   :: Word32
-    -- ^ Signal identifier being tracked - values 0x00 through 0x1F represent GPS
-    -- PRNs 1 through 32 respectively (PRN-1 notation); other values reserved
-    -- for future use
+  , _msgTrackingIq_sid   :: SBPGnssSignal
+    -- ^ GNSS signal identifier
   , _msgTrackingIq_corrs :: [TrackingChannelCorrelation]
     -- ^ Early, Prompt and Late correlations
   } deriving ( Show, Read, Eq )
@@ -127,13 +124,13 @@
 instance Binary MsgTrackingIq where
   get = do
     _msgTrackingIq_channel <- getWord8
-    _msgTrackingIq_sid <- getWord32le
+    _msgTrackingIq_sid <- get
     _msgTrackingIq_corrs <- replicateM 3 get
     return MsgTrackingIq {..}
 
   put MsgTrackingIq {..} = do
     putWord8 _msgTrackingIq_channel
-    putWord32le _msgTrackingIq_sid
+    put _msgTrackingIq_sid
     mapM_ put _msgTrackingIq_corrs
 
 $(deriveSBP 'msgTrackingIq ''MsgTrackingIq)
diff --git a/src/SwiftNav/SBP/User.hs b/src/SwiftNav/SBP/User.hs
new file mode 100644
--- /dev/null
+++ b/src/SwiftNav/SBP/User.hs
@@ -0,0 +1,53 @@
+-- |
+-- Module:      SwiftNav.SBP.User
+-- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
+-- License:     LGPL-3
+-- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- Messages reserved for use by the user.
+
+module SwiftNav.SBP.User where
+
+import BasicPrelude
+import Control.Lens
+import Control.Monad.Loops
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.IEEE754
+import Data.Binary.Put
+import Data.ByteString
+import Data.ByteString.Lazy hiding ( ByteString )
+import Data.Int
+import Data.Word
+import SwiftNav.SBP.Encoding
+import SwiftNav.SBP.TH
+import SwiftNav.SBP.Types
+
+msgUserData :: Word16
+msgUserData = 0x0800
+
+-- | SBP class for message MSG_USER_DATA (0x0800).
+--
+-- This message can contain any application specific user data up to a maximum
+-- length of 255 bytes per message.
+data MsgUserData = MsgUserData
+  { _msgUserData_contents :: [Word8]
+    -- ^ User data payload
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgUserData where
+  get = do
+    _msgUserData_contents <- whileM (liftM not isEmpty) getWord8
+    return MsgUserData {..}
+
+  put MsgUserData {..} = do
+    mapM_ putWord8 _msgUserData_contents
+
+$(deriveSBP 'msgUserData ''MsgUserData)
+
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgUserData_" . stripPrefix "_msgUserData_"}
+             ''MsgUserData)
+$(makeLenses ''MsgUserData)
