diff --git a/sbp.cabal b/sbp.cabal
--- a/sbp.cabal
+++ b/sbp.cabal
@@ -1,5 +1,5 @@
 name:                  sbp
-version:               1.2.2
+version:               1.2.4
 synopsis:              SwiftNav's SBP Library
 homepage:              https://github.com/swift-nav/libsbp
 license:               LGPL-3
@@ -30,7 +30,7 @@
                      , SwiftNav.SBP.ExtEvents
                      , SwiftNav.SBP.FileIo
                      , SwiftNav.SBP.Flash
-                     , SwiftNav.SBP.GnssSignal
+                     , SwiftNav.SBP.Gnss
                      , SwiftNav.SBP.Logging
                      , SwiftNav.SBP.Navigation
                      , SwiftNav.SBP.Observation
diff --git a/src/SwiftNav/SBP.hs b/src/SwiftNav/SBP.hs
--- a/src/SwiftNav/SBP.hs
+++ b/src/SwiftNav/SBP.hs
@@ -16,7 +16,7 @@
   , module SwiftNav.SBP.ExtEvents
   , module SwiftNav.SBP.FileIo
   , module SwiftNav.SBP.Flash
-  , module SwiftNav.SBP.GnssSignal
+  , module SwiftNav.SBP.Gnss
   , module SwiftNav.SBP.Logging
   , module SwiftNav.SBP.Navigation
   , module SwiftNav.SBP.Observation
@@ -37,7 +37,7 @@
 import SwiftNav.SBP.ExtEvents
 import SwiftNav.SBP.FileIo
 import SwiftNav.SBP.Flash
-import SwiftNav.SBP.GnssSignal
+import SwiftNav.SBP.Gnss
 import SwiftNav.SBP.Logging
 import SwiftNav.SBP.Navigation
 import SwiftNav.SBP.Observation
@@ -56,6 +56,7 @@
 data SBPMsg =
      SBPMsgAcqResult MsgAcqResult Msg
    | SBPMsgAcqResultDepA MsgAcqResultDepA Msg
+   | SBPMsgAcqResultDepB MsgAcqResultDepB Msg
    | SBPMsgAcqSvProfile MsgAcqSvProfile Msg
    | SBPMsgAlmanac MsgAlmanac Msg
    | SBPMsgBasePosEcef MsgBasePosEcef Msg
@@ -129,6 +130,7 @@
    | SBPMsgTrackingIq MsgTrackingIq Msg
    | SBPMsgTrackingState MsgTrackingState Msg
    | SBPMsgTrackingStateDepA MsgTrackingStateDepA Msg
+   | SBPMsgTrackingStateDetailed MsgTrackingStateDetailed Msg
    | SBPMsgTweet MsgTweet Msg
    | SBPMsgUartState MsgUartState Msg
    | SBPMsgUartStateDepa MsgUartStateDepa Msg
@@ -150,6 +152,7 @@
           | checkCrc m /= _msgSBPCrc = SBPMsgBadCrc m
           | _msgSBPType == msgAcqResult = SBPMsgAcqResult (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgAcqResultDepA = SBPMsgAcqResultDepA (decode (fromStrict _msgSBPPayload)) m
+          | _msgSBPType == msgAcqResultDepB = SBPMsgAcqResultDepB (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgAcqSvProfile = SBPMsgAcqSvProfile (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgAlmanac = SBPMsgAlmanac (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgBasePosEcef = SBPMsgBasePosEcef (decode (fromStrict _msgSBPPayload)) m
@@ -223,6 +226,7 @@
           | _msgSBPType == msgTrackingIq = SBPMsgTrackingIq (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgTrackingState = SBPMsgTrackingState (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgTrackingStateDepA = SBPMsgTrackingStateDepA (decode (fromStrict _msgSBPPayload)) m
+          | _msgSBPType == msgTrackingStateDetailed = SBPMsgTrackingStateDetailed (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgTweet = SBPMsgTweet (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgUartState = SBPMsgUartState (decode (fromStrict _msgSBPPayload)) m
           | _msgSBPType == msgUartStateDepa = SBPMsgUartStateDepa (decode (fromStrict _msgSBPPayload)) m
@@ -236,6 +240,7 @@
     encode' sm where
       encode' (SBPMsgAcqResult _ m) = put m
       encode' (SBPMsgAcqResultDepA _ m) = put m
+      encode' (SBPMsgAcqResultDepB _ m) = put m
       encode' (SBPMsgAcqSvProfile _ m) = put m
       encode' (SBPMsgAlmanac _ m) = put m
       encode' (SBPMsgBasePosEcef _ m) = put m
@@ -309,6 +314,7 @@
       encode' (SBPMsgTrackingIq _ m) = put m
       encode' (SBPMsgTrackingState _ m) = put m
       encode' (SBPMsgTrackingStateDepA _ m) = put m
+      encode' (SBPMsgTrackingStateDetailed _ m) = put m
       encode' (SBPMsgTweet _ m) = put m
       encode' (SBPMsgUartState _ m) = put m
       encode' (SBPMsgUartStateDepa _ m) = put m
@@ -325,6 +331,7 @@
       decode' msgType
         | msgType == msgAcqResult = SBPMsgAcqResult <$> parseJSON obj <*> parseJSON obj
         | msgType == msgAcqResultDepA = SBPMsgAcqResultDepA <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgAcqResultDepB = SBPMsgAcqResultDepB <$> parseJSON obj <*> parseJSON obj
         | msgType == msgAcqSvProfile = SBPMsgAcqSvProfile <$> parseJSON obj <*> parseJSON obj
         | msgType == msgAlmanac = SBPMsgAlmanac <$> parseJSON obj <*> parseJSON obj
         | msgType == msgBasePosEcef = SBPMsgBasePosEcef <$> parseJSON obj <*> parseJSON obj
@@ -398,6 +405,7 @@
         | msgType == msgTrackingIq = SBPMsgTrackingIq <$> parseJSON obj <*> parseJSON obj
         | msgType == msgTrackingState = SBPMsgTrackingState <$> parseJSON obj <*> parseJSON obj
         | msgType == msgTrackingStateDepA = SBPMsgTrackingStateDepA <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgTrackingStateDetailed = SBPMsgTrackingStateDetailed <$> parseJSON obj <*> parseJSON obj
         | msgType == msgTweet = SBPMsgTweet <$> parseJSON obj <*> parseJSON obj
         | msgType == msgUartState = SBPMsgUartState <$> parseJSON obj <*> parseJSON obj
         | msgType == msgUartStateDepa = SBPMsgUartStateDepa <$> parseJSON obj <*> parseJSON obj
@@ -416,6 +424,7 @@
 instance ToJSON SBPMsg where
   toJSON (SBPMsgAcqResult n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgAcqResultDepA n m) = toJSON n `mergeValues` toJSON m
+  toJSON (SBPMsgAcqResultDepB n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgAcqSvProfile n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgAlmanac n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgBasePosEcef n m) = toJSON n `mergeValues` toJSON m
@@ -489,6 +498,7 @@
   toJSON (SBPMsgTrackingIq n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgTrackingState n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgTrackingStateDepA n m) = toJSON n `mergeValues` toJSON m
+  toJSON (SBPMsgTrackingStateDetailed n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgTweet n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgUartState n m) = toJSON n `mergeValues` toJSON m
   toJSON (SBPMsgUartStateDepa n m) = toJSON n `mergeValues` toJSON m
@@ -501,6 +511,7 @@
 instance HasMsg SBPMsg where
   msg f (SBPMsgAcqResult n m) = SBPMsgAcqResult n <$> f m
   msg f (SBPMsgAcqResultDepA n m) = SBPMsgAcqResultDepA n <$> f m
+  msg f (SBPMsgAcqResultDepB n m) = SBPMsgAcqResultDepB n <$> f m
   msg f (SBPMsgAcqSvProfile n m) = SBPMsgAcqSvProfile n <$> f m
   msg f (SBPMsgAlmanac n m) = SBPMsgAlmanac n <$> f m
   msg f (SBPMsgBasePosEcef n m) = SBPMsgBasePosEcef n <$> f m
@@ -574,6 +585,7 @@
   msg f (SBPMsgTrackingIq n m) = SBPMsgTrackingIq n <$> f m
   msg f (SBPMsgTrackingState n m) = SBPMsgTrackingState n <$> f m
   msg f (SBPMsgTrackingStateDepA n m) = SBPMsgTrackingStateDepA n <$> f m
+  msg f (SBPMsgTrackingStateDetailed n m) = SBPMsgTrackingStateDetailed n <$> f m
   msg f (SBPMsgTweet n m) = SBPMsgTweet n <$> f m
   msg f (SBPMsgUartState n m) = SBPMsgUartState n <$> f m
   msg f (SBPMsgUartStateDepa n m) = SBPMsgUartStateDepa n <$> f m
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
@@ -26,21 +26,20 @@
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
-import SwiftNav.SBP.GnssSignal
+import SwiftNav.SBP.Gnss
 
 msgAcqResult :: Word16
-msgAcqResult = 0x0014
+msgAcqResult = 0x001F
 
--- | SBP class for message MSG_ACQ_RESULT (0x0014).
+-- | SBP class for message MSG_ACQ_RESULT (0x001F).
 --
 -- This message describes the results from an attempted GPS signal acquisition
 -- search for a satellite PRN over a code phase/carrier frequency range. It
 -- contains the parameters of the point in the acquisition search space with
--- the best signal-to-noise (SNR) ratio.
+-- the best carrier-to-noise (CN/0) ratio.
 data MsgAcqResult = MsgAcqResult
-  { _msgAcqResult_snr :: Float
-    -- ^ 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_cn0 :: Float
+    -- ^ CN/0 of best point
   , _msgAcqResult_cp :: Float
     -- ^ Code phase of best point
   , _msgAcqResult_cf :: Float
@@ -51,14 +50,14 @@
 
 instance Binary MsgAcqResult where
   get = do
-    _msgAcqResult_snr <- getFloat32le
+    _msgAcqResult_cn0 <- getFloat32le
     _msgAcqResult_cp <- getFloat32le
     _msgAcqResult_cf <- getFloat32le
     _msgAcqResult_sid <- get
     return MsgAcqResult {..}
 
   put MsgAcqResult {..} = do
-    putFloat32le _msgAcqResult_snr
+    putFloat32le _msgAcqResult_cn0
     putFloat32le _msgAcqResult_cp
     putFloat32le _msgAcqResult_cf
     put _msgAcqResult_sid
@@ -69,6 +68,44 @@
              ''MsgAcqResult)
 $(makeLenses ''MsgAcqResult)
 
+msgAcqResultDepB :: Word16
+msgAcqResultDepB = 0x0014
+
+-- | SBP class for message MSG_ACQ_RESULT_DEP_B (0x0014).
+--
+-- Deprecated.
+data MsgAcqResultDepB = MsgAcqResultDepB
+  { _msgAcqResultDepB_snr :: Float
+    -- ^ SNR of best point. Currently in arbitrary SNR points, but will be in
+    -- units of dB Hz in a later revision of this message.
+  , _msgAcqResultDepB_cp :: Float
+    -- ^ Code phase of best point
+  , _msgAcqResultDepB_cf :: Float
+    -- ^ Carrier frequency of best point
+  , _msgAcqResultDepB_sid :: GnssSignal
+    -- ^ GNSS signal for which acquisition was attempted
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgAcqResultDepB where
+  get = do
+    _msgAcqResultDepB_snr <- getFloat32le
+    _msgAcqResultDepB_cp <- getFloat32le
+    _msgAcqResultDepB_cf <- getFloat32le
+    _msgAcqResultDepB_sid <- get
+    return MsgAcqResultDepB {..}
+
+  put MsgAcqResultDepB {..} = do
+    putFloat32le _msgAcqResultDepB_snr
+    putFloat32le _msgAcqResultDepB_cp
+    putFloat32le _msgAcqResultDepB_cf
+    put _msgAcqResultDepB_sid
+
+$(deriveSBP 'msgAcqResultDepB ''MsgAcqResultDepB)
+
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgAcqResultDepB_" . stripPrefix "_msgAcqResultDepB_"}
+             ''MsgAcqResultDepB)
+$(makeLenses ''MsgAcqResultDepB)
+
 msgAcqResultDepA :: Word16
 msgAcqResultDepA = 0x0015
 
@@ -130,11 +167,11 @@
     -- ^ Timestamp of the job complete event
   , _acqSvProfile_time_spent :: Word32
     -- ^ Time spent to search for sid.code
-  , _acqSvProfile_cf_min   :: Word32
+  , _acqSvProfile_cf_min   :: Int32
     -- ^ Doppler range lowest frequency
-  , _acqSvProfile_cf_max   :: Word32
+  , _acqSvProfile_cf_max   :: Int32
     -- ^ Doppler range highest frequency
-  , _acqSvProfile_cf       :: Word32
+  , _acqSvProfile_cf       :: Int32
     -- ^ Doppler value of detected peak. Only valid if status is '1'
   , _acqSvProfile_cp       :: Word32
     -- ^ Codephase of detected peak. Only valid if status is '1'
@@ -150,9 +187,9 @@
     _acqSvProfile_bin_width <- getWord16le
     _acqSvProfile_timestamp <- getWord32le
     _acqSvProfile_time_spent <- getWord32le
-    _acqSvProfile_cf_min <- getWord32le
-    _acqSvProfile_cf_max <- getWord32le
-    _acqSvProfile_cf <- getWord32le
+    _acqSvProfile_cf_min <- fromIntegral <$> getWord32le
+    _acqSvProfile_cf_max <- fromIntegral <$> getWord32le
+    _acqSvProfile_cf <- fromIntegral <$> getWord32le
     _acqSvProfile_cp <- getWord32le
     return AcqSvProfile {..}
 
@@ -165,9 +202,9 @@
     putWord16le _acqSvProfile_bin_width
     putWord32le _acqSvProfile_timestamp
     putWord32le _acqSvProfile_time_spent
-    putWord32le _acqSvProfile_cf_min
-    putWord32le _acqSvProfile_cf_max
-    putWord32le _acqSvProfile_cf
+    putWord32le $ fromIntegral _acqSvProfile_cf_min
+    putWord32le $ fromIntegral _acqSvProfile_cf_max
+    putWord32le $ fromIntegral _acqSvProfile_cf
     putWord32le _acqSvProfile_cp
 $(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_acqSvProfile_" . stripPrefix "_acqSvProfile_"}
              ''AcqSvProfile)
@@ -187,7 +224,7 @@
 
 instance Binary MsgAcqSvProfile where
   get = do
-    _msgAcqSvProfile_acq_sv_profile <- whileM (liftM not isEmpty) get
+    _msgAcqSvProfile_acq_sv_profile <- whileM (not <$> isEmpty) get
     return MsgAcqSvProfile {..}
 
   put MsgAcqSvProfile {..} = do
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
@@ -65,19 +65,19 @@
 data MsgBootloaderHandshakeResp = MsgBootloaderHandshakeResp
   { _msgBootloaderHandshakeResp_flags :: Word32
     -- ^ Bootloader flags
-  , _msgBootloaderHandshakeResp_version :: ByteString
+  , _msgBootloaderHandshakeResp_version :: Text
     -- ^ Bootloader version number
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgBootloaderHandshakeResp where
   get = do
     _msgBootloaderHandshakeResp_flags <- getWord32le
-    _msgBootloaderHandshakeResp_version <- liftM toStrict getRemainingLazyByteString
+    _msgBootloaderHandshakeResp_version <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgBootloaderHandshakeResp {..}
 
   put MsgBootloaderHandshakeResp {..} = do
     putWord32le _msgBootloaderHandshakeResp_flags
-    putByteString _msgBootloaderHandshakeResp_version
+    putByteString $ encodeUtf8 _msgBootloaderHandshakeResp_version
 
 $(deriveSBP 'msgBootloaderHandshakeResp ''MsgBootloaderHandshakeResp)
 
@@ -179,7 +179,7 @@
 
 instance Binary MsgBootloaderHandshakeDepA where
   get = do
-    _msgBootloaderHandshakeDepA_handshake <- whileM (liftM not isEmpty) getWord8
+    _msgBootloaderHandshakeDepA_handshake <- whileM (not <$> isEmpty) getWord8
     return MsgBootloaderHandshakeDepA {..}
 
   put MsgBootloaderHandshakeDepA {..} = do
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
@@ -53,7 +53,7 @@
   get = do
     _msgExtEvent_wn <- getWord16le
     _msgExtEvent_tow <- getWord32le
-    _msgExtEvent_ns <- liftM fromIntegral getWord32le
+    _msgExtEvent_ns <- fromIntegral <$> getWord32le
     _msgExtEvent_flags <- getWord8
     _msgExtEvent_pin <- getWord8
     return 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
@@ -51,7 +51,7 @@
     -- ^ File offset
   , _msgFileioReadReq_chunk_size :: Word8
     -- ^ Chunk size to read
-  , _msgFileioReadReq_filename :: ByteString
+  , _msgFileioReadReq_filename :: Text
     -- ^ Name of the file to read from
   } deriving ( Show, Read, Eq )
 
@@ -60,14 +60,14 @@
     _msgFileioReadReq_sequence <- getWord32le
     _msgFileioReadReq_offset <- getWord32le
     _msgFileioReadReq_chunk_size <- getWord8
-    _msgFileioReadReq_filename <- liftM toStrict getRemainingLazyByteString
+    _msgFileioReadReq_filename <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgFileioReadReq {..}
 
   put MsgFileioReadReq {..} = do
     putWord32le _msgFileioReadReq_sequence
     putWord32le _msgFileioReadReq_offset
     putWord8 _msgFileioReadReq_chunk_size
-    putByteString _msgFileioReadReq_filename
+    putByteString $ encodeUtf8 _msgFileioReadReq_filename
 
 $(deriveSBP 'msgFileioReadReq ''MsgFileioReadReq)
 
@@ -94,7 +94,7 @@
 instance Binary MsgFileioReadResp where
   get = do
     _msgFileioReadResp_sequence <- getWord32le
-    _msgFileioReadResp_contents <- whileM (liftM not isEmpty) getWord8
+    _msgFileioReadResp_contents <- whileM (not <$> isEmpty) getWord8
     return MsgFileioReadResp {..}
 
   put MsgFileioReadResp {..} = do
@@ -125,7 +125,7 @@
     -- ^ Read sequence number
   , _msgFileioReadDirReq_offset :: Word32
     -- ^ The offset to skip the first n elements of the file list
-  , _msgFileioReadDirReq_dirname :: ByteString
+  , _msgFileioReadDirReq_dirname :: Text
     -- ^ Name of the directory to list
   } deriving ( Show, Read, Eq )
 
@@ -133,13 +133,13 @@
   get = do
     _msgFileioReadDirReq_sequence <- getWord32le
     _msgFileioReadDirReq_offset <- getWord32le
-    _msgFileioReadDirReq_dirname <- liftM toStrict getRemainingLazyByteString
+    _msgFileioReadDirReq_dirname <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgFileioReadDirReq {..}
 
   put MsgFileioReadDirReq {..} = do
     putWord32le _msgFileioReadDirReq_sequence
     putWord32le _msgFileioReadDirReq_offset
-    putByteString _msgFileioReadDirReq_dirname
+    putByteString $ encodeUtf8 _msgFileioReadDirReq_dirname
 
 $(deriveSBP 'msgFileioReadDirReq ''MsgFileioReadDirReq)
 
@@ -167,7 +167,7 @@
 instance Binary MsgFileioReadDirResp where
   get = do
     _msgFileioReadDirResp_sequence <- getWord32le
-    _msgFileioReadDirResp_contents <- whileM (liftM not isEmpty) getWord8
+    _msgFileioReadDirResp_contents <- whileM (not <$> isEmpty) getWord8
     return MsgFileioReadDirResp {..}
 
   put MsgFileioReadDirResp {..} = do
@@ -190,17 +190,17 @@
 -- message". A device will only process this message when it is received from
 -- sender ID 0x42.
 data MsgFileioRemove = MsgFileioRemove
-  { _msgFileioRemove_filename :: ByteString
+  { _msgFileioRemove_filename :: Text
     -- ^ Name of the file to delete
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFileioRemove where
   get = do
-    _msgFileioRemove_filename <- liftM toStrict getRemainingLazyByteString
+    _msgFileioRemove_filename <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgFileioRemove {..}
 
   put MsgFileioRemove {..} = do
-    putByteString _msgFileioRemove_filename
+    putByteString $ encodeUtf8 _msgFileioRemove_filename
 
 $(deriveSBP 'msgFileioRemove ''MsgFileioRemove)
 
@@ -225,7 +225,7 @@
     -- ^ Write sequence number
   , _msgFileioWriteReq_offset :: Word32
     -- ^ Offset into the file at which to start writing in bytes
-  , _msgFileioWriteReq_filename :: ByteString
+  , _msgFileioWriteReq_filename :: Text
     -- ^ Name of the file to write to
   , _msgFileioWriteReq_data   :: [Word8]
     -- ^ Variable-length array of data to write
@@ -235,14 +235,14 @@
   get = do
     _msgFileioWriteReq_sequence <- getWord32le
     _msgFileioWriteReq_offset <- getWord32le
-    _msgFileioWriteReq_filename <- liftM toStrict getRemainingLazyByteString
-    _msgFileioWriteReq_data <- whileM (liftM not isEmpty) getWord8
+    _msgFileioWriteReq_filename <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
+    _msgFileioWriteReq_data <- whileM (not <$> isEmpty) getWord8
     return MsgFileioWriteReq {..}
 
   put MsgFileioWriteReq {..} = do
     putWord32le _msgFileioWriteReq_sequence
     putWord32le _msgFileioWriteReq_offset
-    putByteString _msgFileioWriteReq_filename
+    putByteString $ encodeUtf8 _msgFileioWriteReq_filename
     mapM_ putWord8 _msgFileioWriteReq_data
 
 $(deriveSBP 'msgFileioWriteReq ''MsgFileioWriteReq)
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
@@ -55,7 +55,7 @@
     _msgFlashProgram_target <- getWord8
     _msgFlashProgram_addr_start <- replicateM 3 getWord8
     _msgFlashProgram_addr_len <- getWord8
-    _msgFlashProgram_data <- whileM (liftM not isEmpty) getWord8
+    _msgFlashProgram_data <- whileM (not <$> isEmpty) getWord8
     return MsgFlashProgram {..}
 
   put MsgFlashProgram {..} = do
diff --git a/src/SwiftNav/SBP/Gnss.hs b/src/SwiftNav/SBP/Gnss.hs
new file mode 100644
--- /dev/null
+++ b/src/SwiftNav/SBP/Gnss.hs
@@ -0,0 +1,104 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+-- |
+-- Module:      SwiftNav.SBP.Gnss
+-- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
+-- License:     LGPL-3
+-- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- Various structs shared between modules
+
+module SwiftNav.SBP.Gnss where
+
+import BasicPrelude
+import Control.Lens
+import Control.Monad.Loops
+import Data.Aeson.TH           (defaultOptions, deriveJSON, 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
+
+-- | GnssSignal.
+--
+-- Signal identifier containing constellation, band, and satellite identifier
+data GnssSignal = GnssSignal
+  { _gnssSignal_sat    :: Word16
+    -- ^ Constellation-specific satellite identifier
+  , _gnssSignal_code   :: Word8
+    -- ^ Signal constellation, band and code
+  , _gnssSignal_reserved :: Word8
+    -- ^ Reserved
+  } deriving ( Show, Read, Eq )
+
+instance Binary GnssSignal where
+  get = do
+    _gnssSignal_sat <- getWord16le
+    _gnssSignal_code <- getWord8
+    _gnssSignal_reserved <- getWord8
+    return GnssSignal {..}
+
+  put GnssSignal {..} = do
+    putWord16le _gnssSignal_sat
+    putWord8 _gnssSignal_code
+    putWord8 _gnssSignal_reserved
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_gnssSignal_" . stripPrefix "_gnssSignal_"}
+             ''GnssSignal)
+$(makeLenses ''GnssSignal)
+
+-- | GPSTime.
+--
+-- A wire-appropriate GPS time, defined as the number of milliseconds since
+-- beginning of the week on the Saturday/Sunday transition.
+data GPSTime = GPSTime
+  { _gPSTime_tow :: Word32
+    -- ^ Milliseconds since start of GPS week
+  , _gPSTime_wn :: Word16
+    -- ^ GPS week number
+  } deriving ( Show, Read, Eq )
+
+instance Binary GPSTime where
+  get = do
+    _gPSTime_tow <- getWord32le
+    _gPSTime_wn <- getWord16le
+    return GPSTime {..}
+
+  put GPSTime {..} = do
+    putWord32le _gPSTime_tow
+    putWord16le _gPSTime_wn
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_gPSTime_" . stripPrefix "_gPSTime_"}
+             ''GPSTime)
+$(makeLenses ''GPSTime)
+
+-- | CarrierPhase.
+--
+-- Carrier phase measurement in cycles 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.  This phase has the same sign as the pseudorange.
+data CarrierPhase = CarrierPhase
+  { _carrierPhase_i :: Int32
+    -- ^ Carrier phase whole cycles
+  , _carrierPhase_f :: Word8
+    -- ^ Carrier phase fractional part
+  } deriving ( Show, Read, Eq )
+
+instance Binary CarrierPhase where
+  get = do
+    _carrierPhase_i <- fromIntegral <$> getWord32le
+    _carrierPhase_f <- getWord8
+    return CarrierPhase {..}
+
+  put CarrierPhase {..} = do
+    putWord32le $ fromIntegral _carrierPhase_i
+    putWord8 _carrierPhase_f
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_carrierPhase_" . stripPrefix "_carrierPhase_"}
+             ''CarrierPhase)
+$(makeLenses ''CarrierPhase)
diff --git a/src/SwiftNav/SBP/GnssSignal.hs b/src/SwiftNav/SBP/GnssSignal.hs
deleted file mode 100644
--- a/src/SwiftNav/SBP/GnssSignal.hs
+++ /dev/null
@@ -1,55 +0,0 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
--- |
--- 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           (defaultOptions, deriveJSON, 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
-
--- | GnssSignal.
---
--- Signal identifier containing constellation, band, and satellite identifier
-data GnssSignal = GnssSignal
-  { _gnssSignal_sat    :: Word16
-    -- ^ Constellation-specific satellite identifier
-  , _gnssSignal_code   :: Word8
-    -- ^ Signal constellation, band and code
-  , _gnssSignal_reserved :: Word8
-    -- ^ Reserved
-  } deriving ( Show, Read, Eq )
-
-instance Binary GnssSignal where
-  get = do
-    _gnssSignal_sat <- getWord16le
-    _gnssSignal_code <- getWord8
-    _gnssSignal_reserved <- getWord8
-    return GnssSignal {..}
-
-  put GnssSignal {..} = do
-    putWord16le _gnssSignal_sat
-    putWord8 _gnssSignal_code
-    putWord8 _gnssSignal_reserved
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_gnssSignal_" . stripPrefix "_gnssSignal_"}
-             ''GnssSignal)
-$(makeLenses ''GnssSignal)
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
@@ -38,19 +38,19 @@
 data MsgLog = MsgLog
   { _msgLog_level :: Word8
     -- ^ Logging level
-  , _msgLog_text :: ByteString
+  , _msgLog_text :: Text
     -- ^ Human-readable string
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgLog where
   get = do
     _msgLog_level <- getWord8
-    _msgLog_text <- liftM toStrict getRemainingLazyByteString
+    _msgLog_text <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgLog {..}
 
   put MsgLog {..} = do
     putWord8 _msgLog_level
-    putByteString _msgLog_text
+    putByteString $ encodeUtf8 _msgLog_text
 
 $(deriveSBP 'msgLog ''MsgLog)
 
@@ -65,17 +65,17 @@
 --
 -- All the news fit to tweet.
 data MsgTweet = MsgTweet
-  { _msgTweet_tweet :: ByteString
+  { _msgTweet_tweet :: Text
     -- ^ Human-readable string
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgTweet where
   get = do
-    _msgTweet_tweet <- getByteString 140
+    _msgTweet_tweet <- decodeUtf8 <$> getByteString 140
     return MsgTweet {..}
 
   put MsgTweet {..} = do
-    putByteString _msgTweet_tweet
+    putByteString $ encodeUtf8 _msgTweet_tweet
 
 $(deriveSBP 'msgTweet ''MsgTweet)
 
@@ -90,17 +90,17 @@
 --
 -- Deprecated.
 data MsgPrintDep = MsgPrintDep
-  { _msgPrintDep_text :: ByteString
+  { _msgPrintDep_text :: Text
     -- ^ Human-readable string
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgPrintDep where
   get = do
-    _msgPrintDep_text <- liftM toStrict getRemainingLazyByteString
+    _msgPrintDep_text <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgPrintDep {..}
 
   put MsgPrintDep {..} = do
-    putByteString _msgPrintDep_text
+    putByteString $ encodeUtf8 _msgPrintDep_text
 
 $(deriveSBP 'msgPrintDep ''MsgPrintDep)
 
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
@@ -65,7 +65,7 @@
   get = do
     _msgGpsTime_wn <- getWord16le
     _msgGpsTime_tow <- getWord32le
-    _msgGpsTime_ns <- liftM fromIntegral getWord32le
+    _msgGpsTime_ns <- fromIntegral <$> getWord32le
     _msgGpsTime_flags <- getWord8
     return MsgGpsTime {..}
 
@@ -270,9 +270,9 @@
 instance Binary MsgBaselineEcef where
   get = do
     _msgBaselineEcef_tow <- getWord32le
-    _msgBaselineEcef_x <- liftM fromIntegral getWord32le
-    _msgBaselineEcef_y <- liftM fromIntegral getWord32le
-    _msgBaselineEcef_z <- liftM fromIntegral getWord32le
+    _msgBaselineEcef_x <- fromIntegral <$> getWord32le
+    _msgBaselineEcef_y <- fromIntegral <$> getWord32le
+    _msgBaselineEcef_z <- fromIntegral <$> getWord32le
     _msgBaselineEcef_accuracy <- getWord16le
     _msgBaselineEcef_n_sats <- getWord8
     _msgBaselineEcef_flags <- getWord8
@@ -326,9 +326,9 @@
 instance Binary MsgBaselineNed where
   get = do
     _msgBaselineNed_tow <- getWord32le
-    _msgBaselineNed_n <- liftM fromIntegral getWord32le
-    _msgBaselineNed_e <- liftM fromIntegral getWord32le
-    _msgBaselineNed_d <- liftM fromIntegral 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
@@ -379,9 +379,9 @@
 instance Binary MsgVelEcef where
   get = do
     _msgVelEcef_tow <- getWord32le
-    _msgVelEcef_x <- liftM fromIntegral getWord32le
-    _msgVelEcef_y <- liftM fromIntegral getWord32le
-    _msgVelEcef_z <- liftM fromIntegral getWord32le
+    _msgVelEcef_x <- fromIntegral <$> getWord32le
+    _msgVelEcef_y <- fromIntegral <$> getWord32le
+    _msgVelEcef_z <- fromIntegral <$> getWord32le
     _msgVelEcef_accuracy <- getWord16le
     _msgVelEcef_n_sats <- getWord8
     _msgVelEcef_flags <- getWord8
@@ -433,9 +433,9 @@
 instance Binary MsgVelNed where
   get = do
     _msgVelNed_tow <- getWord32le
-    _msgVelNed_n <- liftM fromIntegral getWord32le
-    _msgVelNed_e <- liftM fromIntegral getWord32le
-    _msgVelNed_d <- liftM fromIntegral 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
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
@@ -26,62 +26,13 @@
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
-import SwiftNav.SBP.GnssSignal
-
--- | ObsGPSTime.
---
--- A wire-appropriate GPS time, defined as the number of milliseconds since
--- beginning of the week on the Saturday/Sunday transition.
-data ObsGPSTime = ObsGPSTime
-  { _obsGPSTime_tow :: Word32
-    -- ^ Milliseconds since start of GPS week
-  , _obsGPSTime_wn :: Word16
-    -- ^ GPS week number
-  } deriving ( Show, Read, Eq )
-
-instance Binary ObsGPSTime where
-  get = do
-    _obsGPSTime_tow <- getWord32le
-    _obsGPSTime_wn <- getWord16le
-    return ObsGPSTime {..}
-
-  put ObsGPSTime {..} = do
-    putWord32le _obsGPSTime_tow
-    putWord16le _obsGPSTime_wn
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_obsGPSTime_" . stripPrefix "_obsGPSTime_"}
-             ''ObsGPSTime)
-$(makeLenses ''ObsGPSTime)
-
--- | CarrierPhase.
---
--- Carrier phase measurement in cycles 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.  This phase has the  same sign as the pseudorange.
-data CarrierPhase = CarrierPhase
-  { _carrierPhase_i :: Int32
-    -- ^ Carrier phase whole cycles
-  , _carrierPhase_f :: Word8
-    -- ^ Carrier phase fractional part
-  } deriving ( Show, Read, Eq )
-
-instance Binary CarrierPhase where
-  get = do
-    _carrierPhase_i <- liftM fromIntegral getWord32le
-    _carrierPhase_f <- getWord8
-    return CarrierPhase {..}
-
-  put CarrierPhase {..} = do
-    putWord32le $ fromIntegral _carrierPhase_i
-    putWord8 _carrierPhase_f
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_carrierPhase_" . stripPrefix "_carrierPhase_"}
-             ''CarrierPhase)
-$(makeLenses ''CarrierPhase)
+import SwiftNav.SBP.Gnss
 
 -- | ObservationHeader.
 --
 -- Header of a GPS observation message.
 data ObservationHeader = ObservationHeader
-  { _observationHeader_t   :: ObsGPSTime
+  { _observationHeader_t   :: GPSTime
     -- ^ GPS time of this observation
   , _observationHeader_n_obs :: Word8
     -- ^ Total number of observations. First nibble is the size of the sequence
@@ -104,7 +55,7 @@
 -- | PackedObsContent.
 --
 -- Pseudorange and carrier phase observation for a satellite being tracked. The
--- observations should be interoperable with 3rd party  receivers and conform
+-- observations should be interoperable with 3rd party receivers and conform
 -- with typical RTCMv3 GNSS observations.
 data PackedObsContent = PackedObsContent
   { _packedObsContent_P  :: Word32
@@ -149,8 +100,8 @@
 -- 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  should be interoperable with 3rd party receivers and
--- conform  with typical RTCMv3 GNSS observations.
+-- The observations should 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
@@ -161,7 +112,7 @@
 instance Binary MsgObs where
   get = do
     _msgObs_header <- get
-    _msgObs_obs <- whileM (liftM not isEmpty) get
+    _msgObs_obs <- whileM (not <$> isEmpty) get
     return MsgObs {..}
 
   put MsgObs {..} = do
@@ -250,7 +201,7 @@
 data EphemerisCommonContent = EphemerisCommonContent
   { _ephemerisCommonContent_sid        :: GnssSignal
     -- ^ GNSS signal identifier
-  , _ephemerisCommonContent_toe        :: ObsGPSTime
+  , _ephemerisCommonContent_toe        :: GPSTime
     -- ^ Time of Ephemerides
   , _ephemerisCommonContent_ura        :: Double
     -- ^ User Range Accuracy
@@ -338,7 +289,7 @@
     -- ^ Polynomial clock correction coefficient (clock drift)
   , _msgEphemerisGps_af2    :: Double
     -- ^ Polynomial clock correction coefficient (rate of clock drift)
-  , _msgEphemerisGps_toc    :: ObsGPSTime
+  , _msgEphemerisGps_toc    :: GPSTime
     -- ^ Clock reference
   , _msgEphemerisGps_iode   :: Word8
     -- ^ Issue of ephemeris data
@@ -1059,7 +1010,7 @@
 
 instance Binary CarrierPhaseDepA where
   get = do
-    _carrierPhaseDepA_i <- liftM fromIntegral getWord32le
+    _carrierPhaseDepA_i <- fromIntegral <$> getWord32le
     _carrierPhaseDepA_f <- getWord8
     return CarrierPhaseDepA {..}
 
@@ -1161,7 +1112,7 @@
 instance Binary MsgObsDepA where
   get = do
     _msgObsDepA_header <- get
-    _msgObsDepA_obs <- whileM (liftM not isEmpty) get
+    _msgObsDepA_obs <- whileM (not <$> isEmpty) get
     return MsgObsDepA {..}
 
   put MsgObsDepA {..} = do
@@ -1179,10 +1130,10 @@
 
 -- | SBP class for message MSG_OBS_DEP_B (0x0043).
 --
--- This observation message has been deprecated in favor of  observations that
+-- This observation message has been deprecated in favor of observations that
 -- are more interoperable. This message should be used for observations
--- referenced to  a nominal pseudorange which are not interoperable with most
--- 3rd party GNSS receievers or typical RTCMv3  observations.
+-- referenced to a nominal pseudorange which are not interoperable with most
+-- 3rd party GNSS receievers or typical RTCMv3 observations.
 data MsgObsDepB = MsgObsDepB
   { _msgObsDepB_header :: ObservationHeader
     -- ^ Header of a GPS observation message
@@ -1193,7 +1144,7 @@
 instance Binary MsgObsDepB where
   get = do
     _msgObsDepB_header <- get
-    _msgObsDepB_obs <- whileM (liftM not isEmpty) get
+    _msgObsDepB_obs <- whileM (not <$> isEmpty) get
     return MsgObsDepB {..}
 
   put MsgObsDepB {..} = do
@@ -1215,7 +1166,7 @@
 -- utilize the ionospheric model for computation of the ionospheric delay.
 -- Please see ICD-GPS-200 (Chapter 20.3.3.5.1.7) for more details.
 data MsgIono = MsgIono
-  { _msgIono_t_nmct :: ObsGPSTime
+  { _msgIono_t_nmct :: GPSTime
     -- ^ Navigation Message Correction Table Valitidy Time
   , _msgIono_a0   :: Double
   , _msgIono_a1   :: Double
@@ -1264,7 +1215,7 @@
 --
 -- Please see ICD-GPS-200 (Chapter 20.3.3.5.1.4) for more details.
 data MsgSvConfigurationGps = MsgSvConfigurationGps
-  { _msgSvConfigurationGps_t_nmct :: ObsGPSTime
+  { _msgSvConfigurationGps_t_nmct :: GPSTime
     -- ^ Navigation Message Correction Table Valitidy Time
   , _msgSvConfigurationGps_l2c_mask :: Word32
     -- ^ L2C capability mask, SV32 bit being MSB, SV1 bit being LSB
@@ -1293,7 +1244,7 @@
 --
 -- Please see ICD-GPS-200 (30.3.3.3.1.1) for more details.
 data MsgGroupDelay = MsgGroupDelay
-  { _msgGroupDelay_t_op   :: ObsGPSTime
+  { _msgGroupDelay_t_op   :: GPSTime
     -- ^ Data Predict Time of Week
   , _msgGroupDelay_prn    :: Word8
     -- ^ Satellite number
@@ -1310,9 +1261,9 @@
     _msgGroupDelay_t_op <- get
     _msgGroupDelay_prn <- getWord8
     _msgGroupDelay_valid <- getWord8
-    _msgGroupDelay_tgd <- liftM fromIntegral getWord16le
-    _msgGroupDelay_isc_l1ca <- liftM fromIntegral getWord16le
-    _msgGroupDelay_isc_l2c <- liftM fromIntegral getWord16le
+    _msgGroupDelay_tgd <- fromIntegral <$> getWord16le
+    _msgGroupDelay_isc_l1ca <- fromIntegral <$> getWord16le
+    _msgGroupDelay_isc_l2c <- fromIntegral <$> getWord16le
     return MsgGroupDelay {..}
 
   put MsgGroupDelay {..} = do
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
@@ -28,7 +28,7 @@
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
-import SwiftNav.SBP.GnssSignal
+import SwiftNav.SBP.Gnss
 
 msgAlmanac :: Word16
 msgAlmanac = 0x0069
@@ -206,7 +206,7 @@
 -- (RTOS) thread usage statistics for the named thread. The reported percentage
 -- values must be normalized.
 data MsgThreadState = MsgThreadState
-  { _msgThreadState_name     :: ByteString
+  { _msgThreadState_name     :: Text
     -- ^ Thread name (NULL terminated)
   , _msgThreadState_cpu      :: Word16
     -- ^ Percentage cpu use for this thread. Values range from 0 - 1000 and needs
@@ -217,13 +217,13 @@
 
 instance Binary MsgThreadState where
   get = do
-    _msgThreadState_name <- getByteString 20
+    _msgThreadState_name <- decodeUtf8 <$> getByteString 20
     _msgThreadState_cpu <- getWord16le
     _msgThreadState_stack_free <- getWord32le
     return MsgThreadState {..}
 
   put MsgThreadState {..} = do
-    putByteString _msgThreadState_name
+    putByteString $ encodeUtf8 _msgThreadState_name
     putWord16le _msgThreadState_cpu
     putWord32le _msgThreadState_stack_free
 
@@ -293,10 +293,10 @@
 
 instance Binary Period where
   get = do
-    _period_avg <- liftM fromIntegral getWord32le
-    _period_pmin <- liftM fromIntegral getWord32le
-    _period_pmax <- liftM fromIntegral getWord32le
-    _period_current <- liftM fromIntegral getWord32le
+    _period_avg <- fromIntegral <$> getWord32le
+    _period_pmin <- fromIntegral <$> getWord32le
+    _period_pmax <- fromIntegral <$> getWord32le
+    _period_current <- fromIntegral <$> getWord32le
     return Period {..}
 
   put Period {..} = do
@@ -327,10 +327,10 @@
 
 instance Binary Latency where
   get = do
-    _latency_avg <- liftM fromIntegral getWord32le
-    _latency_lmin <- liftM fromIntegral getWord32le
-    _latency_lmax <- liftM fromIntegral getWord32le
-    _latency_current <- liftM fromIntegral getWord32le
+    _latency_avg <- fromIntegral <$> getWord32le
+    _latency_lmin <- fromIntegral <$> getWord32le
+    _latency_lmax <- fromIntegral <$> getWord32le
+    _latency_current <- fromIntegral <$> getWord32le
     return Latency {..}
 
   put Latency {..} = do
@@ -507,11 +507,11 @@
 
 instance Binary MsgDeviceMonitor where
   get = do
-    _msgDeviceMonitor_dev_vin <- liftM fromIntegral getWord16le
-    _msgDeviceMonitor_cpu_vint <- liftM fromIntegral getWord16le
-    _msgDeviceMonitor_cpu_vaux <- liftM fromIntegral getWord16le
-    _msgDeviceMonitor_cpu_temperature <- liftM fromIntegral getWord16le
-    _msgDeviceMonitor_fe_temperature <- liftM 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
     return MsgDeviceMonitor {..}
 
   put MsgDeviceMonitor {..} = do
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
@@ -61,7 +61,7 @@
 --
 -- The setting message writes the device configuration.
 data MsgSettingsWrite = MsgSettingsWrite
-  { _msgSettingsWrite_setting :: ByteString
+  { _msgSettingsWrite_setting :: Text
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
     -- SETTING, VALUE]. A device will only process to this message when it is
     -- received from sender ID 0x42.
@@ -69,11 +69,11 @@
 
 instance Binary MsgSettingsWrite where
   get = do
-    _msgSettingsWrite_setting <- liftM toStrict getRemainingLazyByteString
+    _msgSettingsWrite_setting <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgSettingsWrite {..}
 
   put MsgSettingsWrite {..} = do
-    putByteString _msgSettingsWrite_setting
+    putByteString $ encodeUtf8 _msgSettingsWrite_setting
 
 $(deriveSBP 'msgSettingsWrite ''MsgSettingsWrite)
 
@@ -88,7 +88,7 @@
 --
 -- The setting message reads the device configuration.
 data MsgSettingsReadReq = MsgSettingsReadReq
-  { _msgSettingsReadReq_setting :: ByteString
+  { _msgSettingsReadReq_setting :: Text
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
     -- SETTING]. A device will only respond to this message when it is received
     -- from sender ID 0x42.
@@ -96,11 +96,11 @@
 
 instance Binary MsgSettingsReadReq where
   get = do
-    _msgSettingsReadReq_setting <- liftM toStrict getRemainingLazyByteString
+    _msgSettingsReadReq_setting <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgSettingsReadReq {..}
 
   put MsgSettingsReadReq {..} = do
-    putByteString _msgSettingsReadReq_setting
+    putByteString $ encodeUtf8 _msgSettingsReadReq_setting
 
 $(deriveSBP 'msgSettingsReadReq ''MsgSettingsReadReq)
 
@@ -115,18 +115,18 @@
 --
 -- The setting message reads the device configuration.
 data MsgSettingsReadResp = MsgSettingsReadResp
-  { _msgSettingsReadResp_setting :: ByteString
+  { _msgSettingsReadResp_setting :: Text
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
     -- SETTING, VALUE].
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSettingsReadResp where
   get = do
-    _msgSettingsReadResp_setting <- liftM toStrict getRemainingLazyByteString
+    _msgSettingsReadResp_setting <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgSettingsReadResp {..}
 
   put MsgSettingsReadResp {..} = do
-    putByteString _msgSettingsReadResp_setting
+    putByteString $ encodeUtf8 _msgSettingsReadResp_setting
 
 $(deriveSBP 'msgSettingsReadResp ''MsgSettingsReadResp)
 
@@ -175,7 +175,7 @@
   { _msgSettingsReadByIndexResp_index :: Word16
     -- ^ An index into the device settings, with values ranging from 0 to
     -- length(settings)
-  , _msgSettingsReadByIndexResp_setting :: ByteString
+  , _msgSettingsReadByIndexResp_setting :: Text
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
     -- SETTING, VALUE].
   } deriving ( Show, Read, Eq )
@@ -183,12 +183,12 @@
 instance Binary MsgSettingsReadByIndexResp where
   get = do
     _msgSettingsReadByIndexResp_index <- getWord16le
-    _msgSettingsReadByIndexResp_setting <- liftM toStrict getRemainingLazyByteString
+    _msgSettingsReadByIndexResp_setting <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgSettingsReadByIndexResp {..}
 
   put MsgSettingsReadByIndexResp {..} = do
     putWord16le _msgSettingsReadByIndexResp_index
-    putByteString _msgSettingsReadByIndexResp_setting
+    putByteString $ encodeUtf8 _msgSettingsReadByIndexResp_setting
 
 $(deriveSBP 'msgSettingsReadByIndexResp ''MsgSettingsReadByIndexResp)
 
@@ -227,18 +227,18 @@
 -- settings daemon.  The host should reply with MSG_SETTINGS_WRITE for this
 -- setting to set the initial value.
 data MsgSettingsRegister = MsgSettingsRegister
-  { _msgSettingsRegister_setting :: ByteString
+  { _msgSettingsRegister_setting :: Text
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
     -- SETTING, VALUE].
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSettingsRegister where
   get = do
-    _msgSettingsRegister_setting <- liftM toStrict getRemainingLazyByteString
+    _msgSettingsRegister_setting <- decodeUtf8 . toStrict <$> getRemainingLazyByteString
     return MsgSettingsRegister {..}
 
   put MsgSettingsRegister {..} = do
-    putByteString _msgSettingsRegister_setting
+    putByteString $ encodeUtf8 _msgSettingsRegister_setting
 
 $(deriveSBP 'msgSettingsRegister ''MsgSettingsRegister)
 
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
@@ -26,8 +26,118 @@
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
-import SwiftNav.SBP.GnssSignal
+import SwiftNav.SBP.Gnss
 
+msgTrackingStateDetailed :: Word16
+msgTrackingStateDetailed = 0x0011
+
+-- | SBP class for message MSG_TRACKING_STATE_DETAILED (0x0011).
+--
+-- The tracking message returns a set tracking channel parameters for a single
+-- tracking channel useful for debugging issues.
+data MsgTrackingStateDetailed = MsgTrackingStateDetailed
+  { _msgTrackingStateDetailed_recv_time  :: Word64
+    -- ^ Receiver clock time.
+  , _msgTrackingStateDetailed_tot        :: GPSTime
+    -- ^ Time of transmission of signal from satellite.
+  , _msgTrackingStateDetailed_P          :: Word32
+    -- ^ Pseudorange observation.
+  , _msgTrackingStateDetailed_P_std      :: Word16
+    -- ^ Pseudorange observation standard deviation.
+  , _msgTrackingStateDetailed_L          :: CarrierPhase
+    -- ^ Carrier phase observation with typical sign convention. Only valid when
+    -- PLL pessimistic lock is achieved.
+  , _msgTrackingStateDetailed_cn0        :: Word8
+    -- ^ Carrier-to-Noise density
+  , _msgTrackingStateDetailed_lock       :: Word16
+    -- ^ Lock indicator. This value changes whenever a satellite signal has lost
+    -- and regained lock, indicating that the carrier phase ambiguity may have
+    -- changed.
+  , _msgTrackingStateDetailed_sid        :: GnssSignal
+    -- ^ GNSS signal identifier.
+  , _msgTrackingStateDetailed_doppler    :: Int32
+    -- ^ Carrier Doppler frequency.
+  , _msgTrackingStateDetailed_doppler_std :: Word16
+    -- ^ Carrier Doppler frequency standard deviation.
+  , _msgTrackingStateDetailed_uptime     :: Word32
+    -- ^ Number of seconds of continuous tracking. Specifies how much time signal
+    -- is in continuous track.
+  , _msgTrackingStateDetailed_clock_offset :: Word16
+    -- ^ TCXO clock offset.
+  , _msgTrackingStateDetailed_clock_drift :: Word16
+    -- ^ TCXO clock drift.
+  , _msgTrackingStateDetailed_corr_spacing :: Word16
+    -- ^ Early-Prompt (EP) and Prompt-Late (PL) correlators spacing.
+  , _msgTrackingStateDetailed_acceleration :: Word8
+    -- ^ Acceleration. Valid only when acceleration valid flag is set.
+  , _msgTrackingStateDetailed_sync_flags :: Word8
+    -- ^ Synchronization status flags.
+  , _msgTrackingStateDetailed_tow_flags  :: Word8
+    -- ^ TOW status flags.
+  , _msgTrackingStateDetailed_track_flags :: Word8
+    -- ^ Tracking loop status flags.
+  , _msgTrackingStateDetailed_nav_flags  :: Word8
+    -- ^ Navigation data status flags.
+  , _msgTrackingStateDetailed_pset_flags :: Word8
+    -- ^ Parameters sets flags.
+  , _msgTrackingStateDetailed_misc_flags :: Word8
+    -- ^ Miscellaneous flags.
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgTrackingStateDetailed where
+  get = do
+    _msgTrackingStateDetailed_recv_time <- getWord64le
+    _msgTrackingStateDetailed_tot <- get
+    _msgTrackingStateDetailed_P <- getWord32le
+    _msgTrackingStateDetailed_P_std <- getWord16le
+    _msgTrackingStateDetailed_L <- get
+    _msgTrackingStateDetailed_cn0 <- getWord8
+    _msgTrackingStateDetailed_lock <- getWord16le
+    _msgTrackingStateDetailed_sid <- get
+    _msgTrackingStateDetailed_doppler <- fromIntegral <$> getWord32le
+    _msgTrackingStateDetailed_doppler_std <- getWord16le
+    _msgTrackingStateDetailed_uptime <- getWord32le
+    _msgTrackingStateDetailed_clock_offset <- getWord16le
+    _msgTrackingStateDetailed_clock_drift <- getWord16le
+    _msgTrackingStateDetailed_corr_spacing <- getWord16le
+    _msgTrackingStateDetailed_acceleration <- getWord8
+    _msgTrackingStateDetailed_sync_flags <- getWord8
+    _msgTrackingStateDetailed_tow_flags <- getWord8
+    _msgTrackingStateDetailed_track_flags <- getWord8
+    _msgTrackingStateDetailed_nav_flags <- getWord8
+    _msgTrackingStateDetailed_pset_flags <- getWord8
+    _msgTrackingStateDetailed_misc_flags <- getWord8
+    return MsgTrackingStateDetailed {..}
+
+  put MsgTrackingStateDetailed {..} = do
+    putWord64le _msgTrackingStateDetailed_recv_time
+    put _msgTrackingStateDetailed_tot
+    putWord32le _msgTrackingStateDetailed_P
+    putWord16le _msgTrackingStateDetailed_P_std
+    put _msgTrackingStateDetailed_L
+    putWord8 _msgTrackingStateDetailed_cn0
+    putWord16le _msgTrackingStateDetailed_lock
+    put _msgTrackingStateDetailed_sid
+    putWord32le $ fromIntegral _msgTrackingStateDetailed_doppler
+    putWord16le _msgTrackingStateDetailed_doppler_std
+    putWord32le _msgTrackingStateDetailed_uptime
+    putWord16le _msgTrackingStateDetailed_clock_offset
+    putWord16le _msgTrackingStateDetailed_clock_drift
+    putWord16le _msgTrackingStateDetailed_corr_spacing
+    putWord8 _msgTrackingStateDetailed_acceleration
+    putWord8 _msgTrackingStateDetailed_sync_flags
+    putWord8 _msgTrackingStateDetailed_tow_flags
+    putWord8 _msgTrackingStateDetailed_track_flags
+    putWord8 _msgTrackingStateDetailed_nav_flags
+    putWord8 _msgTrackingStateDetailed_pset_flags
+    putWord8 _msgTrackingStateDetailed_misc_flags
+
+$(deriveSBP 'msgTrackingStateDetailed ''MsgTrackingStateDetailed)
+
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgTrackingStateDetailed_" . stripPrefix "_msgTrackingStateDetailed_"}
+             ''MsgTrackingStateDetailed)
+$(makeLenses ''MsgTrackingStateDetailed)
+
 -- | TrackingChannelState.
 --
 -- Tracking channel state for a specific satellite signal and measured signal
@@ -66,12 +176,12 @@
 -- tracked satellites.
 data MsgTrackingState = MsgTrackingState
   { _msgTrackingState_states :: [TrackingChannelState]
-    -- ^ Satellite tracking channel state
+    -- ^ Signal tracking channel state
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgTrackingState where
   get = do
-    _msgTrackingState_states <- whileM (liftM not isEmpty) get
+    _msgTrackingState_states <- whileM (not <$> isEmpty) get
     return MsgTrackingState {..}
 
   put MsgTrackingState {..} = do
@@ -95,8 +205,8 @@
 
 instance Binary TrackingChannelCorrelation where
   get = do
-    _trackingChannelCorrelation_I <- liftM fromIntegral getWord32le
-    _trackingChannelCorrelation_Q <- liftM fromIntegral getWord32le
+    _trackingChannelCorrelation_I <- fromIntegral <$> getWord32le
+    _trackingChannelCorrelation_Q <- fromIntegral <$> getWord32le
     return TrackingChannelCorrelation {..}
 
   put TrackingChannelCorrelation {..} = do
@@ -180,7 +290,7 @@
 
 instance Binary MsgTrackingStateDepA where
   get = do
-    _msgTrackingStateDepA_states <- whileM (liftM not isEmpty) get
+    _msgTrackingStateDepA_states <- whileM (not <$> isEmpty) get
     return MsgTrackingStateDepA {..}
 
   put MsgTrackingStateDepA {..} = do
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
@@ -41,7 +41,7 @@
 
 instance Binary MsgUserData where
   get = do
-    _msgUserData_contents <- whileM (liftM not isEmpty) getWord8
+    _msgUserData_contents <- whileM (not <$> isEmpty) getWord8
     return MsgUserData {..}
 
   put MsgUserData {..} = do
