diff --git a/sbp.cabal b/sbp.cabal
--- a/sbp.cabal
+++ b/sbp.cabal
@@ -1,5 +1,5 @@
 name:                  sbp
-version:               0.50.7
+version:               0.50.8
 synopsis:              SwiftNav's SBP Library
 homepage:              https://github.com/swift-nav/libsbp
 license:               LGPL-3
@@ -49,8 +49,8 @@
                      , binary
                      , bytestring
                      , data-binary-ieee754
+                     , lens
                      , monad-loops
-                     , scientific
                      , text
                      , unordered-containers
   default-extensions:  RecordWildCards
diff --git a/src/SwiftNav/SBP.hs b/src/SwiftNav/SBP.hs
--- a/src/SwiftNav/SBP.hs
+++ b/src/SwiftNav/SBP.hs
@@ -11,16 +11,14 @@
 module SwiftNav.SBP where
 
 import BasicPrelude hiding (lookup)
-import Data.Aeson hiding (decode)
+import Control.Lens hiding ((.=))
+import Data.Aeson hiding (decode, decode')
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.Put
-import Data.ByteString
 import Data.ByteString.Lazy hiding (ByteString)
 import Data.ByteString.Builder
 import Data.HashMap.Strict
-import Data.Scientific
-import Data.Word
 import SwiftNav.CRC16
 import SwiftNav.SBP.Acquisition
 import SwiftNav.SBP.Bootload
@@ -44,36 +42,38 @@
 defaultSenderID = 0x42
 
 data Msg = Msg
-  { msgSBPType    :: Word16
-  , msgSBPSender  :: Word16
-  , msgSBPLen     :: Word8
-  , msgSBPPayload :: !ByteString
-  , msgSBPCrc     :: Word16
+  { _msgSBPType    :: Word16
+  , _msgSBPSender  :: Word16
+  , _msgSBPLen     :: Word8
+  , _msgSBPPayload :: !ByteString
+  , _msgSBPCrc     :: Word16
   } deriving ( Show, Read, Eq )
 
+$(makeLenses ''Msg)
+
 instance Binary Msg where
   get = do
-    msgSBPType <- getWord16le
-    msgSBPSender <- getWord16le
-    msgSBPLen <- getWord8
-    msgSBPPayload <- getByteString $ fromIntegral msgSBPLen
-    msgSBPCrc <- getWord16le
+    _msgSBPType <- getWord16le
+    _msgSBPSender <- getWord16le
+    _msgSBPLen <- getWord8
+    _msgSBPPayload <- getByteString $ fromIntegral _msgSBPLen
+    _msgSBPCrc <- getWord16le
     return Msg {..}
 
   put Msg {..} = do
-    putWord16le msgSBPType
-    putWord16le msgSBPSender
-    putWord8 msgSBPLen
-    putByteString msgSBPPayload
-    putWord16le msgSBPCrc
+    putWord16le _msgSBPType
+    putWord16le _msgSBPSender
+    putWord8 _msgSBPLen
+    putByteString _msgSBPPayload
+    putWord16le _msgSBPCrc
 
 checkCrc :: Msg -> Word16
 checkCrc Msg {..} =
   crc16 $ toLazyByteString $
-    word16LE msgSBPType   <>
-    word16LE msgSBPSender <>
-    word8 msgSBPLen       <>
-    byteString msgSBPPayload
+    word16LE _msgSBPType   <>
+    word16LE _msgSBPSender <>
+    word8 _msgSBPLen       <>
+    byteString _msgSBPPayload
 
 instance FromJSON Msg where
   parseJSON (Object v) = do
@@ -85,13 +85,14 @@
   parseJSON _ = mzero
 
 instance ToJSON Msg where
-  toJSON Msg {..} = object [ "preamble" .= msgPreamble
-                           , "msg_type" .= msgSBPType
-                           , "sender" .= msgSBPSender
-                           , "length" .= msgSBPLen
-                           , "payload" .= msgSBPPayload
-                           , "crc" .= msgSBPCrc
-                           ]
+  toJSON Msg {..} = object
+    [ "preamble" .= msgPreamble
+    , "msg_type" .= _msgSBPType
+    , "sender" .= _msgSBPSender
+    , "length" .= _msgSBPLen
+    , "payload" .= _msgSBPPayload
+    , "crc" .= _msgSBPCrc
+    ]
 
 data SBPMsg =
      SBPMsgAcqResult MsgAcqResult Msg
@@ -171,73 +172,73 @@
       sbp <- get
       return $ decode' sbp where
         decode' sbp@Msg {..}
-          | checkCrc sbp /= msgSBPCrc = SBPMsgBadCrc sbp
-          | msgSBPType == msgAcqResult = SBPMsgAcqResult (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgAcqResultDepA = SBPMsgAcqResultDepA (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgAlmanac = SBPMsgAlmanac (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgBasePos = SBPMsgBasePos (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgBaselineEcef = SBPMsgBaselineEcef (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgBaselineNed = SBPMsgBaselineNed (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgBootloaderHandshakeDepA = SBPMsgBootloaderHandshakeDepA (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgBootloaderHandshakeReq = SBPMsgBootloaderHandshakeReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgBootloaderHandshakeResp = SBPMsgBootloaderHandshakeResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgBootloaderJumpToApp = SBPMsgBootloaderJumpToApp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgCwResults = SBPMsgCwResults (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgCwStart = SBPMsgCwStart (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgDops = SBPMsgDops (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgEphemeris = SBPMsgEphemeris (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgEphemerisDepA = SBPMsgEphemerisDepA (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgEphemerisDepB = SBPMsgEphemerisDepB (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgExtEvent = SBPMsgExtEvent (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFileioReadDirReq = SBPMsgFileioReadDirReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFileioReadDirResp = SBPMsgFileioReadDirResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFileioReadReq = SBPMsgFileioReadReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFileioReadResp = SBPMsgFileioReadResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFileioRemove = SBPMsgFileioRemove (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFileioWriteReq = SBPMsgFileioWriteReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFileioWriteResp = SBPMsgFileioWriteResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFlashDone = SBPMsgFlashDone (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFlashErase = SBPMsgFlashErase (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFlashProgram = SBPMsgFlashProgram (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFlashReadReq = SBPMsgFlashReadReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgFlashReadResp = SBPMsgFlashReadResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgGpsTime = SBPMsgGpsTime (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgHeartbeat = SBPMsgHeartbeat (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgIarState = SBPMsgIarState (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgInitBase = SBPMsgInitBase (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgLog = SBPMsgLog (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgMaskSatellite = SBPMsgMaskSatellite (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgObs = SBPMsgObs (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgObsDepA = SBPMsgObsDepA (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgPosEcef = SBPMsgPosEcef (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgPosLlh = SBPMsgPosLlh (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgPrintDep = SBPMsgPrintDep (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgReset = SBPMsgReset (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgResetFilters = SBPMsgResetFilters (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgSetTime = SBPMsgSetTime (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgSettingsReadByIndexDone = SBPMsgSettingsReadByIndexDone (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgSettingsReadByIndexReq = SBPMsgSettingsReadByIndexReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgSettingsReadByIndexResp = SBPMsgSettingsReadByIndexResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgSettingsReadReq = SBPMsgSettingsReadReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgSettingsReadResp = SBPMsgSettingsReadResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgSettingsSave = SBPMsgSettingsSave (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgSettingsWrite = SBPMsgSettingsWrite (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgStartup = SBPMsgStartup (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgStmFlashLockSector = SBPMsgStmFlashLockSector (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgStmUniqueIdResp = SBPMsgStmUniqueIdResp (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgThreadState = SBPMsgThreadState (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgTrackingIq = SBPMsgTrackingIq (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgTrackingState = SBPMsgTrackingState (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgTrackingStateDepA = SBPMsgTrackingStateDepA (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgTweet = SBPMsgTweet (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgUartState = SBPMsgUartState (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgVelEcef = SBPMsgVelEcef (decode (fromStrict msgSBPPayload)) sbp
-          | msgSBPType == msgVelNed = SBPMsgVelNed (decode (fromStrict msgSBPPayload)) sbp
+          | checkCrc sbp /= _msgSBPCrc = SBPMsgBadCrc sbp
+          | _msgSBPType == msgAcqResult = SBPMsgAcqResult (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgAcqResultDepA = SBPMsgAcqResultDepA (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgAlmanac = SBPMsgAlmanac (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBasePos = SBPMsgBasePos (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBaselineEcef = SBPMsgBaselineEcef (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBaselineNed = SBPMsgBaselineNed (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBootloaderHandshakeDepA = SBPMsgBootloaderHandshakeDepA (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBootloaderHandshakeReq = SBPMsgBootloaderHandshakeReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBootloaderHandshakeResp = SBPMsgBootloaderHandshakeResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBootloaderJumpToApp = SBPMsgBootloaderJumpToApp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgCwResults = SBPMsgCwResults (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgCwStart = SBPMsgCwStart (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgDops = SBPMsgDops (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgEphemeris = SBPMsgEphemeris (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgEphemerisDepA = SBPMsgEphemerisDepA (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgEphemerisDepB = SBPMsgEphemerisDepB (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgExtEvent = SBPMsgExtEvent (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFileioReadDirReq = SBPMsgFileioReadDirReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFileioReadDirResp = SBPMsgFileioReadDirResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFileioReadReq = SBPMsgFileioReadReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFileioReadResp = SBPMsgFileioReadResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFileioRemove = SBPMsgFileioRemove (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFileioWriteReq = SBPMsgFileioWriteReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFileioWriteResp = SBPMsgFileioWriteResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFlashDone = SBPMsgFlashDone (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFlashErase = SBPMsgFlashErase (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFlashProgram = SBPMsgFlashProgram (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFlashReadReq = SBPMsgFlashReadReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgFlashReadResp = SBPMsgFlashReadResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgGpsTime = SBPMsgGpsTime (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgHeartbeat = SBPMsgHeartbeat (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgIarState = SBPMsgIarState (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgInitBase = SBPMsgInitBase (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgLog = SBPMsgLog (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgMaskSatellite = SBPMsgMaskSatellite (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgObs = SBPMsgObs (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgObsDepA = SBPMsgObsDepA (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgPosEcef = SBPMsgPosEcef (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgPosLlh = SBPMsgPosLlh (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgPrintDep = SBPMsgPrintDep (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgReset = SBPMsgReset (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgResetFilters = SBPMsgResetFilters (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgSetTime = SBPMsgSetTime (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgSettingsReadByIndexDone = SBPMsgSettingsReadByIndexDone (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgSettingsReadByIndexReq = SBPMsgSettingsReadByIndexReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgSettingsReadByIndexResp = SBPMsgSettingsReadByIndexResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgSettingsReadReq = SBPMsgSettingsReadReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgSettingsReadResp = SBPMsgSettingsReadResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgSettingsSave = SBPMsgSettingsSave (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgSettingsWrite = SBPMsgSettingsWrite (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgStartup = SBPMsgStartup (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgStmFlashLockSector = SBPMsgStmFlashLockSector (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgStmUniqueIdResp = SBPMsgStmUniqueIdResp (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgThreadState = SBPMsgThreadState (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgTrackingIq = SBPMsgTrackingIq (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgTrackingState = SBPMsgTrackingState (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgTrackingStateDepA = SBPMsgTrackingStateDepA (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgTweet = SBPMsgTweet (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgUartState = SBPMsgUartState (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgVelEcef = SBPMsgVelEcef (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgVelNed = SBPMsgVelNed (decode (fromStrict _msgSBPPayload)) sbp
           | otherwise = SBPMsgUnknown sbp
 
   put msg = do
@@ -314,151 +315,151 @@
       encode' (SBPMsgBadCrc  sbp) = sbp
 
 instance FromJSON SBPMsg where
-  parseJSON (Object o) = case lookup "msg_type" o of
-    Nothing         -> mzero
-    Just (Number msgType) -> decode' (toBoundedInteger msgType) where
-                               decode' Nothing = mzero
-                               decode' (Just t)
-                                 | t == msgAcqResult = SBPMsgAcqResult <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgAcqResultDepA = SBPMsgAcqResultDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgAlmanac = SBPMsgAlmanac <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgBasePos = SBPMsgBasePos <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgBaselineEcef = SBPMsgBaselineEcef <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgBaselineNed = SBPMsgBaselineNed <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgBootloaderHandshakeDepA = SBPMsgBootloaderHandshakeDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgBootloaderHandshakeReq = SBPMsgBootloaderHandshakeReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgBootloaderHandshakeResp = SBPMsgBootloaderHandshakeResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgBootloaderJumpToApp = SBPMsgBootloaderJumpToApp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgCwResults = SBPMsgCwResults <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgCwStart = SBPMsgCwStart <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgDops = SBPMsgDops <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgEphemeris = SBPMsgEphemeris <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgEphemerisDepA = SBPMsgEphemerisDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgEphemerisDepB = SBPMsgEphemerisDepB <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgExtEvent = SBPMsgExtEvent <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFileioReadDirReq = SBPMsgFileioReadDirReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFileioReadDirResp = SBPMsgFileioReadDirResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFileioReadReq = SBPMsgFileioReadReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFileioReadResp = SBPMsgFileioReadResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFileioRemove = SBPMsgFileioRemove <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFileioWriteReq = SBPMsgFileioWriteReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFileioWriteResp = SBPMsgFileioWriteResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFlashDone = SBPMsgFlashDone <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFlashErase = SBPMsgFlashErase <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFlashProgram = SBPMsgFlashProgram <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFlashReadReq = SBPMsgFlashReadReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgFlashReadResp = SBPMsgFlashReadResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgGpsTime = SBPMsgGpsTime <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgHeartbeat = SBPMsgHeartbeat <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgIarState = SBPMsgIarState <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgInitBase = SBPMsgInitBase <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgLog = SBPMsgLog <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgMaskSatellite = SBPMsgMaskSatellite <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgObs = SBPMsgObs <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgObsDepA = SBPMsgObsDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgPosEcef = SBPMsgPosEcef <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgPosLlh = SBPMsgPosLlh <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgPrintDep = SBPMsgPrintDep <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgReset = SBPMsgReset <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgResetFilters = SBPMsgResetFilters <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgSetTime = SBPMsgSetTime <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgSettingsReadByIndexDone = SBPMsgSettingsReadByIndexDone <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgSettingsReadByIndexReq = SBPMsgSettingsReadByIndexReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgSettingsReadByIndexResp = SBPMsgSettingsReadByIndexResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgSettingsReadReq = SBPMsgSettingsReadReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgSettingsReadResp = SBPMsgSettingsReadResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgSettingsSave = SBPMsgSettingsSave <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgSettingsWrite = SBPMsgSettingsWrite <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgStartup = SBPMsgStartup <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgStmFlashLockSector = SBPMsgStmFlashLockSector <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgStmUniqueIdResp = SBPMsgStmUniqueIdResp <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgThreadState = SBPMsgThreadState <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgTrackingIq = SBPMsgTrackingIq <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgTrackingState = SBPMsgTrackingState <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgTrackingStateDepA = SBPMsgTrackingStateDepA <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgTweet = SBPMsgTweet <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgUartState = SBPMsgUartState <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgVelEcef = SBPMsgVelEcef <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | t == msgVelNed = SBPMsgVelNed <$> parseJSON (Object o) <*> parseJSON (Object o)
-                                 | otherwise = SBPMsgUnknown <$> parseJSON (Object o)
-    Just _ -> mzero
-  parseJSON (_) = mzero
+  parseJSON obj@(Object o) = do
+    msgType <- o .: "msg_type"
+    decode' msgType where
+      decode' msgType
+        | msgType == msgAcqResult = SBPMsgAcqResult <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgAcqResultDepA = SBPMsgAcqResultDepA <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgAlmanac = SBPMsgAlmanac <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgBasePos = SBPMsgBasePos <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgBaselineEcef = SBPMsgBaselineEcef <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgBaselineNed = SBPMsgBaselineNed <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgBootloaderHandshakeDepA = SBPMsgBootloaderHandshakeDepA <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgBootloaderHandshakeReq = SBPMsgBootloaderHandshakeReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgBootloaderHandshakeResp = SBPMsgBootloaderHandshakeResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgBootloaderJumpToApp = SBPMsgBootloaderJumpToApp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgCwResults = SBPMsgCwResults <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgCwStart = SBPMsgCwStart <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgDops = SBPMsgDops <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgEphemeris = SBPMsgEphemeris <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgEphemerisDepA = SBPMsgEphemerisDepA <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgEphemerisDepB = SBPMsgEphemerisDepB <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgExtEvent = SBPMsgExtEvent <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFileioReadDirReq = SBPMsgFileioReadDirReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFileioReadDirResp = SBPMsgFileioReadDirResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFileioReadReq = SBPMsgFileioReadReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFileioReadResp = SBPMsgFileioReadResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFileioRemove = SBPMsgFileioRemove <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFileioWriteReq = SBPMsgFileioWriteReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFileioWriteResp = SBPMsgFileioWriteResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFlashDone = SBPMsgFlashDone <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFlashErase = SBPMsgFlashErase <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFlashProgram = SBPMsgFlashProgram <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFlashReadReq = SBPMsgFlashReadReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgFlashReadResp = SBPMsgFlashReadResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgGpsTime = SBPMsgGpsTime <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgHeartbeat = SBPMsgHeartbeat <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgIarState = SBPMsgIarState <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgInitBase = SBPMsgInitBase <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgLog = SBPMsgLog <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgMaskSatellite = SBPMsgMaskSatellite <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgObs = SBPMsgObs <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgObsDepA = SBPMsgObsDepA <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgPosEcef = SBPMsgPosEcef <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgPosLlh = SBPMsgPosLlh <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgPrintDep = SBPMsgPrintDep <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgReset = SBPMsgReset <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgResetFilters = SBPMsgResetFilters <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgSetTime = SBPMsgSetTime <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgSettingsReadByIndexDone = SBPMsgSettingsReadByIndexDone <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgSettingsReadByIndexReq = SBPMsgSettingsReadByIndexReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgSettingsReadByIndexResp = SBPMsgSettingsReadByIndexResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgSettingsReadReq = SBPMsgSettingsReadReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgSettingsReadResp = SBPMsgSettingsReadResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgSettingsSave = SBPMsgSettingsSave <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgSettingsWrite = SBPMsgSettingsWrite <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgStartup = SBPMsgStartup <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgStmFlashLockSector = SBPMsgStmFlashLockSector <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgStmUniqueIdResp = SBPMsgStmUniqueIdResp <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgThreadState = SBPMsgThreadState <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgTrackingIq = SBPMsgTrackingIq <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgTrackingState = SBPMsgTrackingState <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgTrackingStateDepA = SBPMsgTrackingStateDepA <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgTweet = SBPMsgTweet <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgUartState = SBPMsgUartState <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgVelEcef = SBPMsgVelEcef <$> parseJSON obj <*> parseJSON obj
+        | msgType == msgVelNed = SBPMsgVelNed <$> parseJSON obj <*> parseJSON obj
+        | otherwise = SBPMsgUnknown <$> parseJSON obj
+  parseJSON _ = mzero
 
 merge :: Value -> Value -> Value
-merge (Object one) (Object two) = Object $ one <> two
-merge _ _ = undefined
+merge (Object one) (Object two) = Object (one <> two)
+merge _ (Object two) = Object two
+merge (Object one) _ = Object one
+merge _ v = v
 
 instance ToJSON SBPMsg where
-   toJSON (SBPMsgAcqResult msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgAcqResultDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgAlmanac msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgBasePos msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgBaselineEcef msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgBaselineNed msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgBootloaderHandshakeDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgBootloaderHandshakeReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgBootloaderHandshakeResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgBootloaderJumpToApp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgCwResults msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgCwStart msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgDops msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgEphemeris msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgEphemerisDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgEphemerisDepB msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgExtEvent msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFileioReadDirReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFileioReadDirResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFileioReadReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFileioReadResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFileioRemove msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFileioWriteReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFileioWriteResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFlashDone msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFlashErase msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFlashProgram msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFlashReadReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgFlashReadResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgGpsTime msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgHeartbeat msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgIarState msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgInitBase msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgLog msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgM25FlashWriteStatus msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgMaskSatellite msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgNapDeviceDnaReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgNapDeviceDnaResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgObs msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgObsDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgPosEcef msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgPosLlh msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgPrintDep msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgReset msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgResetFilters msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgSetTime msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgSettingsReadByIndexDone msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgSettingsReadByIndexReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgSettingsReadByIndexResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgSettingsReadReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgSettingsReadResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgSettingsSave msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgSettingsWrite msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgStartup msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgStmFlashLockSector msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgStmFlashUnlockSector msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgStmUniqueIdReq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgStmUniqueIdResp msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgThreadState msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgTrackingIq msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgTrackingState msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgTrackingStateDepA msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgTweet msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgUartState msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgVelEcef msg sbp) = merge (toJSON msg) (toJSON sbp)
-   toJSON (SBPMsgVelNed msg sbp) = merge (toJSON msg) (toJSON sbp)
+   toJSON (SBPMsgAcqResult msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgAcqResultDepA msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgAlmanac msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBasePos msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBaselineEcef msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBaselineNed msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBootloaderHandshakeDepA msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBootloaderHandshakeReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBootloaderHandshakeResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBootloaderJumpToApp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgCwResults msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgCwStart msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgDops msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgEphemeris msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgEphemerisDepA msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgEphemerisDepB msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgExtEvent msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFileioReadDirReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFileioReadDirResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFileioReadReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFileioReadResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFileioRemove msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFileioWriteReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFileioWriteResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFlashDone msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFlashErase msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFlashProgram msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFlashReadReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgFlashReadResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgGpsTime msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgHeartbeat msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgIarState msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgInitBase msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgLog msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgM25FlashWriteStatus msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgMaskSatellite msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgNapDeviceDnaReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgNapDeviceDnaResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgObs msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgObsDepA msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgPosEcef msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgPosLlh msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgPrintDep msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgReset msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgResetFilters msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgSetTime msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgSettingsReadByIndexDone msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgSettingsReadByIndexReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgSettingsReadByIndexResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgSettingsReadReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgSettingsReadResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgSettingsSave msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgSettingsWrite msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgStartup msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgStmFlashLockSector msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgStmFlashUnlockSector msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgStmUniqueIdReq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgStmUniqueIdResp msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgThreadState msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgTrackingIq msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgTrackingState msg sbp) = toJSON msg `merge` toJSON sbp
+   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 (SBPMsgVelEcef msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgVelNed msg sbp) = toJSON msg `merge` toJSON sbp
    toJSON (SBPMsgBadCrc sbp) = toJSON sbp
    toJSON (SBPMsgUnknown 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
@@ -11,7 +11,7 @@
 module SwiftNav.SBP.Acquisition where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -34,14 +34,14 @@
 -- contains the parameters of the point in the acquisition search space with
 -- the best signal-to-noise (SNR) ratio.
 data MsgAcqResult = MsgAcqResult
-  { msgAcqResult_snr :: Float
+  { _msgAcqResult_snr :: Float
     -- ^ SNR of best point. Currently dimensonless, but will have units of dB Hz
     -- in the revision of this message.
-  , msgAcqResult_cp :: Float
+  , _msgAcqResult_cp :: Float
     -- ^ Code phase of best point
-  , msgAcqResult_cf :: Float
+  , _msgAcqResult_cf :: Float
     -- ^ Carrier frequency of best point
-  , msgAcqResult_sid :: Word32
+  , _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.
@@ -49,20 +49,21 @@
 
 instance Binary MsgAcqResult where
   get = do
-    msgAcqResult_snr <- getFloat32le
-    msgAcqResult_cp <- getFloat32le
-    msgAcqResult_cf <- getFloat32le
-    msgAcqResult_sid <- getWord32le
+    _msgAcqResult_snr <- getFloat32le
+    _msgAcqResult_cp <- getFloat32le
+    _msgAcqResult_cf <- getFloat32le
+    _msgAcqResult_sid <- getWord32le
     return MsgAcqResult {..}
 
   put MsgAcqResult {..} = do
-    putFloat32le msgAcqResult_snr
-    putFloat32le msgAcqResult_cp
-    putFloat32le msgAcqResult_cf
-    putWord32le msgAcqResult_sid
+    putFloat32le _msgAcqResult_snr
+    putFloat32le _msgAcqResult_cp
+    putFloat32le _msgAcqResult_cf
+    putWord32le _msgAcqResult_sid
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgAcqResult_" . stripPrefix "msgAcqResult_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgAcqResult_" . stripPrefix "_msgAcqResult_"}
              ''MsgAcqResult)
+$(makeLenses ''MsgAcqResult)
 
 msgAcqResultDepA :: Word16
 msgAcqResultDepA = 0x0015
@@ -71,31 +72,32 @@
 --
 -- Deprecated.
 data MsgAcqResultDepA = MsgAcqResultDepA
-  { msgAcqResultDepA_snr :: Float
+  { _msgAcqResultDepA_snr :: Float
     -- ^ SNR of best point. Currently dimensonless, but will have units of dB Hz
     -- in the revision of this message.
-  , msgAcqResultDepA_cp :: Float
+  , _msgAcqResultDepA_cp :: Float
     -- ^ Code phase of best point
-  , msgAcqResultDepA_cf :: Float
+  , _msgAcqResultDepA_cf :: Float
     -- ^ Carrier frequency of best point
-  , msgAcqResultDepA_prn :: Word8
+  , _msgAcqResultDepA_prn :: Word8
     -- ^ PRN-1 identifier of the satellite signal for which acquisition was
     -- attempted
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgAcqResultDepA where
   get = do
-    msgAcqResultDepA_snr <- getFloat32le
-    msgAcqResultDepA_cp <- getFloat32le
-    msgAcqResultDepA_cf <- getFloat32le
-    msgAcqResultDepA_prn <- getWord8
+    _msgAcqResultDepA_snr <- getFloat32le
+    _msgAcqResultDepA_cp <- getFloat32le
+    _msgAcqResultDepA_cf <- getFloat32le
+    _msgAcqResultDepA_prn <- getWord8
     return MsgAcqResultDepA {..}
 
   put MsgAcqResultDepA {..} = do
-    putFloat32le msgAcqResultDepA_snr
-    putFloat32le msgAcqResultDepA_cp
-    putFloat32le msgAcqResultDepA_cf
-    putWord8 msgAcqResultDepA_prn
+    putFloat32le _msgAcqResultDepA_snr
+    putFloat32le _msgAcqResultDepA_cp
+    putFloat32le _msgAcqResultDepA_cf
+    putWord8 _msgAcqResultDepA_prn
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgAcqResultDepA_" . stripPrefix "msgAcqResultDepA_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgAcqResultDepA_" . stripPrefix "_msgAcqResultDepA_"}
              ''MsgAcqResultDepA)
+$(makeLenses ''MsgAcqResultDepA)
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
@@ -14,7 +14,7 @@
 module SwiftNav.SBP.Bootload where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -45,8 +45,9 @@
   put MsgBootloaderHandshakeReq =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgBootloaderHandshakeReq_" . stripPrefix "msgBootloaderHandshakeReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgBootloaderHandshakeReq_" . stripPrefix "_msgBootloaderHandshakeReq_"}
              ''MsgBootloaderHandshakeReq)
+$(makeLenses ''MsgBootloaderHandshakeReq)
 
 msgBootloaderHandshakeResp :: Word16
 msgBootloaderHandshakeResp = 0x00B4
@@ -58,24 +59,25 @@
 -- MSG_BOOTLOADER_HANDSHAKE_REQ.  The payload contains the bootloader version
 -- number and the SBP protocol version number.
 data MsgBootloaderHandshakeResp = MsgBootloaderHandshakeResp
-  { msgBootloaderHandshakeResp_flags  :: Word32
+  { _msgBootloaderHandshakeResp_flags :: Word32
     -- ^ Bootloader flags
-  , msgBootloaderHandshakeResp_version :: ByteString
+  , _msgBootloaderHandshakeResp_version :: ByteString
     -- ^ Bootloader version number
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgBootloaderHandshakeResp where
   get = do
-    msgBootloaderHandshakeResp_flags <- getWord32le
-    msgBootloaderHandshakeResp_version <- liftM toStrict getRemainingLazyByteString
+    _msgBootloaderHandshakeResp_flags <- getWord32le
+    _msgBootloaderHandshakeResp_version <- liftM toStrict getRemainingLazyByteString
     return MsgBootloaderHandshakeResp {..}
 
   put MsgBootloaderHandshakeResp {..} = do
-    putWord32le msgBootloaderHandshakeResp_flags
-    putByteString msgBootloaderHandshakeResp_version
+    putWord32le _msgBootloaderHandshakeResp_flags
+    putByteString _msgBootloaderHandshakeResp_version
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgBootloaderHandshakeResp_" . stripPrefix "msgBootloaderHandshakeResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgBootloaderHandshakeResp_" . stripPrefix "_msgBootloaderHandshakeResp_"}
              ''MsgBootloaderHandshakeResp)
+$(makeLenses ''MsgBootloaderHandshakeResp)
 
 msgBootloaderJumpToApp :: Word16
 msgBootloaderJumpToApp = 0x00B1
@@ -84,20 +86,21 @@
 --
 -- The host initiates the bootloader to jump to the application.
 data MsgBootloaderJumpToApp = MsgBootloaderJumpToApp
-  { msgBootloaderJumpToApp_jump :: Word8
+  { _msgBootloaderJumpToApp_jump :: Word8
     -- ^ Ignored by the device
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgBootloaderJumpToApp where
   get = do
-    msgBootloaderJumpToApp_jump <- getWord8
+    _msgBootloaderJumpToApp_jump <- getWord8
     return MsgBootloaderJumpToApp {..}
 
   put MsgBootloaderJumpToApp {..} = do
-    putWord8 msgBootloaderJumpToApp_jump
+    putWord8 _msgBootloaderJumpToApp_jump
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgBootloaderJumpToApp_" . stripPrefix "msgBootloaderJumpToApp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgBootloaderJumpToApp_" . stripPrefix "_msgBootloaderJumpToApp_"}
              ''MsgBootloaderJumpToApp)
+$(makeLenses ''MsgBootloaderJumpToApp)
 
 msgNapDeviceDnaReq :: Word16
 msgNapDeviceDnaReq = 0x00DE
@@ -119,8 +122,9 @@
   put MsgNapDeviceDnaReq =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgNapDeviceDnaReq_" . stripPrefix "msgNapDeviceDnaReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgNapDeviceDnaReq_" . stripPrefix "_msgNapDeviceDnaReq_"}
              ''MsgNapDeviceDnaReq)
+$(makeLenses ''MsgNapDeviceDnaReq)
 
 msgNapDeviceDnaResp :: Word16
 msgNapDeviceDnaResp = 0x00DD
@@ -134,20 +138,21 @@
 -- that this ID is tied to the FPGA, and not related to the Piksi's serial
 -- number.
 data MsgNapDeviceDnaResp = MsgNapDeviceDnaResp
-  { msgNapDeviceDnaResp_dna :: [Word8]
+  { _msgNapDeviceDnaResp_dna :: [Word8]
     -- ^ 57-bit SwiftNAP FPGA Device ID. Remaining bits are padded on the right.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgNapDeviceDnaResp where
   get = do
-    msgNapDeviceDnaResp_dna <- replicateM 8 getWord8
+    _msgNapDeviceDnaResp_dna <- replicateM 8 getWord8
     return MsgNapDeviceDnaResp {..}
 
   put MsgNapDeviceDnaResp {..} = do
-    mapM_ putWord8 msgNapDeviceDnaResp_dna
+    mapM_ putWord8 _msgNapDeviceDnaResp_dna
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgNapDeviceDnaResp_" . stripPrefix "msgNapDeviceDnaResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgNapDeviceDnaResp_" . stripPrefix "_msgNapDeviceDnaResp_"}
              ''MsgNapDeviceDnaResp)
+$(makeLenses ''MsgNapDeviceDnaResp)
 
 msgBootloaderHandshakeDepA :: Word16
 msgBootloaderHandshakeDepA = 0x00B0
@@ -156,17 +161,18 @@
 --
 -- Deprecated.
 data MsgBootloaderHandshakeDepA = MsgBootloaderHandshakeDepA
-  { msgBootloaderHandshakeDepA_handshake :: [Word8]
+  { _msgBootloaderHandshakeDepA_handshake :: [Word8]
     -- ^ Version number string (not NULL terminated)
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgBootloaderHandshakeDepA where
   get = do
-    msgBootloaderHandshakeDepA_handshake <- whileM (liftM not isEmpty) getWord8
+    _msgBootloaderHandshakeDepA_handshake <- whileM (liftM not isEmpty) getWord8
     return MsgBootloaderHandshakeDepA {..}
 
   put MsgBootloaderHandshakeDepA {..} = do
-    mapM_ putWord8 msgBootloaderHandshakeDepA_handshake
+    mapM_ putWord8 _msgBootloaderHandshakeDepA_handshake
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgBootloaderHandshakeDepA_" . stripPrefix "msgBootloaderHandshakeDepA_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgBootloaderHandshakeDepA_" . stripPrefix "_msgBootloaderHandshakeDepA_"}
              ''MsgBootloaderHandshakeDepA)
+$(makeLenses ''MsgBootloaderHandshakeDepA)
diff --git a/src/SwiftNav/SBP/Encoding.hs b/src/SwiftNav/SBP/Encoding.hs
--- a/src/SwiftNav/SBP/Encoding.hs
+++ b/src/SwiftNav/SBP/Encoding.hs
@@ -11,11 +11,10 @@
 module SwiftNav.SBP.Encoding where
 
 import BasicPrelude
-import Control.Applicative
 import Data.Aeson
 import qualified Data.ByteString as B
 import Data.ByteString.Base64 as Base64
-import Data.Text.Encoding (encodeUtf8, decodeUtf8With)
+import Data.Text.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error
 
 -- ByteString doesn't have Aeson instances defined for it since
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
@@ -12,7 +12,7 @@
 module SwiftNav.SBP.ExtEvents where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -33,34 +33,35 @@
 -- Reports detection of an external event, the GPS time it occurred, which pin
 -- it was and whether it was rising or falling.
 data MsgExtEvent = MsgExtEvent
-  { msgExtEvent_wn   :: Word16
+  { _msgExtEvent_wn  :: Word16
     -- ^ GPS week number
-  , msgExtEvent_tow  :: Word32
+  , _msgExtEvent_tow :: Word32
     -- ^ GPS time of week rounded to the nearest millisecond
-  , msgExtEvent_ns   :: Int32
+  , _msgExtEvent_ns  :: Int32
     -- ^ Nanosecond residual of millisecond-rounded TOW (ranges from -500000 to
     -- 500000)
-  , msgExtEvent_flags :: Word8
+  , _msgExtEvent_flags :: Word8
     -- ^ Flags
-  , msgExtEvent_pin  :: Word8
+  , _msgExtEvent_pin :: Word8
     -- ^ Pin number.  0..9 = DEBUG0..9.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgExtEvent where
   get = do
-    msgExtEvent_wn <- getWord16le
-    msgExtEvent_tow <- getWord32le
-    msgExtEvent_ns <- liftM fromIntegral getWord32le
-    msgExtEvent_flags <- getWord8
-    msgExtEvent_pin <- getWord8
+    _msgExtEvent_wn <- getWord16le
+    _msgExtEvent_tow <- getWord32le
+    _msgExtEvent_ns <- liftM fromIntegral getWord32le
+    _msgExtEvent_flags <- getWord8
+    _msgExtEvent_pin <- getWord8
     return MsgExtEvent {..}
 
   put MsgExtEvent {..} = do
-    putWord16le msgExtEvent_wn
-    putWord32le msgExtEvent_tow
-    putWord32le $ fromIntegral msgExtEvent_ns
-    putWord8 msgExtEvent_flags
-    putWord8 msgExtEvent_pin
+    putWord16le _msgExtEvent_wn
+    putWord32le _msgExtEvent_tow
+    putWord32le $ fromIntegral _msgExtEvent_ns
+    putWord8 _msgExtEvent_flags
+    putWord8 _msgExtEvent_pin
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgExtEvent_" . stripPrefix "msgExtEvent_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgExtEvent_" . stripPrefix "_msgExtEvent_"}
              ''MsgExtEvent)
+$(makeLenses ''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
@@ -18,7 +18,7 @@
 module SwiftNav.SBP.FileIo where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -41,32 +41,33 @@
 -- where the message length field indicates how many bytes were succesfully
 -- read.The sequence number in the request will be returned in the response.
 data MsgFileioReadReq = MsgFileioReadReq
-  { msgFileioReadReq_sequence  :: Word32
+  { _msgFileioReadReq_sequence :: Word32
     -- ^ Read sequence number
-  , msgFileioReadReq_offset    :: Word32
+  , _msgFileioReadReq_offset   :: Word32
     -- ^ File offset
-  , msgFileioReadReq_chunk_size :: Word8
+  , _msgFileioReadReq_chunk_size :: Word8
     -- ^ Chunk size to read
-  , msgFileioReadReq_filename  :: ByteString
+  , _msgFileioReadReq_filename :: ByteString
     -- ^ Name of the file to read from
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFileioReadReq where
   get = do
-    msgFileioReadReq_sequence <- getWord32le
-    msgFileioReadReq_offset <- getWord32le
-    msgFileioReadReq_chunk_size <- getWord8
-    msgFileioReadReq_filename <- liftM toStrict getRemainingLazyByteString
+    _msgFileioReadReq_sequence <- getWord32le
+    _msgFileioReadReq_offset <- getWord32le
+    _msgFileioReadReq_chunk_size <- getWord8
+    _msgFileioReadReq_filename <- liftM toStrict getRemainingLazyByteString
     return MsgFileioReadReq {..}
 
   put MsgFileioReadReq {..} = do
-    putWord32le msgFileioReadReq_sequence
-    putWord32le msgFileioReadReq_offset
-    putWord8 msgFileioReadReq_chunk_size
-    putByteString msgFileioReadReq_filename
+    putWord32le _msgFileioReadReq_sequence
+    putWord32le _msgFileioReadReq_offset
+    putWord8 _msgFileioReadReq_chunk_size
+    putByteString _msgFileioReadReq_filename
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFileioReadReq_" . stripPrefix "msgFileioReadReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFileioReadReq_" . stripPrefix "_msgFileioReadReq_"}
              ''MsgFileioReadReq)
+$(makeLenses ''MsgFileioReadReq)
 
 msgFileioReadResp :: Word16
 msgFileioReadResp = 0x00A3
@@ -78,24 +79,25 @@
 -- length field indicates how many bytes were succesfully read. The sequence
 -- number in the response is preserved from the request.
 data MsgFileioReadResp = MsgFileioReadResp
-  { msgFileioReadResp_sequence :: Word32
+  { _msgFileioReadResp_sequence :: Word32
     -- ^ Read sequence number
-  , msgFileioReadResp_contents :: [Word8]
+  , _msgFileioReadResp_contents :: [Word8]
     -- ^ Contents of read file
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFileioReadResp where
   get = do
-    msgFileioReadResp_sequence <- getWord32le
-    msgFileioReadResp_contents <- whileM (liftM not isEmpty) getWord8
+    _msgFileioReadResp_sequence <- getWord32le
+    _msgFileioReadResp_contents <- whileM (liftM not isEmpty) getWord8
     return MsgFileioReadResp {..}
 
   put MsgFileioReadResp {..} = do
-    putWord32le msgFileioReadResp_sequence
-    mapM_ putWord8 msgFileioReadResp_contents
+    putWord32le _msgFileioReadResp_sequence
+    mapM_ putWord8 _msgFileioReadResp_contents
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFileioReadResp_" . stripPrefix "msgFileioReadResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFileioReadResp_" . stripPrefix "_msgFileioReadResp_"}
              ''MsgFileioReadResp)
+$(makeLenses ''MsgFileioReadResp)
 
 msgFileioReadDirReq :: Word16
 msgFileioReadDirReq = 0x00A9
@@ -109,28 +111,29 @@
 -- listing is chunked over multiple SBP packets. The sequence number in the
 -- request will be returned in the response.
 data MsgFileioReadDirReq = MsgFileioReadDirReq
-  { msgFileioReadDirReq_sequence :: Word32
+  { _msgFileioReadDirReq_sequence :: Word32
     -- ^ Read sequence number
-  , msgFileioReadDirReq_offset  :: Word32
+  , _msgFileioReadDirReq_offset :: Word32
     -- ^ The offset to skip the first n elements of the file list
-  , msgFileioReadDirReq_dirname :: ByteString
+  , _msgFileioReadDirReq_dirname :: ByteString
     -- ^ Name of the directory to list
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFileioReadDirReq where
   get = do
-    msgFileioReadDirReq_sequence <- getWord32le
-    msgFileioReadDirReq_offset <- getWord32le
-    msgFileioReadDirReq_dirname <- liftM toStrict getRemainingLazyByteString
+    _msgFileioReadDirReq_sequence <- getWord32le
+    _msgFileioReadDirReq_offset <- getWord32le
+    _msgFileioReadDirReq_dirname <- liftM toStrict getRemainingLazyByteString
     return MsgFileioReadDirReq {..}
 
   put MsgFileioReadDirReq {..} = do
-    putWord32le msgFileioReadDirReq_sequence
-    putWord32le msgFileioReadDirReq_offset
-    putByteString msgFileioReadDirReq_dirname
+    putWord32le _msgFileioReadDirReq_sequence
+    putWord32le _msgFileioReadDirReq_offset
+    putByteString _msgFileioReadDirReq_dirname
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFileioReadDirReq_" . stripPrefix "msgFileioReadDirReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFileioReadDirReq_" . stripPrefix "_msgFileioReadDirReq_"}
              ''MsgFileioReadDirReq)
+$(makeLenses ''MsgFileioReadDirReq)
 
 msgFileioReadDirResp :: Word16
 msgFileioReadDirResp = 0x00AA
@@ -143,24 +146,25 @@
 -- of the list is identified by an entry containing just the character 0xFF.
 -- The sequence number in the response is preserved from the request.
 data MsgFileioReadDirResp = MsgFileioReadDirResp
-  { msgFileioReadDirResp_sequence :: Word32
+  { _msgFileioReadDirResp_sequence :: Word32
     -- ^ Read sequence number
-  , msgFileioReadDirResp_contents :: [Word8]
+  , _msgFileioReadDirResp_contents :: [Word8]
     -- ^ Contents of read directory
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFileioReadDirResp where
   get = do
-    msgFileioReadDirResp_sequence <- getWord32le
-    msgFileioReadDirResp_contents <- whileM (liftM not isEmpty) getWord8
+    _msgFileioReadDirResp_sequence <- getWord32le
+    _msgFileioReadDirResp_contents <- whileM (liftM not isEmpty) getWord8
     return MsgFileioReadDirResp {..}
 
   put MsgFileioReadDirResp {..} = do
-    putWord32le msgFileioReadDirResp_sequence
-    mapM_ putWord8 msgFileioReadDirResp_contents
+    putWord32le _msgFileioReadDirResp_sequence
+    mapM_ putWord8 _msgFileioReadDirResp_contents
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFileioReadDirResp_" . stripPrefix "msgFileioReadDirResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFileioReadDirResp_" . stripPrefix "_msgFileioReadDirResp_"}
              ''MsgFileioReadDirResp)
+$(makeLenses ''MsgFileioReadDirResp)
 
 msgFileioRemove :: Word16
 msgFileioRemove = 0x00AC
@@ -169,20 +173,21 @@
 --
 -- The file remove message deletes a file from the file system.
 data MsgFileioRemove = MsgFileioRemove
-  { msgFileioRemove_filename :: ByteString
+  { _msgFileioRemove_filename :: ByteString
     -- ^ Name of the file to delete
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFileioRemove where
   get = do
-    msgFileioRemove_filename <- liftM toStrict getRemainingLazyByteString
+    _msgFileioRemove_filename <- liftM toStrict getRemainingLazyByteString
     return MsgFileioRemove {..}
 
   put MsgFileioRemove {..} = do
-    putByteString msgFileioRemove_filename
+    putByteString _msgFileioRemove_filename
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFileioRemove_" . stripPrefix "msgFileioRemove_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFileioRemove_" . stripPrefix "_msgFileioRemove_"}
              ''MsgFileioRemove)
+$(makeLenses ''MsgFileioRemove)
 
 msgFileioWriteReq :: Word16
 msgFileioWriteReq = 0x00AD
@@ -194,32 +199,33 @@
 -- MSG_FILEIO_WRITE_RESP message to check integrity of the write. The sequence
 -- number in the request will be returned in the response.
 data MsgFileioWriteReq = MsgFileioWriteReq
-  { msgFileioWriteReq_sequence :: Word32
+  { _msgFileioWriteReq_sequence :: Word32
     -- ^ Write sequence number
-  , msgFileioWriteReq_offset  :: Word32
+  , _msgFileioWriteReq_offset :: Word32
     -- ^ Offset into the file at which to start writing in bytes
-  , msgFileioWriteReq_filename :: ByteString
+  , _msgFileioWriteReq_filename :: ByteString
     -- ^ Name of the file to write to
-  , msgFileioWriteReq_data    :: [Word8]
+  , _msgFileioWriteReq_data   :: [Word8]
     -- ^ Variable-length array of data to write
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFileioWriteReq where
   get = do
-    msgFileioWriteReq_sequence <- getWord32le
-    msgFileioWriteReq_offset <- getWord32le
-    msgFileioWriteReq_filename <- liftM toStrict getRemainingLazyByteString
-    msgFileioWriteReq_data <- whileM (liftM not isEmpty) getWord8
+    _msgFileioWriteReq_sequence <- getWord32le
+    _msgFileioWriteReq_offset <- getWord32le
+    _msgFileioWriteReq_filename <- liftM toStrict getRemainingLazyByteString
+    _msgFileioWriteReq_data <- whileM (liftM not isEmpty) getWord8
     return MsgFileioWriteReq {..}
 
   put MsgFileioWriteReq {..} = do
-    putWord32le msgFileioWriteReq_sequence
-    putWord32le msgFileioWriteReq_offset
-    putByteString msgFileioWriteReq_filename
-    mapM_ putWord8 msgFileioWriteReq_data
+    putWord32le _msgFileioWriteReq_sequence
+    putWord32le _msgFileioWriteReq_offset
+    putByteString _msgFileioWriteReq_filename
+    mapM_ putWord8 _msgFileioWriteReq_data
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFileioWriteReq_" . stripPrefix "msgFileioWriteReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFileioWriteReq_" . stripPrefix "_msgFileioWriteReq_"}
              ''MsgFileioWriteReq)
+$(makeLenses ''MsgFileioWriteReq)
 
 msgFileioWriteResp :: Word16
 msgFileioWriteResp = 0x00AB
@@ -231,17 +237,18 @@
 -- MSG_FILEIO_WRITE_REQ message to check integrity of the write. The sequence
 -- number in the response is preserved from the request.
 data MsgFileioWriteResp = MsgFileioWriteResp
-  { msgFileioWriteResp_sequence :: Word32
+  { _msgFileioWriteResp_sequence :: Word32
     -- ^ Write sequence number
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFileioWriteResp where
   get = do
-    msgFileioWriteResp_sequence <- getWord32le
+    _msgFileioWriteResp_sequence <- getWord32le
     return MsgFileioWriteResp {..}
 
   put MsgFileioWriteResp {..} = do
-    putWord32le msgFileioWriteResp_sequence
+    putWord32le _msgFileioWriteResp_sequence
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFileioWriteResp_" . stripPrefix "msgFileioWriteResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFileioWriteResp_" . stripPrefix "_msgFileioWriteResp_"}
              ''MsgFileioWriteResp)
+$(makeLenses ''MsgFileioWriteResp)
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
@@ -15,7 +15,7 @@
 module SwiftNav.SBP.Flash where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -39,32 +39,33 @@
 -- 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
+  { _msgFlashProgram_target   :: Word8
     -- ^ Target flags
-  , msgFlashProgram_addr_start :: [Word8]
+  , _msgFlashProgram_addr_start :: [Word8]
     -- ^ Starting address offset to program
-  , msgFlashProgram_addr_len  :: Word8
+  , _msgFlashProgram_addr_len :: Word8
     -- ^ Length of set of addresses to program, counting up from starting address
-  , msgFlashProgram_data      :: [Word8]
+  , _msgFlashProgram_data     :: [Word8]
     -- ^ Data to program addresses with, with length N=addr_len
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFlashProgram where
   get = do
-    msgFlashProgram_target <- getWord8
-    msgFlashProgram_addr_start <- replicateM 3 getWord8
-    msgFlashProgram_addr_len <- getWord8
-    msgFlashProgram_data <- whileM (liftM not isEmpty) getWord8
+    _msgFlashProgram_target <- getWord8
+    _msgFlashProgram_addr_start <- replicateM 3 getWord8
+    _msgFlashProgram_addr_len <- getWord8
+    _msgFlashProgram_data <- whileM (liftM not isEmpty) getWord8
     return MsgFlashProgram {..}
 
   put MsgFlashProgram {..} = do
-    putWord8 msgFlashProgram_target
-    mapM_ putWord8 msgFlashProgram_addr_start
-    putWord8 msgFlashProgram_addr_len
-    mapM_ putWord8 msgFlashProgram_data
+    putWord8 _msgFlashProgram_target
+    mapM_ putWord8 _msgFlashProgram_addr_start
+    putWord8 _msgFlashProgram_addr_len
+    mapM_ putWord8 _msgFlashProgram_data
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFlashProgram_" . stripPrefix "msgFlashProgram_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFlashProgram_" . stripPrefix "_msgFlashProgram_"}
              ''MsgFlashProgram)
+$(makeLenses ''MsgFlashProgram)
 
 msgFlashDone :: Word16
 msgFlashDone = 0x00E0
@@ -76,20 +77,21 @@
 -- MSG_FLASH_READ_REQ, or MSG_FLASH_PROGRAM, may return this message on
 -- failure.
 data MsgFlashDone = MsgFlashDone
-  { msgFlashDone_response :: Word8
+  { _msgFlashDone_response :: Word8
     -- ^ Response flags
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFlashDone where
   get = do
-    msgFlashDone_response <- getWord8
+    _msgFlashDone_response <- getWord8
     return MsgFlashDone {..}
 
   put MsgFlashDone {..} = do
-    putWord8 msgFlashDone_response
+    putWord8 _msgFlashDone_response
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFlashDone_" . stripPrefix "msgFlashDone_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFlashDone_" . stripPrefix "_msgFlashDone_"}
              ''MsgFlashDone)
+$(makeLenses ''MsgFlashDone)
 
 msgFlashReadReq :: Word16
 msgFlashReadReq = 0x00E7
@@ -103,28 +105,29 @@
 -- exceeded or FLASH_INVALID_ADDR (3) if the address is outside of the allowed
 -- range.
 data MsgFlashReadReq = MsgFlashReadReq
-  { msgFlashReadReq_target    :: Word8
+  { _msgFlashReadReq_target   :: Word8
     -- ^ Target flags
-  , msgFlashReadReq_addr_start :: [Word8]
+  , _msgFlashReadReq_addr_start :: [Word8]
     -- ^ Starting address offset to read from
-  , msgFlashReadReq_addr_len  :: Word8
+  , _msgFlashReadReq_addr_len :: Word8
     -- ^ Length of set of addresses to read, counting up from starting address
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFlashReadReq where
   get = do
-    msgFlashReadReq_target <- getWord8
-    msgFlashReadReq_addr_start <- replicateM 3 getWord8
-    msgFlashReadReq_addr_len <- getWord8
+    _msgFlashReadReq_target <- getWord8
+    _msgFlashReadReq_addr_start <- replicateM 3 getWord8
+    _msgFlashReadReq_addr_len <- getWord8
     return MsgFlashReadReq {..}
 
   put MsgFlashReadReq {..} = do
-    putWord8 msgFlashReadReq_target
-    mapM_ putWord8 msgFlashReadReq_addr_start
-    putWord8 msgFlashReadReq_addr_len
+    putWord8 _msgFlashReadReq_target
+    mapM_ putWord8 _msgFlashReadReq_addr_start
+    putWord8 _msgFlashReadReq_addr_len
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFlashReadReq_" . stripPrefix "msgFlashReadReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFlashReadReq_" . stripPrefix "_msgFlashReadReq_"}
              ''MsgFlashReadReq)
+$(makeLenses ''MsgFlashReadReq)
 
 msgFlashReadResp :: Word16
 msgFlashReadResp = 0x00E1
@@ -138,28 +141,29 @@
 -- exceeded or FLASH_INVALID_ADDR (3) if the address is outside of the allowed
 -- range.
 data MsgFlashReadResp = MsgFlashReadResp
-  { msgFlashReadResp_target    :: Word8
+  { _msgFlashReadResp_target   :: Word8
     -- ^ Target flags
-  , msgFlashReadResp_addr_start :: [Word8]
+  , _msgFlashReadResp_addr_start :: [Word8]
     -- ^ Starting address offset to read from
-  , msgFlashReadResp_addr_len  :: Word8
+  , _msgFlashReadResp_addr_len :: Word8
     -- ^ Length of set of addresses to read, counting up from starting address
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFlashReadResp where
   get = do
-    msgFlashReadResp_target <- getWord8
-    msgFlashReadResp_addr_start <- replicateM 3 getWord8
-    msgFlashReadResp_addr_len <- getWord8
+    _msgFlashReadResp_target <- getWord8
+    _msgFlashReadResp_addr_start <- replicateM 3 getWord8
+    _msgFlashReadResp_addr_len <- getWord8
     return MsgFlashReadResp {..}
 
   put MsgFlashReadResp {..} = do
-    putWord8 msgFlashReadResp_target
-    mapM_ putWord8 msgFlashReadResp_addr_start
-    putWord8 msgFlashReadResp_addr_len
+    putWord8 _msgFlashReadResp_target
+    mapM_ putWord8 _msgFlashReadResp_addr_start
+    putWord8 _msgFlashReadResp_addr_len
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFlashReadResp_" . stripPrefix "msgFlashReadResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFlashReadResp_" . stripPrefix "_msgFlashReadResp_"}
              ''MsgFlashReadResp)
+$(makeLenses ''MsgFlashReadResp)
 
 msgFlashErase :: Word16
 msgFlashErase = 0x00E2
@@ -171,24 +175,25 @@
 -- message containing the return code - FLASH_OK (0) on success or
 -- FLASH_INVALID_FLASH (1) if the flash specified is invalid.
 data MsgFlashErase = MsgFlashErase
-  { msgFlashErase_target    :: Word8
+  { _msgFlashErase_target   :: Word8
     -- ^ Target flags
-  , msgFlashErase_sector_num :: Word32
+  , _msgFlashErase_sector_num :: Word32
     -- ^ Flash sector number to erase (0-11 for the STM, 0-15 for the M25)
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgFlashErase where
   get = do
-    msgFlashErase_target <- getWord8
-    msgFlashErase_sector_num <- getWord32le
+    _msgFlashErase_target <- getWord8
+    _msgFlashErase_sector_num <- getWord32le
     return MsgFlashErase {..}
 
   put MsgFlashErase {..} = do
-    putWord8 msgFlashErase_target
-    putWord32le msgFlashErase_sector_num
+    putWord8 _msgFlashErase_target
+    putWord32le _msgFlashErase_sector_num
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgFlashErase_" . stripPrefix "msgFlashErase_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgFlashErase_" . stripPrefix "_msgFlashErase_"}
              ''MsgFlashErase)
+$(makeLenses ''MsgFlashErase)
 
 msgStmFlashLockSector :: Word16
 msgStmFlashLockSector = 0x00E3
@@ -198,20 +203,21 @@
 -- The flash lock message locks a sector of the STM flash memory. The device
 -- replies with a MSG_FLASH_DONE message.
 data MsgStmFlashLockSector = MsgStmFlashLockSector
-  { msgStmFlashLockSector_sector :: Word32
+  { _msgStmFlashLockSector_sector :: Word32
     -- ^ Flash sector number to lock
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgStmFlashLockSector where
   get = do
-    msgStmFlashLockSector_sector <- getWord32le
+    _msgStmFlashLockSector_sector <- getWord32le
     return MsgStmFlashLockSector {..}
 
   put MsgStmFlashLockSector {..} = do
-    putWord32le msgStmFlashLockSector_sector
+    putWord32le _msgStmFlashLockSector_sector
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgStmFlashLockSector_" . stripPrefix "msgStmFlashLockSector_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgStmFlashLockSector_" . stripPrefix "_msgStmFlashLockSector_"}
              ''MsgStmFlashLockSector)
+$(makeLenses ''MsgStmFlashLockSector)
 
 msgStmFlashUnlockSector :: Word16
 msgStmFlashUnlockSector = 0x00E4
@@ -221,20 +227,21 @@
 -- The flash unlock message unlocks a sector of the STM flash memory. The
 -- device replies with a MSG_FLASH_DONE message.
 data MsgStmFlashUnlockSector = MsgStmFlashUnlockSector
-  { msgStmFlashUnlockSector_sector :: Word32
+  { _msgStmFlashUnlockSector_sector :: Word32
     -- ^ Flash sector number to unlock
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgStmFlashUnlockSector where
   get = do
-    msgStmFlashUnlockSector_sector <- getWord32le
+    _msgStmFlashUnlockSector_sector <- getWord32le
     return MsgStmFlashUnlockSector {..}
 
   put MsgStmFlashUnlockSector {..} = do
-    putWord32le msgStmFlashUnlockSector_sector
+    putWord32le _msgStmFlashUnlockSector_sector
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgStmFlashUnlockSector_" . stripPrefix "msgStmFlashUnlockSector_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgStmFlashUnlockSector_" . stripPrefix "_msgStmFlashUnlockSector_"}
              ''MsgStmFlashUnlockSector)
+$(makeLenses ''MsgStmFlashUnlockSector)
 
 msgStmUniqueIdReq :: Word16
 msgStmUniqueIdReq = 0x00E8
@@ -254,8 +261,9 @@
   put MsgStmUniqueIdReq =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgStmUniqueIdReq_" . stripPrefix "msgStmUniqueIdReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgStmUniqueIdReq_" . stripPrefix "_msgStmUniqueIdReq_"}
              ''MsgStmUniqueIdReq)
+$(makeLenses ''MsgStmUniqueIdReq)
 
 msgStmUniqueIdResp :: Word16
 msgStmUniqueIdResp = 0x00E5
@@ -266,20 +274,21 @@
 -- 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..
 data MsgStmUniqueIdResp = MsgStmUniqueIdResp
-  { msgStmUniqueIdResp_stm_id :: [Word8]
+  { _msgStmUniqueIdResp_stm_id :: [Word8]
     -- ^ Device unique ID
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgStmUniqueIdResp where
   get = do
-    msgStmUniqueIdResp_stm_id <- replicateM 12 getWord8
+    _msgStmUniqueIdResp_stm_id <- replicateM 12 getWord8
     return MsgStmUniqueIdResp {..}
 
   put MsgStmUniqueIdResp {..} = do
-    mapM_ putWord8 msgStmUniqueIdResp_stm_id
+    mapM_ putWord8 _msgStmUniqueIdResp_stm_id
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgStmUniqueIdResp_" . stripPrefix "msgStmUniqueIdResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgStmUniqueIdResp_" . stripPrefix "_msgStmUniqueIdResp_"}
              ''MsgStmUniqueIdResp)
+$(makeLenses ''MsgStmUniqueIdResp)
 
 msgM25FlashWriteStatus :: Word16
 msgM25FlashWriteStatus = 0x00F3
@@ -289,17 +298,18 @@
 -- The flash status message writes to the 8-bit M25 flash status register. The
 -- device replies with a MSG_FLASH_DONE message.
 data MsgM25FlashWriteStatus = MsgM25FlashWriteStatus
-  { msgM25FlashWriteStatus_status :: [Word8]
+  { _msgM25FlashWriteStatus_status :: [Word8]
     -- ^ Byte to write to the M25 flash status register
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgM25FlashWriteStatus where
   get = do
-    msgM25FlashWriteStatus_status <- replicateM 1 getWord8
+    _msgM25FlashWriteStatus_status <- replicateM 1 getWord8
     return MsgM25FlashWriteStatus {..}
 
   put MsgM25FlashWriteStatus {..} = do
-    mapM_ putWord8 msgM25FlashWriteStatus_status
+    mapM_ putWord8 _msgM25FlashWriteStatus_status
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgM25FlashWriteStatus_" . stripPrefix "msgM25FlashWriteStatus_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgM25FlashWriteStatus_" . stripPrefix "_msgM25FlashWriteStatus_"}
              ''MsgM25FlashWriteStatus)
+$(makeLenses ''MsgM25FlashWriteStatus)
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
@@ -12,7 +12,7 @@
 module SwiftNav.SBP.Logging where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -34,24 +34,25 @@
 -- containing errors, warnings and informational messages at ERROR, WARNING,
 -- DEBUG, INFO logging levels.
 data MsgLog = MsgLog
-  { msgLog_level :: Word8
+  { _msgLog_level :: Word8
     -- ^ Logging level
-  , msgLog_text :: ByteString
+  , _msgLog_text :: ByteString
     -- ^ Human-readable string
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgLog where
   get = do
-    msgLog_level <- getWord8
-    msgLog_text <- liftM toStrict getRemainingLazyByteString
+    _msgLog_level <- getWord8
+    _msgLog_text <- liftM toStrict getRemainingLazyByteString
     return MsgLog {..}
 
   put MsgLog {..} = do
-    putWord8 msgLog_level
-    putByteString msgLog_text
+    putWord8 _msgLog_level
+    putByteString _msgLog_text
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgLog_" . stripPrefix "msgLog_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgLog_" . stripPrefix "_msgLog_"}
              ''MsgLog)
+$(makeLenses ''MsgLog)
 
 msgTweet :: Word16
 msgTweet = 0x0012
@@ -60,20 +61,21 @@
 --
 -- All the news fit to tweet.
 data MsgTweet = MsgTweet
-  { msgTweet_tweet :: ByteString
+  { _msgTweet_tweet :: ByteString
     -- ^ Human-readable string
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgTweet where
   get = do
-    msgTweet_tweet <- getByteString 140
+    _msgTweet_tweet <- getByteString 140
     return MsgTweet {..}
 
   put MsgTweet {..} = do
-    putByteString msgTweet_tweet
+    putByteString _msgTweet_tweet
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgTweet_" . stripPrefix "msgTweet_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgTweet_" . stripPrefix "_msgTweet_"}
              ''MsgTweet)
+$(makeLenses ''MsgTweet)
 
 msgPrintDep :: Word16
 msgPrintDep = 0x0010
@@ -82,17 +84,18 @@
 --
 -- Deprecated.
 data MsgPrintDep = MsgPrintDep
-  { msgPrintDep_text :: ByteString
+  { _msgPrintDep_text :: ByteString
     -- ^ Human-readable string
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgPrintDep where
   get = do
-    msgPrintDep_text <- liftM toStrict getRemainingLazyByteString
+    _msgPrintDep_text <- liftM toStrict getRemainingLazyByteString
     return MsgPrintDep {..}
 
   put MsgPrintDep {..} = do
-    putByteString msgPrintDep_text
+    putByteString _msgPrintDep_text
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgPrintDep_" . stripPrefix "msgPrintDep_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgPrintDep_" . stripPrefix "_msgPrintDep_"}
              ''MsgPrintDep)
+$(makeLenses ''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
@@ -19,7 +19,7 @@
 module SwiftNav.SBP.Navigation where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -47,33 +47,34 @@
 -- 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
+  { _msgGpsTime_wn  :: Word16
     -- ^ GPS week number
-  , msgGpsTime_tow  :: Word32
+  , _msgGpsTime_tow :: Word32
     -- ^ GPS time of week rounded to the nearest millisecond
-  , msgGpsTime_ns   :: Int32
+  , _msgGpsTime_ns  :: Int32
     -- ^ Nanosecond residual of millisecond-rounded TOW (ranges from -500000 to
     -- 500000)
-  , msgGpsTime_flags :: Word8
+  , _msgGpsTime_flags :: Word8
     -- ^ Status flags (reserved)
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgGpsTime where
   get = do
-    msgGpsTime_wn <- getWord16le
-    msgGpsTime_tow <- getWord32le
-    msgGpsTime_ns <- liftM fromIntegral getWord32le
-    msgGpsTime_flags <- getWord8
+    _msgGpsTime_wn <- getWord16le
+    _msgGpsTime_tow <- getWord32le
+    _msgGpsTime_ns <- liftM fromIntegral getWord32le
+    _msgGpsTime_flags <- getWord8
     return MsgGpsTime {..}
 
   put MsgGpsTime {..} = do
-    putWord16le msgGpsTime_wn
-    putWord32le msgGpsTime_tow
-    putWord32le $ fromIntegral msgGpsTime_ns
-    putWord8 msgGpsTime_flags
+    putWord16le _msgGpsTime_wn
+    putWord32le _msgGpsTime_tow
+    putWord32le $ fromIntegral _msgGpsTime_ns
+    putWord8 _msgGpsTime_flags
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgGpsTime_" . stripPrefix "msgGpsTime_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgGpsTime_" . stripPrefix "_msgGpsTime_"}
              ''MsgGpsTime)
+$(makeLenses ''MsgGpsTime)
 
 msgDops :: Word16
 msgDops = 0x0206
@@ -83,40 +84,41 @@
 -- This dilution of precision (DOP) message describes the effect of navigation
 -- satellite geometry on positional measurement precision.
 data MsgDops = MsgDops
-  { msgDops_tow :: Word32
+  { _msgDops_tow :: Word32
     -- ^ GPS Time of Week
-  , msgDops_gdop :: Word16
+  , _msgDops_gdop :: Word16
     -- ^ Geometric Dilution of Precision
-  , msgDops_pdop :: Word16
+  , _msgDops_pdop :: Word16
     -- ^ Position Dilution of Precision
-  , msgDops_tdop :: Word16
+  , _msgDops_tdop :: Word16
     -- ^ Time Dilution of Precision
-  , msgDops_hdop :: Word16
+  , _msgDops_hdop :: Word16
     -- ^ Horizontal Dilution of Precision
-  , msgDops_vdop :: Word16
+  , _msgDops_vdop :: Word16
     -- ^ Vertical Dilution of Precision
   } 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_tow <- getWord32le
+    _msgDops_gdop <- getWord16le
+    _msgDops_pdop <- getWord16le
+    _msgDops_tdop <- getWord16le
+    _msgDops_hdop <- getWord16le
+    _msgDops_vdop <- getWord16le
     return MsgDops {..}
 
   put MsgDops {..} = do
-    putWord32le msgDops_tow
-    putWord16le msgDops_gdop
-    putWord16le msgDops_pdop
-    putWord16le msgDops_tdop
-    putWord16le msgDops_hdop
-    putWord16le msgDops_vdop
+    putWord32le _msgDops_tow
+    putWord16le _msgDops_gdop
+    putWord16le _msgDops_pdop
+    putWord16le _msgDops_tdop
+    putWord16le _msgDops_hdop
+    putWord16le _msgDops_vdop
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgDops_" . stripPrefix "msgDops_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgDops_" . stripPrefix "_msgDops_"}
              ''MsgDops)
+$(makeLenses ''MsgDops)
 
 msgPosEcef :: Word16
 msgPosEcef = 0x0200
@@ -131,44 +133,45 @@
 -- 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
+  { _msgPosEcef_tow    :: Word32
     -- ^ GPS Time of Week
-  , msgPosEcef_x       :: Double
+  , _msgPosEcef_x      :: Double
     -- ^ ECEF X coordinate
-  , msgPosEcef_y       :: Double
+  , _msgPosEcef_y      :: Double
     -- ^ ECEF Y coordinate
-  , msgPosEcef_z       :: Double
+  , _msgPosEcef_z      :: Double
     -- ^ ECEF Z coordinate
-  , msgPosEcef_accuracy :: Word16
+  , _msgPosEcef_accuracy :: Word16
     -- ^ Position accuracy estimate (not implemented). Defaults to 0.
-  , msgPosEcef_n_sats  :: Word8
+  , _msgPosEcef_n_sats :: Word8
     -- ^ Number of satellites used in solution
-  , msgPosEcef_flags   :: Word8
+  , _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
+    _msgPosEcef_tow <- getWord32le
+    _msgPosEcef_x <- getFloat64le
+    _msgPosEcef_y <- getFloat64le
+    _msgPosEcef_z <- getFloat64le
+    _msgPosEcef_accuracy <- getWord16le
+    _msgPosEcef_n_sats <- getWord8
+    _msgPosEcef_flags <- getWord8
     return 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
+    putWord32le _msgPosEcef_tow
+    putFloat64le _msgPosEcef_x
+    putFloat64le _msgPosEcef_y
+    putFloat64le _msgPosEcef_z
+    putWord16le _msgPosEcef_accuracy
+    putWord8 _msgPosEcef_n_sats
+    putWord8 _msgPosEcef_flags
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgPosEcef_" . stripPrefix "msgPosEcef_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgPosEcef_" . stripPrefix "_msgPosEcef_"}
              ''MsgPosEcef)
+$(makeLenses ''MsgPosEcef)
 
 msgPosLlh :: Word16
 msgPosLlh = 0x0201
@@ -183,48 +186,49 @@
 -- time is given by the preceding MSG_GPS_TIME with the matching time-of-week
 -- (tow).
 data MsgPosLlh = MsgPosLlh
-  { msgPosLlh_tow       :: Word32
+  { _msgPosLlh_tow      :: Word32
     -- ^ GPS Time of Week
-  , msgPosLlh_lat       :: Double
+  , _msgPosLlh_lat      :: Double
     -- ^ Latitude
-  , msgPosLlh_lon       :: Double
+  , _msgPosLlh_lon      :: Double
     -- ^ Longitude
-  , msgPosLlh_height    :: Double
+  , _msgPosLlh_height   :: Double
     -- ^ Height
-  , msgPosLlh_h_accuracy :: Word16
+  , _msgPosLlh_h_accuracy :: Word16
     -- ^ Horizontal position accuracy estimate (not implemented). Defaults to 0.
-  , msgPosLlh_v_accuracy :: Word16
+  , _msgPosLlh_v_accuracy :: Word16
     -- ^ Vertical position accuracy estimate (not implemented). Defaults to 0.
-  , msgPosLlh_n_sats    :: Word8
+  , _msgPosLlh_n_sats   :: Word8
     -- ^ Number of satellites used in solution.
-  , msgPosLlh_flags     :: Word8
+  , _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
+    _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
     return 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
+    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
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgPosLlh_" . stripPrefix "msgPosLlh_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgPosLlh_" . stripPrefix "_msgPosLlh_"}
              ''MsgPosLlh)
+$(makeLenses ''MsgPosLlh)
 
 msgBaselineEcef :: Word16
 msgBaselineEcef = 0x0202
@@ -236,44 +240,45 @@
 -- 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
+  { _msgBaselineEcef_tow    :: Word32
     -- ^ GPS Time of Week
-  , msgBaselineEcef_x       :: Int32
+  , _msgBaselineEcef_x      :: Int32
     -- ^ Baseline ECEF X coordinate
-  , msgBaselineEcef_y       :: Int32
+  , _msgBaselineEcef_y      :: Int32
     -- ^ Baseline ECEF Y coordinate
-  , msgBaselineEcef_z       :: Int32
+  , _msgBaselineEcef_z      :: Int32
     -- ^ Baseline ECEF Z coordinate
-  , msgBaselineEcef_accuracy :: Word16
+  , _msgBaselineEcef_accuracy :: Word16
     -- ^ Position accuracy estimate (not implemented). Defaults to 0.
-  , msgBaselineEcef_n_sats  :: Word8
+  , _msgBaselineEcef_n_sats :: Word8
     -- ^ Number of satellites used in solution
-  , msgBaselineEcef_flags   :: Word8
+  , _msgBaselineEcef_flags  :: Word8
     -- ^ Status flags
   } deriving ( Show, Read, Eq )
 
 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_accuracy <- getWord16le
-    msgBaselineEcef_n_sats <- getWord8
-    msgBaselineEcef_flags <- getWord8
+    _msgBaselineEcef_tow <- getWord32le
+    _msgBaselineEcef_x <- liftM fromIntegral getWord32le
+    _msgBaselineEcef_y <- liftM fromIntegral getWord32le
+    _msgBaselineEcef_z <- liftM fromIntegral getWord32le
+    _msgBaselineEcef_accuracy <- getWord16le
+    _msgBaselineEcef_n_sats <- getWord8
+    _msgBaselineEcef_flags <- getWord8
     return 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
+    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
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgBaselineEcef_" . stripPrefix "msgBaselineEcef_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgBaselineEcef_" . stripPrefix "_msgBaselineEcef_"}
              ''MsgBaselineEcef)
+$(makeLenses ''MsgBaselineEcef)
 
 msgBaselineNed :: Word16
 msgBaselineNed = 0x0203
@@ -287,48 +292,49 @@
 -- time is given by the preceding MSG_GPS_TIME with the matching time-of-week
 -- (tow).
 data MsgBaselineNed = MsgBaselineNed
-  { msgBaselineNed_tow       :: Word32
+  { _msgBaselineNed_tow      :: Word32
     -- ^ GPS Time of Week
-  , msgBaselineNed_n         :: Int32
+  , _msgBaselineNed_n        :: Int32
     -- ^ Baseline North coordinate
-  , msgBaselineNed_e         :: Int32
+  , _msgBaselineNed_e        :: Int32
     -- ^ Baseline East coordinate
-  , msgBaselineNed_d         :: Int32
+  , _msgBaselineNed_d        :: Int32
     -- ^ Baseline Down coordinate
-  , msgBaselineNed_h_accuracy :: Word16
+  , _msgBaselineNed_h_accuracy :: Word16
     -- ^ Horizontal position accuracy estimate (not implemented). Defaults to 0.
-  , msgBaselineNed_v_accuracy :: Word16
+  , _msgBaselineNed_v_accuracy :: Word16
     -- ^ Vertical position accuracy estimate (not implemented). Defaults to 0.
-  , msgBaselineNed_n_sats    :: Word8
+  , _msgBaselineNed_n_sats   :: Word8
     -- ^ Number of satellites used in solution
-  , msgBaselineNed_flags     :: Word8
+  , _msgBaselineNed_flags    :: Word8
     -- ^ Status flags
   } deriving ( Show, Read, Eq )
 
 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_h_accuracy <- getWord16le
-    msgBaselineNed_v_accuracy <- getWord16le
-    msgBaselineNed_n_sats <- getWord8
-    msgBaselineNed_flags <- getWord8
+    _msgBaselineNed_tow <- getWord32le
+    _msgBaselineNed_n <- liftM fromIntegral getWord32le
+    _msgBaselineNed_e <- liftM fromIntegral getWord32le
+    _msgBaselineNed_d <- liftM fromIntegral getWord32le
+    _msgBaselineNed_h_accuracy <- getWord16le
+    _msgBaselineNed_v_accuracy <- getWord16le
+    _msgBaselineNed_n_sats <- getWord8
+    _msgBaselineNed_flags <- getWord8
     return 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
+    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
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgBaselineNed_" . stripPrefix "msgBaselineNed_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgBaselineNed_" . stripPrefix "_msgBaselineNed_"}
              ''MsgBaselineNed)
+$(makeLenses ''MsgBaselineNed)
 
 msgVelEcef :: Word16
 msgVelEcef = 0x0204
@@ -339,44 +345,45 @@
 -- 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
+  { _msgVelEcef_tow    :: Word32
     -- ^ GPS Time of Week
-  , msgVelEcef_x       :: Int32
+  , _msgVelEcef_x      :: Int32
     -- ^ Velocity ECEF X coordinate
-  , msgVelEcef_y       :: Int32
+  , _msgVelEcef_y      :: Int32
     -- ^ Velocity ECEF Y coordinate
-  , msgVelEcef_z       :: Int32
+  , _msgVelEcef_z      :: Int32
     -- ^ Velocity ECEF Z coordinate
-  , msgVelEcef_accuracy :: Word16
+  , _msgVelEcef_accuracy :: Word16
     -- ^ Velocity accuracy estimate (not implemented). Defaults to 0.
-  , msgVelEcef_n_sats  :: Word8
+  , _msgVelEcef_n_sats :: Word8
     -- ^ Number of satellites used in solution
-  , msgVelEcef_flags   :: Word8
+  , _msgVelEcef_flags  :: Word8
     -- ^ Status flags (reserved)
   } deriving ( Show, Read, Eq )
 
 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_accuracy <- getWord16le
-    msgVelEcef_n_sats <- getWord8
-    msgVelEcef_flags <- getWord8
+    _msgVelEcef_tow <- getWord32le
+    _msgVelEcef_x <- liftM fromIntegral getWord32le
+    _msgVelEcef_y <- liftM fromIntegral getWord32le
+    _msgVelEcef_z <- liftM fromIntegral getWord32le
+    _msgVelEcef_accuracy <- getWord16le
+    _msgVelEcef_n_sats <- getWord8
+    _msgVelEcef_flags <- getWord8
     return 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
+    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
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgVelEcef_" . stripPrefix "msgVelEcef_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgVelEcef_" . stripPrefix "_msgVelEcef_"}
              ''MsgVelEcef)
+$(makeLenses ''MsgVelEcef)
 
 msgVelNed :: Word16
 msgVelNed = 0x0205
@@ -387,45 +394,46 @@
 -- coordinates. 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
+  { _msgVelNed_tow      :: Word32
     -- ^ GPS Time of Week
-  , msgVelNed_n         :: Int32
+  , _msgVelNed_n        :: Int32
     -- ^ Velocity North coordinate
-  , msgVelNed_e         :: Int32
+  , _msgVelNed_e        :: Int32
     -- ^ Velocity East coordinate
-  , msgVelNed_d         :: Int32
+  , _msgVelNed_d        :: Int32
     -- ^ Velocity Down coordinate
-  , msgVelNed_h_accuracy :: Word16
+  , _msgVelNed_h_accuracy :: Word16
     -- ^ Horizontal velocity accuracy estimate (not implemented). Defaults to 0.
-  , msgVelNed_v_accuracy :: Word16
+  , _msgVelNed_v_accuracy :: Word16
     -- ^ Vertical velocity accuracy estimate (not implemented). Defaults to 0.
-  , msgVelNed_n_sats    :: Word8
+  , _msgVelNed_n_sats   :: Word8
     -- ^ Number of satellites used in solution
-  , msgVelNed_flags     :: Word8
+  , _msgVelNed_flags    :: Word8
     -- ^ Status flags (reserved)
   } deriving ( Show, Read, Eq )
 
 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_h_accuracy <- getWord16le
-    msgVelNed_v_accuracy <- getWord16le
-    msgVelNed_n_sats <- getWord8
-    msgVelNed_flags <- getWord8
+    _msgVelNed_tow <- getWord32le
+    _msgVelNed_n <- liftM fromIntegral getWord32le
+    _msgVelNed_e <- liftM fromIntegral getWord32le
+    _msgVelNed_d <- liftM fromIntegral getWord32le
+    _msgVelNed_h_accuracy <- getWord16le
+    _msgVelNed_v_accuracy <- getWord16le
+    _msgVelNed_n_sats <- getWord8
+    _msgVelNed_flags <- getWord8
     return 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
+    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
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgVelNed_" . stripPrefix "msgVelNed_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgVelNed_" . stripPrefix "_msgVelNed_"}
              ''MsgVelNed)
+$(makeLenses ''MsgVelNed)
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
@@ -11,7 +11,7 @@
 module SwiftNav.SBP.Observation where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -29,24 +29,25 @@
 -- 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
+  { _obsGPSTime_tow :: Word32
     -- ^ Milliseconds since start of GPS week
-  , obsGPSTime_wn :: Word16
+  , _obsGPSTime_wn :: Word16
     -- ^ GPS week number
   } deriving ( Show, Read, Eq )
 
 instance Binary ObsGPSTime where
   get = do
-    obsGPSTime_tow <- getWord32le
-    obsGPSTime_wn <- getWord16le
+    _obsGPSTime_tow <- getWord32le
+    _obsGPSTime_wn <- getWord16le
     return ObsGPSTime {..}
 
   put ObsGPSTime {..} = do
-    putWord32le obsGPSTime_tow
-    putWord16le obsGPSTime_wn
+    putWord32le _obsGPSTime_tow
+    putWord16le _obsGPSTime_wn
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "obsGPSTime_" . stripPrefix "obsGPSTime_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_obsGPSTime_" . stripPrefix "_obsGPSTime_"}
              ''ObsGPSTime)
+$(makeLenses ''ObsGPSTime)
 
 -- | CarrierPhase.
 --
@@ -54,64 +55,66 @@
 -- number with Q32.8 layout, i.e. 32-bits of whole cycles and 8-bits of
 -- fractional cycles.
 data CarrierPhase = CarrierPhase
-  { carrierPhase_i :: Int32
+  { _carrierPhase_i :: Int32
     -- ^ Carrier phase whole cycles
-  , carrierPhase_f :: Word8
+  , _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
+    _carrierPhase_i <- liftM fromIntegral getWord32le
+    _carrierPhase_f <- getWord8
     return CarrierPhase {..}
 
   put CarrierPhase {..} = do
-    putWord32le $ fromIntegral carrierPhase_i
-    putWord8 carrierPhase_f
+    putWord32le $ fromIntegral _carrierPhase_i
+    putWord8 _carrierPhase_f
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "carrierPhase_" . stripPrefix "carrierPhase_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_carrierPhase_" . stripPrefix "_carrierPhase_"}
              ''CarrierPhase)
+$(makeLenses ''CarrierPhase)
 
 -- | ObservationHeader.
 --
 -- Header of a GPS observation message.
 data ObservationHeader = ObservationHeader
-  { observationHeader_t    :: ObsGPSTime
+  { _observationHeader_t   :: ObsGPSTime
     -- ^ GPS time of this observation
-  , observationHeader_n_obs :: Word8
+  , _observationHeader_n_obs :: Word8
     -- ^ Total number of observations. First nibble is the size of the sequence
     -- (n), second nibble is the zero-indexed counter (ith packet of n)
   } deriving ( Show, Read, Eq )
 
 instance Binary ObservationHeader where
   get = do
-    observationHeader_t <- get
-    observationHeader_n_obs <- getWord8
+    _observationHeader_t <- get
+    _observationHeader_n_obs <- getWord8
     return ObservationHeader {..}
 
   put ObservationHeader {..} = do
-    put observationHeader_t
-    putWord8 observationHeader_n_obs
+    put _observationHeader_t
+    putWord8 _observationHeader_n_obs
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "observationHeader_" . stripPrefix "observationHeader_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_observationHeader_" . stripPrefix "_observationHeader_"}
              ''ObservationHeader)
+$(makeLenses ''ObservationHeader)
 
 -- | PackedObsContent.
 --
 -- Pseudorange and carrier phase observation for a satellite being tracked.
 data PackedObsContent = PackedObsContent
-  { packedObsContent_P   :: Word32
+  { _packedObsContent_P  :: Word32
     -- ^ Pseudorange observation
-  , packedObsContent_L   :: CarrierPhase
+  , _packedObsContent_L  :: CarrierPhase
     -- ^ Carrier phase observation
-  , packedObsContent_cn0 :: Word8
+  , _packedObsContent_cn0 :: Word8
     -- ^ Carrier-to-Noise density
-  , packedObsContent_lock :: Word16
+  , _packedObsContent_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.
-  , packedObsContent_sid :: Word32
+  , _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.
@@ -119,22 +122,23 @@
 
 instance Binary PackedObsContent where
   get = do
-    packedObsContent_P <- getWord32le
-    packedObsContent_L <- get
-    packedObsContent_cn0 <- getWord8
-    packedObsContent_lock <- getWord16le
-    packedObsContent_sid <- getWord32le
+    _packedObsContent_P <- getWord32le
+    _packedObsContent_L <- get
+    _packedObsContent_cn0 <- getWord8
+    _packedObsContent_lock <- getWord16le
+    _packedObsContent_sid <- getWord32le
     return PackedObsContent {..}
 
   put PackedObsContent {..} = do
-    putWord32le packedObsContent_P
-    put packedObsContent_L
-    putWord8 packedObsContent_cn0
-    putWord16le packedObsContent_lock
-    putWord32le packedObsContent_sid
+    putWord32le _packedObsContent_P
+    put _packedObsContent_L
+    putWord8 _packedObsContent_cn0
+    putWord16le _packedObsContent_lock
+    putWord32le _packedObsContent_sid
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "packedObsContent_" . stripPrefix "packedObsContent_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_packedObsContent_" . stripPrefix "_packedObsContent_"}
              ''PackedObsContent)
+$(makeLenses ''PackedObsContent)
 
 msgObs :: Word16
 msgObs = 0x0043
@@ -146,24 +150,25 @@
 -- 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).
 data MsgObs = MsgObs
-  { msgObs_header :: ObservationHeader
+  { _msgObs_header :: ObservationHeader
     -- ^ Header of a GPS observation message
-  , msgObs_obs   :: [PackedObsContent]
+  , _msgObs_obs  :: [PackedObsContent]
     -- ^ Pseudorange and carrier phase observation for a satellite being tracked.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgObs where
   get = do
-    msgObs_header <- get
-    msgObs_obs <- whileM (liftM not isEmpty) get
+    _msgObs_header <- get
+    _msgObs_obs <- whileM (liftM not isEmpty) get
     return MsgObs {..}
 
   put MsgObs {..} = do
-    put msgObs_header
-    mapM_ put msgObs_obs
+    put _msgObs_header
+    mapM_ put _msgObs_obs
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgObs_" . stripPrefix "msgObs_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgObs_" . stripPrefix "_msgObs_"}
              ''MsgObs)
+$(makeLenses ''MsgObs)
 
 msgBasePos :: Word16
 msgBasePos = 0x0044
@@ -175,28 +180,29 @@
 -- required to be a high-accuracy surveyed location of the base station. Any
 -- error here will result in an error in the pseudo-absolute position output.
 data MsgBasePos = MsgBasePos
-  { msgBasePos_lat   :: Double
+  { _msgBasePos_lat  :: Double
     -- ^ Latitude
-  , msgBasePos_lon   :: Double
+  , _msgBasePos_lon  :: Double
     -- ^ Longitude
-  , msgBasePos_height :: Double
+  , _msgBasePos_height :: Double
     -- ^ Height
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgBasePos where
   get = do
-    msgBasePos_lat <- getFloat64le
-    msgBasePos_lon <- getFloat64le
-    msgBasePos_height <- getFloat64le
+    _msgBasePos_lat <- getFloat64le
+    _msgBasePos_lon <- getFloat64le
+    _msgBasePos_height <- getFloat64le
     return MsgBasePos {..}
 
   put MsgBasePos {..} = do
-    putFloat64le msgBasePos_lat
-    putFloat64le msgBasePos_lon
-    putFloat64le msgBasePos_height
+    putFloat64le _msgBasePos_lat
+    putFloat64le _msgBasePos_lon
+    putFloat64le _msgBasePos_height
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgBasePos_" . stripPrefix "msgBasePos_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgBasePos_" . stripPrefix "_msgBasePos_"}
              ''MsgBasePos)
+$(makeLenses ''MsgBasePos)
 
 msgEphemeris :: Word16
 msgEphemeris = 0x0047
@@ -208,138 +214,139 @@
 -- see the Navstar GPS Space Segment/Navigation user interfaces (ICD-GPS-200,
 -- Table 20-III) for more details.
 data MsgEphemeris = MsgEphemeris
-  { msgEphemeris_tgd     :: Double
+  { _msgEphemeris_tgd    :: Double
     -- ^ Group delay differential between L1 and L2
-  , msgEphemeris_c_rs    :: Double
+  , _msgEphemeris_c_rs   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the orbit radius
-  , msgEphemeris_c_rc    :: Double
+  , _msgEphemeris_c_rc   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the orbit radius
-  , msgEphemeris_c_uc    :: Double
+  , _msgEphemeris_c_uc   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the argument of
     -- latitude
-  , msgEphemeris_c_us    :: Double
+  , _msgEphemeris_c_us   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the argument of
     -- latitude
-  , msgEphemeris_c_ic    :: Double
+  , _msgEphemeris_c_ic   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the angle of
     -- inclination
-  , msgEphemeris_c_is    :: Double
+  , _msgEphemeris_c_is   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the angle of
     -- inclination
-  , msgEphemeris_dn      :: Double
+  , _msgEphemeris_dn     :: Double
     -- ^ Mean motion difference
-  , msgEphemeris_m0      :: Double
+  , _msgEphemeris_m0     :: Double
     -- ^ Mean anomaly at reference time
-  , msgEphemeris_ecc     :: Double
+  , _msgEphemeris_ecc    :: Double
     -- ^ Eccentricity of satellite orbit
-  , msgEphemeris_sqrta   :: Double
+  , _msgEphemeris_sqrta  :: Double
     -- ^ Square root of the semi-major axis of orbit
-  , msgEphemeris_omega0  :: Double
+  , _msgEphemeris_omega0 :: Double
     -- ^ Longitude of ascending node of orbit plane at weekly epoch
-  , msgEphemeris_omegadot :: Double
+  , _msgEphemeris_omegadot :: Double
     -- ^ Rate of right ascension
-  , msgEphemeris_w       :: Double
+  , _msgEphemeris_w      :: Double
     -- ^ Argument of perigee
-  , msgEphemeris_inc     :: Double
+  , _msgEphemeris_inc    :: Double
     -- ^ Inclination
-  , msgEphemeris_inc_dot :: Double
+  , _msgEphemeris_inc_dot :: Double
     -- ^ Inclination first derivative
-  , msgEphemeris_af0     :: Double
+  , _msgEphemeris_af0    :: Double
     -- ^ Polynomial clock correction coefficient (clock bias)
-  , msgEphemeris_af1     :: Double
+  , _msgEphemeris_af1    :: Double
     -- ^ Polynomial clock correction coefficient (clock drift)
-  , msgEphemeris_af2     :: Double
+  , _msgEphemeris_af2    :: Double
     -- ^ Polynomial clock correction coefficient (rate of clock drift)
-  , msgEphemeris_toe_tow :: Double
+  , _msgEphemeris_toe_tow :: Double
     -- ^ Time of week
-  , msgEphemeris_toe_wn  :: Word16
+  , _msgEphemeris_toe_wn :: Word16
     -- ^ Week number
-  , msgEphemeris_toc_tow :: Double
+  , _msgEphemeris_toc_tow :: Double
     -- ^ Clock reference time of week
-  , msgEphemeris_toc_wn  :: Word16
+  , _msgEphemeris_toc_wn :: Word16
     -- ^ Clock reference week number
-  , msgEphemeris_valid   :: Word8
+  , _msgEphemeris_valid  :: Word8
     -- ^ Is valid?
-  , msgEphemeris_healthy :: Word8
+  , _msgEphemeris_healthy :: Word8
     -- ^ Satellite is healthy?
-  , msgEphemeris_sid     :: Word32
+  , _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_iode    :: Word8
+  , _msgEphemeris_iode   :: Word8
     -- ^ Issue of ephemeris data
-  , msgEphemeris_iodc    :: Word16
+  , _msgEphemeris_iodc   :: Word16
     -- ^ Issue of clock data
-  , msgEphemeris_reserved :: Word32
+  , _msgEphemeris_reserved :: Word32
     -- ^ Reserved field
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgEphemeris where
   get = do
-    msgEphemeris_tgd <- getFloat64le
-    msgEphemeris_c_rs <- getFloat64le
-    msgEphemeris_c_rc <- getFloat64le
-    msgEphemeris_c_uc <- getFloat64le
-    msgEphemeris_c_us <- getFloat64le
-    msgEphemeris_c_ic <- getFloat64le
-    msgEphemeris_c_is <- getFloat64le
-    msgEphemeris_dn <- getFloat64le
-    msgEphemeris_m0 <- getFloat64le
-    msgEphemeris_ecc <- getFloat64le
-    msgEphemeris_sqrta <- getFloat64le
-    msgEphemeris_omega0 <- getFloat64le
-    msgEphemeris_omegadot <- getFloat64le
-    msgEphemeris_w <- getFloat64le
-    msgEphemeris_inc <- getFloat64le
-    msgEphemeris_inc_dot <- getFloat64le
-    msgEphemeris_af0 <- getFloat64le
-    msgEphemeris_af1 <- getFloat64le
-    msgEphemeris_af2 <- getFloat64le
-    msgEphemeris_toe_tow <- getFloat64le
-    msgEphemeris_toe_wn <- getWord16le
-    msgEphemeris_toc_tow <- getFloat64le
-    msgEphemeris_toc_wn <- getWord16le
-    msgEphemeris_valid <- getWord8
-    msgEphemeris_healthy <- getWord8
-    msgEphemeris_sid <- getWord32le
-    msgEphemeris_iode <- getWord8
-    msgEphemeris_iodc <- getWord16le
-    msgEphemeris_reserved <- getWord32le
+    _msgEphemeris_tgd <- getFloat64le
+    _msgEphemeris_c_rs <- getFloat64le
+    _msgEphemeris_c_rc <- getFloat64le
+    _msgEphemeris_c_uc <- getFloat64le
+    _msgEphemeris_c_us <- getFloat64le
+    _msgEphemeris_c_ic <- getFloat64le
+    _msgEphemeris_c_is <- getFloat64le
+    _msgEphemeris_dn <- getFloat64le
+    _msgEphemeris_m0 <- getFloat64le
+    _msgEphemeris_ecc <- getFloat64le
+    _msgEphemeris_sqrta <- getFloat64le
+    _msgEphemeris_omega0 <- getFloat64le
+    _msgEphemeris_omegadot <- getFloat64le
+    _msgEphemeris_w <- getFloat64le
+    _msgEphemeris_inc <- getFloat64le
+    _msgEphemeris_inc_dot <- getFloat64le
+    _msgEphemeris_af0 <- getFloat64le
+    _msgEphemeris_af1 <- getFloat64le
+    _msgEphemeris_af2 <- getFloat64le
+    _msgEphemeris_toe_tow <- getFloat64le
+    _msgEphemeris_toe_wn <- getWord16le
+    _msgEphemeris_toc_tow <- getFloat64le
+    _msgEphemeris_toc_wn <- getWord16le
+    _msgEphemeris_valid <- getWord8
+    _msgEphemeris_healthy <- getWord8
+    _msgEphemeris_sid <- getWord32le
+    _msgEphemeris_iode <- getWord8
+    _msgEphemeris_iodc <- getWord16le
+    _msgEphemeris_reserved <- getWord32le
     return MsgEphemeris {..}
 
   put MsgEphemeris {..} = do
-    putFloat64le msgEphemeris_tgd
-    putFloat64le msgEphemeris_c_rs
-    putFloat64le msgEphemeris_c_rc
-    putFloat64le msgEphemeris_c_uc
-    putFloat64le msgEphemeris_c_us
-    putFloat64le msgEphemeris_c_ic
-    putFloat64le msgEphemeris_c_is
-    putFloat64le msgEphemeris_dn
-    putFloat64le msgEphemeris_m0
-    putFloat64le msgEphemeris_ecc
-    putFloat64le msgEphemeris_sqrta
-    putFloat64le msgEphemeris_omega0
-    putFloat64le msgEphemeris_omegadot
-    putFloat64le msgEphemeris_w
-    putFloat64le msgEphemeris_inc
-    putFloat64le msgEphemeris_inc_dot
-    putFloat64le msgEphemeris_af0
-    putFloat64le msgEphemeris_af1
-    putFloat64le msgEphemeris_af2
-    putFloat64le msgEphemeris_toe_tow
-    putWord16le msgEphemeris_toe_wn
-    putFloat64le msgEphemeris_toc_tow
-    putWord16le msgEphemeris_toc_wn
-    putWord8 msgEphemeris_valid
-    putWord8 msgEphemeris_healthy
-    putWord32le msgEphemeris_sid
-    putWord8 msgEphemeris_iode
-    putWord16le msgEphemeris_iodc
-    putWord32le msgEphemeris_reserved
+    putFloat64le _msgEphemeris_tgd
+    putFloat64le _msgEphemeris_c_rs
+    putFloat64le _msgEphemeris_c_rc
+    putFloat64le _msgEphemeris_c_uc
+    putFloat64le _msgEphemeris_c_us
+    putFloat64le _msgEphemeris_c_ic
+    putFloat64le _msgEphemeris_c_is
+    putFloat64le _msgEphemeris_dn
+    putFloat64le _msgEphemeris_m0
+    putFloat64le _msgEphemeris_ecc
+    putFloat64le _msgEphemeris_sqrta
+    putFloat64le _msgEphemeris_omega0
+    putFloat64le _msgEphemeris_omegadot
+    putFloat64le _msgEphemeris_w
+    putFloat64le _msgEphemeris_inc
+    putFloat64le _msgEphemeris_inc_dot
+    putFloat64le _msgEphemeris_af0
+    putFloat64le _msgEphemeris_af1
+    putFloat64le _msgEphemeris_af2
+    putFloat64le _msgEphemeris_toe_tow
+    putWord16le _msgEphemeris_toe_wn
+    putFloat64le _msgEphemeris_toc_tow
+    putWord16le _msgEphemeris_toc_wn
+    putWord8 _msgEphemeris_valid
+    putWord8 _msgEphemeris_healthy
+    putWord32le _msgEphemeris_sid
+    putWord8 _msgEphemeris_iode
+    putWord16le _msgEphemeris_iodc
+    putWord32le _msgEphemeris_reserved
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgEphemeris_" . stripPrefix "msgEphemeris_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgEphemeris_" . stripPrefix "_msgEphemeris_"}
              ''MsgEphemeris)
+$(makeLenses ''MsgEphemeris)
 
 msgEphemerisDepA :: Word16
 msgEphemerisDepA = 0x001A
@@ -348,124 +355,125 @@
 --
 -- Deprecated.
 data MsgEphemerisDepA = MsgEphemerisDepA
-  { msgEphemerisDepA_tgd     :: Double
+  { _msgEphemerisDepA_tgd    :: Double
     -- ^ Group delay differential between L1 and L2
-  , msgEphemerisDepA_c_rs    :: Double
+  , _msgEphemerisDepA_c_rs   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the orbit radius
-  , msgEphemerisDepA_c_rc    :: Double
+  , _msgEphemerisDepA_c_rc   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the orbit radius
-  , msgEphemerisDepA_c_uc    :: Double
+  , _msgEphemerisDepA_c_uc   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the argument of
     -- latitude
-  , msgEphemerisDepA_c_us    :: Double
+  , _msgEphemerisDepA_c_us   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the argument of
     -- latitude
-  , msgEphemerisDepA_c_ic    :: Double
+  , _msgEphemerisDepA_c_ic   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the angle of
     -- inclination
-  , msgEphemerisDepA_c_is    :: Double
+  , _msgEphemerisDepA_c_is   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the angle of
     -- inclination
-  , msgEphemerisDepA_dn      :: Double
+  , _msgEphemerisDepA_dn     :: Double
     -- ^ Mean motion difference
-  , msgEphemerisDepA_m0      :: Double
+  , _msgEphemerisDepA_m0     :: Double
     -- ^ Mean anomaly at reference time
-  , msgEphemerisDepA_ecc     :: Double
+  , _msgEphemerisDepA_ecc    :: Double
     -- ^ Eccentricity of satellite orbit
-  , msgEphemerisDepA_sqrta   :: Double
+  , _msgEphemerisDepA_sqrta  :: Double
     -- ^ Square root of the semi-major axis of orbit
-  , msgEphemerisDepA_omega0  :: Double
+  , _msgEphemerisDepA_omega0 :: Double
     -- ^ Longitude of ascending node of orbit plane at weekly epoch
-  , msgEphemerisDepA_omegadot :: Double
+  , _msgEphemerisDepA_omegadot :: Double
     -- ^ Rate of right ascension
-  , msgEphemerisDepA_w       :: Double
+  , _msgEphemerisDepA_w      :: Double
     -- ^ Argument of perigee
-  , msgEphemerisDepA_inc     :: Double
+  , _msgEphemerisDepA_inc    :: Double
     -- ^ Inclination
-  , msgEphemerisDepA_inc_dot :: Double
+  , _msgEphemerisDepA_inc_dot :: Double
     -- ^ Inclination first derivative
-  , msgEphemerisDepA_af0     :: Double
+  , _msgEphemerisDepA_af0    :: Double
     -- ^ Polynomial clock correction coefficient (clock bias)
-  , msgEphemerisDepA_af1     :: Double
+  , _msgEphemerisDepA_af1    :: Double
     -- ^ Polynomial clock correction coefficient (clock drift)
-  , msgEphemerisDepA_af2     :: Double
+  , _msgEphemerisDepA_af2    :: Double
     -- ^ Polynomial clock correction coefficient (rate of clock drift)
-  , msgEphemerisDepA_toe_tow :: Double
+  , _msgEphemerisDepA_toe_tow :: Double
     -- ^ Time of week
-  , msgEphemerisDepA_toe_wn  :: Word16
+  , _msgEphemerisDepA_toe_wn :: Word16
     -- ^ Week number
-  , msgEphemerisDepA_toc_tow :: Double
+  , _msgEphemerisDepA_toc_tow :: Double
     -- ^ Clock reference time of week
-  , msgEphemerisDepA_toc_wn  :: Word16
+  , _msgEphemerisDepA_toc_wn :: Word16
     -- ^ Clock reference week number
-  , msgEphemerisDepA_valid   :: Word8
+  , _msgEphemerisDepA_valid  :: Word8
     -- ^ Is valid?
-  , msgEphemerisDepA_healthy :: Word8
+  , _msgEphemerisDepA_healthy :: Word8
     -- ^ Satellite is healthy?
-  , msgEphemerisDepA_prn     :: Word8
+  , _msgEphemerisDepA_prn    :: Word8
     -- ^ PRN being tracked
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgEphemerisDepA where
   get = do
-    msgEphemerisDepA_tgd <- getFloat64le
-    msgEphemerisDepA_c_rs <- getFloat64le
-    msgEphemerisDepA_c_rc <- getFloat64le
-    msgEphemerisDepA_c_uc <- getFloat64le
-    msgEphemerisDepA_c_us <- getFloat64le
-    msgEphemerisDepA_c_ic <- getFloat64le
-    msgEphemerisDepA_c_is <- getFloat64le
-    msgEphemerisDepA_dn <- getFloat64le
-    msgEphemerisDepA_m0 <- getFloat64le
-    msgEphemerisDepA_ecc <- getFloat64le
-    msgEphemerisDepA_sqrta <- getFloat64le
-    msgEphemerisDepA_omega0 <- getFloat64le
-    msgEphemerisDepA_omegadot <- getFloat64le
-    msgEphemerisDepA_w <- getFloat64le
-    msgEphemerisDepA_inc <- getFloat64le
-    msgEphemerisDepA_inc_dot <- getFloat64le
-    msgEphemerisDepA_af0 <- getFloat64le
-    msgEphemerisDepA_af1 <- getFloat64le
-    msgEphemerisDepA_af2 <- getFloat64le
-    msgEphemerisDepA_toe_tow <- getFloat64le
-    msgEphemerisDepA_toe_wn <- getWord16le
-    msgEphemerisDepA_toc_tow <- getFloat64le
-    msgEphemerisDepA_toc_wn <- getWord16le
-    msgEphemerisDepA_valid <- getWord8
-    msgEphemerisDepA_healthy <- getWord8
-    msgEphemerisDepA_prn <- getWord8
+    _msgEphemerisDepA_tgd <- getFloat64le
+    _msgEphemerisDepA_c_rs <- getFloat64le
+    _msgEphemerisDepA_c_rc <- getFloat64le
+    _msgEphemerisDepA_c_uc <- getFloat64le
+    _msgEphemerisDepA_c_us <- getFloat64le
+    _msgEphemerisDepA_c_ic <- getFloat64le
+    _msgEphemerisDepA_c_is <- getFloat64le
+    _msgEphemerisDepA_dn <- getFloat64le
+    _msgEphemerisDepA_m0 <- getFloat64le
+    _msgEphemerisDepA_ecc <- getFloat64le
+    _msgEphemerisDepA_sqrta <- getFloat64le
+    _msgEphemerisDepA_omega0 <- getFloat64le
+    _msgEphemerisDepA_omegadot <- getFloat64le
+    _msgEphemerisDepA_w <- getFloat64le
+    _msgEphemerisDepA_inc <- getFloat64le
+    _msgEphemerisDepA_inc_dot <- getFloat64le
+    _msgEphemerisDepA_af0 <- getFloat64le
+    _msgEphemerisDepA_af1 <- getFloat64le
+    _msgEphemerisDepA_af2 <- getFloat64le
+    _msgEphemerisDepA_toe_tow <- getFloat64le
+    _msgEphemerisDepA_toe_wn <- getWord16le
+    _msgEphemerisDepA_toc_tow <- getFloat64le
+    _msgEphemerisDepA_toc_wn <- getWord16le
+    _msgEphemerisDepA_valid <- getWord8
+    _msgEphemerisDepA_healthy <- getWord8
+    _msgEphemerisDepA_prn <- getWord8
     return MsgEphemerisDepA {..}
 
   put MsgEphemerisDepA {..} = do
-    putFloat64le msgEphemerisDepA_tgd
-    putFloat64le msgEphemerisDepA_c_rs
-    putFloat64le msgEphemerisDepA_c_rc
-    putFloat64le msgEphemerisDepA_c_uc
-    putFloat64le msgEphemerisDepA_c_us
-    putFloat64le msgEphemerisDepA_c_ic
-    putFloat64le msgEphemerisDepA_c_is
-    putFloat64le msgEphemerisDepA_dn
-    putFloat64le msgEphemerisDepA_m0
-    putFloat64le msgEphemerisDepA_ecc
-    putFloat64le msgEphemerisDepA_sqrta
-    putFloat64le msgEphemerisDepA_omega0
-    putFloat64le msgEphemerisDepA_omegadot
-    putFloat64le msgEphemerisDepA_w
-    putFloat64le msgEphemerisDepA_inc
-    putFloat64le msgEphemerisDepA_inc_dot
-    putFloat64le msgEphemerisDepA_af0
-    putFloat64le msgEphemerisDepA_af1
-    putFloat64le msgEphemerisDepA_af2
-    putFloat64le msgEphemerisDepA_toe_tow
-    putWord16le msgEphemerisDepA_toe_wn
-    putFloat64le msgEphemerisDepA_toc_tow
-    putWord16le msgEphemerisDepA_toc_wn
-    putWord8 msgEphemerisDepA_valid
-    putWord8 msgEphemerisDepA_healthy
-    putWord8 msgEphemerisDepA_prn
+    putFloat64le _msgEphemerisDepA_tgd
+    putFloat64le _msgEphemerisDepA_c_rs
+    putFloat64le _msgEphemerisDepA_c_rc
+    putFloat64le _msgEphemerisDepA_c_uc
+    putFloat64le _msgEphemerisDepA_c_us
+    putFloat64le _msgEphemerisDepA_c_ic
+    putFloat64le _msgEphemerisDepA_c_is
+    putFloat64le _msgEphemerisDepA_dn
+    putFloat64le _msgEphemerisDepA_m0
+    putFloat64le _msgEphemerisDepA_ecc
+    putFloat64le _msgEphemerisDepA_sqrta
+    putFloat64le _msgEphemerisDepA_omega0
+    putFloat64le _msgEphemerisDepA_omegadot
+    putFloat64le _msgEphemerisDepA_w
+    putFloat64le _msgEphemerisDepA_inc
+    putFloat64le _msgEphemerisDepA_inc_dot
+    putFloat64le _msgEphemerisDepA_af0
+    putFloat64le _msgEphemerisDepA_af1
+    putFloat64le _msgEphemerisDepA_af2
+    putFloat64le _msgEphemerisDepA_toe_tow
+    putWord16le _msgEphemerisDepA_toe_wn
+    putFloat64le _msgEphemerisDepA_toc_tow
+    putWord16le _msgEphemerisDepA_toc_wn
+    putWord8 _msgEphemerisDepA_valid
+    putWord8 _msgEphemerisDepA_healthy
+    putWord8 _msgEphemerisDepA_prn
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgEphemerisDepA_" . stripPrefix "msgEphemerisDepA_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgEphemerisDepA_" . stripPrefix "_msgEphemerisDepA_"}
              ''MsgEphemerisDepA)
+$(makeLenses ''MsgEphemerisDepA)
 
 msgEphemerisDepB :: Word16
 msgEphemerisDepB = 0x0046
@@ -474,165 +482,167 @@
 --
 -- Deprecated.
 data MsgEphemerisDepB = MsgEphemerisDepB
-  { msgEphemerisDepB_tgd     :: Double
+  { _msgEphemerisDepB_tgd    :: Double
     -- ^ Group delay differential between L1 and L2
-  , msgEphemerisDepB_c_rs    :: Double
+  , _msgEphemerisDepB_c_rs   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the orbit radius
-  , msgEphemerisDepB_c_rc    :: Double
+  , _msgEphemerisDepB_c_rc   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the orbit radius
-  , msgEphemerisDepB_c_uc    :: Double
+  , _msgEphemerisDepB_c_uc   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the argument of
     -- latitude
-  , msgEphemerisDepB_c_us    :: Double
+  , _msgEphemerisDepB_c_us   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the argument of
     -- latitude
-  , msgEphemerisDepB_c_ic    :: Double
+  , _msgEphemerisDepB_c_ic   :: Double
     -- ^ Amplitude of the cosine harmonic correction term to the angle of
     -- inclination
-  , msgEphemerisDepB_c_is    :: Double
+  , _msgEphemerisDepB_c_is   :: Double
     -- ^ Amplitude of the sine harmonic correction term to the angle of
     -- inclination
-  , msgEphemerisDepB_dn      :: Double
+  , _msgEphemerisDepB_dn     :: Double
     -- ^ Mean motion difference
-  , msgEphemerisDepB_m0      :: Double
+  , _msgEphemerisDepB_m0     :: Double
     -- ^ Mean anomaly at reference time
-  , msgEphemerisDepB_ecc     :: Double
+  , _msgEphemerisDepB_ecc    :: Double
     -- ^ Eccentricity of satellite orbit
-  , msgEphemerisDepB_sqrta   :: Double
+  , _msgEphemerisDepB_sqrta  :: Double
     -- ^ Square root of the semi-major axis of orbit
-  , msgEphemerisDepB_omega0  :: Double
+  , _msgEphemerisDepB_omega0 :: Double
     -- ^ Longitude of ascending node of orbit plane at weekly epoch
-  , msgEphemerisDepB_omegadot :: Double
+  , _msgEphemerisDepB_omegadot :: Double
     -- ^ Rate of right ascension
-  , msgEphemerisDepB_w       :: Double
+  , _msgEphemerisDepB_w      :: Double
     -- ^ Argument of perigee
-  , msgEphemerisDepB_inc     :: Double
+  , _msgEphemerisDepB_inc    :: Double
     -- ^ Inclination
-  , msgEphemerisDepB_inc_dot :: Double
+  , _msgEphemerisDepB_inc_dot :: Double
     -- ^ Inclination first derivative
-  , msgEphemerisDepB_af0     :: Double
+  , _msgEphemerisDepB_af0    :: Double
     -- ^ Polynomial clock correction coefficient (clock bias)
-  , msgEphemerisDepB_af1     :: Double
+  , _msgEphemerisDepB_af1    :: Double
     -- ^ Polynomial clock correction coefficient (clock drift)
-  , msgEphemerisDepB_af2     :: Double
+  , _msgEphemerisDepB_af2    :: Double
     -- ^ Polynomial clock correction coefficient (rate of clock drift)
-  , msgEphemerisDepB_toe_tow :: Double
+  , _msgEphemerisDepB_toe_tow :: Double
     -- ^ Time of week
-  , msgEphemerisDepB_toe_wn  :: Word16
+  , _msgEphemerisDepB_toe_wn :: Word16
     -- ^ Week number
-  , msgEphemerisDepB_toc_tow :: Double
+  , _msgEphemerisDepB_toc_tow :: Double
     -- ^ Clock reference time of week
-  , msgEphemerisDepB_toc_wn  :: Word16
+  , _msgEphemerisDepB_toc_wn :: Word16
     -- ^ Clock reference week number
-  , msgEphemerisDepB_valid   :: Word8
+  , _msgEphemerisDepB_valid  :: Word8
     -- ^ Is valid?
-  , msgEphemerisDepB_healthy :: Word8
+  , _msgEphemerisDepB_healthy :: Word8
     -- ^ Satellite is healthy?
-  , msgEphemerisDepB_prn     :: Word8
+  , _msgEphemerisDepB_prn    :: Word8
     -- ^ PRN being tracked
-  , msgEphemerisDepB_iode    :: Word8
+  , _msgEphemerisDepB_iode   :: Word8
     -- ^ Issue of ephemeris data
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgEphemerisDepB where
   get = do
-    msgEphemerisDepB_tgd <- getFloat64le
-    msgEphemerisDepB_c_rs <- getFloat64le
-    msgEphemerisDepB_c_rc <- getFloat64le
-    msgEphemerisDepB_c_uc <- getFloat64le
-    msgEphemerisDepB_c_us <- getFloat64le
-    msgEphemerisDepB_c_ic <- getFloat64le
-    msgEphemerisDepB_c_is <- getFloat64le
-    msgEphemerisDepB_dn <- getFloat64le
-    msgEphemerisDepB_m0 <- getFloat64le
-    msgEphemerisDepB_ecc <- getFloat64le
-    msgEphemerisDepB_sqrta <- getFloat64le
-    msgEphemerisDepB_omega0 <- getFloat64le
-    msgEphemerisDepB_omegadot <- getFloat64le
-    msgEphemerisDepB_w <- getFloat64le
-    msgEphemerisDepB_inc <- getFloat64le
-    msgEphemerisDepB_inc_dot <- getFloat64le
-    msgEphemerisDepB_af0 <- getFloat64le
-    msgEphemerisDepB_af1 <- getFloat64le
-    msgEphemerisDepB_af2 <- getFloat64le
-    msgEphemerisDepB_toe_tow <- getFloat64le
-    msgEphemerisDepB_toe_wn <- getWord16le
-    msgEphemerisDepB_toc_tow <- getFloat64le
-    msgEphemerisDepB_toc_wn <- getWord16le
-    msgEphemerisDepB_valid <- getWord8
-    msgEphemerisDepB_healthy <- getWord8
-    msgEphemerisDepB_prn <- getWord8
-    msgEphemerisDepB_iode <- getWord8
+    _msgEphemerisDepB_tgd <- getFloat64le
+    _msgEphemerisDepB_c_rs <- getFloat64le
+    _msgEphemerisDepB_c_rc <- getFloat64le
+    _msgEphemerisDepB_c_uc <- getFloat64le
+    _msgEphemerisDepB_c_us <- getFloat64le
+    _msgEphemerisDepB_c_ic <- getFloat64le
+    _msgEphemerisDepB_c_is <- getFloat64le
+    _msgEphemerisDepB_dn <- getFloat64le
+    _msgEphemerisDepB_m0 <- getFloat64le
+    _msgEphemerisDepB_ecc <- getFloat64le
+    _msgEphemerisDepB_sqrta <- getFloat64le
+    _msgEphemerisDepB_omega0 <- getFloat64le
+    _msgEphemerisDepB_omegadot <- getFloat64le
+    _msgEphemerisDepB_w <- getFloat64le
+    _msgEphemerisDepB_inc <- getFloat64le
+    _msgEphemerisDepB_inc_dot <- getFloat64le
+    _msgEphemerisDepB_af0 <- getFloat64le
+    _msgEphemerisDepB_af1 <- getFloat64le
+    _msgEphemerisDepB_af2 <- getFloat64le
+    _msgEphemerisDepB_toe_tow <- getFloat64le
+    _msgEphemerisDepB_toe_wn <- getWord16le
+    _msgEphemerisDepB_toc_tow <- getFloat64le
+    _msgEphemerisDepB_toc_wn <- getWord16le
+    _msgEphemerisDepB_valid <- getWord8
+    _msgEphemerisDepB_healthy <- getWord8
+    _msgEphemerisDepB_prn <- getWord8
+    _msgEphemerisDepB_iode <- getWord8
     return MsgEphemerisDepB {..}
 
   put MsgEphemerisDepB {..} = do
-    putFloat64le msgEphemerisDepB_tgd
-    putFloat64le msgEphemerisDepB_c_rs
-    putFloat64le msgEphemerisDepB_c_rc
-    putFloat64le msgEphemerisDepB_c_uc
-    putFloat64le msgEphemerisDepB_c_us
-    putFloat64le msgEphemerisDepB_c_ic
-    putFloat64le msgEphemerisDepB_c_is
-    putFloat64le msgEphemerisDepB_dn
-    putFloat64le msgEphemerisDepB_m0
-    putFloat64le msgEphemerisDepB_ecc
-    putFloat64le msgEphemerisDepB_sqrta
-    putFloat64le msgEphemerisDepB_omega0
-    putFloat64le msgEphemerisDepB_omegadot
-    putFloat64le msgEphemerisDepB_w
-    putFloat64le msgEphemerisDepB_inc
-    putFloat64le msgEphemerisDepB_inc_dot
-    putFloat64le msgEphemerisDepB_af0
-    putFloat64le msgEphemerisDepB_af1
-    putFloat64le msgEphemerisDepB_af2
-    putFloat64le msgEphemerisDepB_toe_tow
-    putWord16le msgEphemerisDepB_toe_wn
-    putFloat64le msgEphemerisDepB_toc_tow
-    putWord16le msgEphemerisDepB_toc_wn
-    putWord8 msgEphemerisDepB_valid
-    putWord8 msgEphemerisDepB_healthy
-    putWord8 msgEphemerisDepB_prn
-    putWord8 msgEphemerisDepB_iode
+    putFloat64le _msgEphemerisDepB_tgd
+    putFloat64le _msgEphemerisDepB_c_rs
+    putFloat64le _msgEphemerisDepB_c_rc
+    putFloat64le _msgEphemerisDepB_c_uc
+    putFloat64le _msgEphemerisDepB_c_us
+    putFloat64le _msgEphemerisDepB_c_ic
+    putFloat64le _msgEphemerisDepB_c_is
+    putFloat64le _msgEphemerisDepB_dn
+    putFloat64le _msgEphemerisDepB_m0
+    putFloat64le _msgEphemerisDepB_ecc
+    putFloat64le _msgEphemerisDepB_sqrta
+    putFloat64le _msgEphemerisDepB_omega0
+    putFloat64le _msgEphemerisDepB_omegadot
+    putFloat64le _msgEphemerisDepB_w
+    putFloat64le _msgEphemerisDepB_inc
+    putFloat64le _msgEphemerisDepB_inc_dot
+    putFloat64le _msgEphemerisDepB_af0
+    putFloat64le _msgEphemerisDepB_af1
+    putFloat64le _msgEphemerisDepB_af2
+    putFloat64le _msgEphemerisDepB_toe_tow
+    putWord16le _msgEphemerisDepB_toe_wn
+    putFloat64le _msgEphemerisDepB_toc_tow
+    putWord16le _msgEphemerisDepB_toc_wn
+    putWord8 _msgEphemerisDepB_valid
+    putWord8 _msgEphemerisDepB_healthy
+    putWord8 _msgEphemerisDepB_prn
+    putWord8 _msgEphemerisDepB_iode
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgEphemerisDepB_" . stripPrefix "msgEphemerisDepB_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgEphemerisDepB_" . stripPrefix "_msgEphemerisDepB_"}
              ''MsgEphemerisDepB)
+$(makeLenses ''MsgEphemerisDepB)
 
 -- | PackedObsContentDepA.
 --
 -- Deprecated.
 data PackedObsContentDepA = PackedObsContentDepA
-  { packedObsContentDepA_P   :: Word32
+  { _packedObsContentDepA_P  :: Word32
     -- ^ Pseudorange observation
-  , packedObsContentDepA_L   :: CarrierPhase
+  , _packedObsContentDepA_L  :: CarrierPhase
     -- ^ Carrier phase observation
-  , packedObsContentDepA_cn0 :: Word8
+  , _packedObsContentDepA_cn0 :: Word8
     -- ^ Carrier-to-Noise density
-  , packedObsContentDepA_lock :: Word16
+  , _packedObsContentDepA_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.
-  , packedObsContentDepA_prn :: Word8
+  , _packedObsContentDepA_prn :: Word8
     -- ^ PRN-1 identifier of the satellite signal
   } deriving ( Show, Read, Eq )
 
 instance Binary PackedObsContentDepA where
   get = do
-    packedObsContentDepA_P <- getWord32le
-    packedObsContentDepA_L <- get
-    packedObsContentDepA_cn0 <- getWord8
-    packedObsContentDepA_lock <- getWord16le
-    packedObsContentDepA_prn <- getWord8
+    _packedObsContentDepA_P <- getWord32le
+    _packedObsContentDepA_L <- get
+    _packedObsContentDepA_cn0 <- getWord8
+    _packedObsContentDepA_lock <- getWord16le
+    _packedObsContentDepA_prn <- getWord8
     return PackedObsContentDepA {..}
 
   put PackedObsContentDepA {..} = do
-    putWord32le packedObsContentDepA_P
-    put packedObsContentDepA_L
-    putWord8 packedObsContentDepA_cn0
-    putWord16le packedObsContentDepA_lock
-    putWord8 packedObsContentDepA_prn
+    putWord32le _packedObsContentDepA_P
+    put _packedObsContentDepA_L
+    putWord8 _packedObsContentDepA_cn0
+    putWord16le _packedObsContentDepA_lock
+    putWord8 _packedObsContentDepA_prn
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "packedObsContentDepA_" . stripPrefix "packedObsContentDepA_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_packedObsContentDepA_" . stripPrefix "_packedObsContentDepA_"}
              ''PackedObsContentDepA)
+$(makeLenses ''PackedObsContentDepA)
 
 msgObsDepA :: Word16
 msgObsDepA = 0x0045
@@ -641,21 +651,22 @@
 --
 -- Deprecated.
 data MsgObsDepA = MsgObsDepA
-  { msgObsDepA_header :: ObservationHeader
+  { _msgObsDepA_header :: ObservationHeader
     -- ^ Header of a GPS observation message
-  , msgObsDepA_obs   :: [PackedObsContentDepA]
+  , _msgObsDepA_obs  :: [PackedObsContentDepA]
     -- ^ Pseudorange and carrier phase observation for a satellite being tracked.
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgObsDepA where
   get = do
-    msgObsDepA_header <- get
-    msgObsDepA_obs <- whileM (liftM not isEmpty) get
+    _msgObsDepA_header <- get
+    _msgObsDepA_obs <- whileM (liftM not isEmpty) get
     return MsgObsDepA {..}
 
   put MsgObsDepA {..} = do
-    put msgObsDepA_header
-    mapM_ put msgObsDepA_obs
+    put _msgObsDepA_header
+    mapM_ put _msgObsDepA_obs
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgObsDepA_" . stripPrefix "msgObsDepA_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgObsDepA_" . stripPrefix "_msgObsDepA_"}
              ''MsgObsDepA)
+$(makeLenses ''MsgObsDepA)
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
@@ -14,7 +14,7 @@
 module SwiftNav.SBP.Piksi where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -44,8 +44,9 @@
   put MsgAlmanac =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgAlmanac_" . stripPrefix "msgAlmanac_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgAlmanac_" . stripPrefix "_msgAlmanac_"}
              ''MsgAlmanac)
+$(makeLenses ''MsgAlmanac)
 
 msgSetTime :: Word16
 msgSetTime = 0x0068
@@ -64,8 +65,9 @@
   put MsgSetTime =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSetTime_" . stripPrefix "msgSetTime_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgSetTime_" . stripPrefix "_msgSetTime_"}
              ''MsgSetTime)
+$(makeLenses ''MsgSetTime)
 
 msgReset :: Word16
 msgReset = 0x00B2
@@ -83,8 +85,9 @@
   put MsgReset =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgReset_" . stripPrefix "msgReset_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgReset_" . stripPrefix "_msgReset_"}
              ''MsgReset)
+$(makeLenses ''MsgReset)
 
 msgCwResults :: Word16
 msgCwResults = 0x00C0
@@ -104,8 +107,9 @@
   put MsgCwResults =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgCwResults_" . stripPrefix "msgCwResults_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgCwResults_" . stripPrefix "_msgCwResults_"}
              ''MsgCwResults)
+$(makeLenses ''MsgCwResults)
 
 msgCwStart :: Word16
 msgCwStart = 0x00C1
@@ -125,8 +129,9 @@
   put MsgCwStart =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgCwStart_" . stripPrefix "msgCwStart_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgCwStart_" . stripPrefix "_msgCwStart_"}
              ''MsgCwStart)
+$(makeLenses ''MsgCwStart)
 
 msgResetFilters :: Word16
 msgResetFilters = 0x0022
@@ -136,20 +141,21 @@
 -- This message resets either the DGNSS Kalman filters or Integer Ambiguity
 -- Resolution (IAR) process.
 data MsgResetFilters = MsgResetFilters
-  { msgResetFilters_filter :: Word8
+  { _msgResetFilters_filter :: Word8
     -- ^ Filter flags
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgResetFilters where
   get = do
-    msgResetFilters_filter <- getWord8
+    _msgResetFilters_filter <- getWord8
     return MsgResetFilters {..}
 
   put MsgResetFilters {..} = do
-    putWord8 msgResetFilters_filter
+    putWord8 _msgResetFilters_filter
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgResetFilters_" . stripPrefix "msgResetFilters_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgResetFilters_" . stripPrefix "_msgResetFilters_"}
              ''MsgResetFilters)
+$(makeLenses ''MsgResetFilters)
 
 msgInitBase :: Word16
 msgInitBase = 0x0023
@@ -170,8 +176,9 @@
   put MsgInitBase =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgInitBase_" . stripPrefix "msgInitBase_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgInitBase_" . stripPrefix "_msgInitBase_"}
              ''MsgInitBase)
+$(makeLenses ''MsgInitBase)
 
 msgThreadState :: Word16
 msgThreadState = 0x0017
@@ -182,69 +189,71 @@
 -- (RTOS) thread usage statistics for the named thread. The reported percentage
 -- values require to be normalized.
 data MsgThreadState = MsgThreadState
-  { msgThreadState_name      :: ByteString
+  { _msgThreadState_name     :: ByteString
     -- ^ Thread name (NULL terminated)
-  , msgThreadState_cpu       :: Word16
+  , _msgThreadState_cpu      :: Word16
     -- ^ Percentage cpu use for this thread. Values range from 0 - 1000 and needs
     -- to be renormalized to 100
-  , msgThreadState_stack_free :: Word32
+  , _msgThreadState_stack_free :: Word32
     -- ^ Free stack space for this thread
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgThreadState where
   get = do
-    msgThreadState_name <- getByteString 20
-    msgThreadState_cpu <- getWord16le
-    msgThreadState_stack_free <- getWord32le
+    _msgThreadState_name <- getByteString 20
+    _msgThreadState_cpu <- getWord16le
+    _msgThreadState_stack_free <- getWord32le
     return MsgThreadState {..}
 
   put MsgThreadState {..} = do
-    putByteString msgThreadState_name
-    putWord16le msgThreadState_cpu
-    putWord32le msgThreadState_stack_free
+    putByteString _msgThreadState_name
+    putWord16le _msgThreadState_cpu
+    putWord32le _msgThreadState_stack_free
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgThreadState_" . stripPrefix "msgThreadState_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgThreadState_" . stripPrefix "_msgThreadState_"}
              ''MsgThreadState)
+$(makeLenses ''MsgThreadState)
 
 -- | UARTChannel.
 --
 -- Throughput, utilization, and error counts on the RX/TX buffers of this UART
 -- channel. The reported percentage values require to be normalized.
 data UARTChannel = UARTChannel
-  { uARTChannel_tx_throughput  :: Float
+  { _uARTChannel_tx_throughput :: Float
     -- ^ UART transmit throughput
-  , uARTChannel_rx_throughput  :: Float
+  , _uARTChannel_rx_throughput :: Float
     -- ^ UART receive throughput
-  , uARTChannel_crc_error_count :: Word16
+  , _uARTChannel_crc_error_count :: Word16
     -- ^ UART CRC error count
-  , uARTChannel_io_error_count :: Word16
+  , _uARTChannel_io_error_count :: Word16
     -- ^ UART IO error count
-  , uARTChannel_tx_buffer_level :: Word8
+  , _uARTChannel_tx_buffer_level :: Word8
     -- ^ UART transmit buffer percentage utilization (ranges from 0 - 255)
-  , uARTChannel_rx_buffer_level :: Word8
+  , _uARTChannel_rx_buffer_level :: Word8
     -- ^ UART receive buffer percentage utilization (ranges from 0 to 255)
   } deriving ( Show, Read, Eq )
 
 instance Binary UARTChannel where
   get = do
-    uARTChannel_tx_throughput <- getFloat32le
-    uARTChannel_rx_throughput <- getFloat32le
-    uARTChannel_crc_error_count <- getWord16le
-    uARTChannel_io_error_count <- getWord16le
-    uARTChannel_tx_buffer_level <- getWord8
-    uARTChannel_rx_buffer_level <- getWord8
+    _uARTChannel_tx_throughput <- getFloat32le
+    _uARTChannel_rx_throughput <- getFloat32le
+    _uARTChannel_crc_error_count <- getWord16le
+    _uARTChannel_io_error_count <- getWord16le
+    _uARTChannel_tx_buffer_level <- getWord8
+    _uARTChannel_rx_buffer_level <- getWord8
     return UARTChannel {..}
 
   put UARTChannel {..} = do
-    putFloat32le uARTChannel_tx_throughput
-    putFloat32le uARTChannel_rx_throughput
-    putWord16le uARTChannel_crc_error_count
-    putWord16le uARTChannel_io_error_count
-    putWord8 uARTChannel_tx_buffer_level
-    putWord8 uARTChannel_rx_buffer_level
+    putFloat32le _uARTChannel_tx_throughput
+    putFloat32le _uARTChannel_rx_throughput
+    putWord16le _uARTChannel_crc_error_count
+    putWord16le _uARTChannel_io_error_count
+    putWord8 _uARTChannel_tx_buffer_level
+    putWord8 _uARTChannel_rx_buffer_level
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "uARTChannel_" . stripPrefix "uARTChannel_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_uARTChannel_" . stripPrefix "_uARTChannel_"}
              ''UARTChannel)
+$(makeLenses ''UARTChannel)
 
 -- | Latency.
 --
@@ -253,32 +262,33 @@
 -- 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
+  { _latency_avg   :: Int32
     -- ^ Average latency
-  , latency_lmin   :: Int32
+  , _latency_lmin  :: Int32
     -- ^ Minimum latency
-  , latency_lmax   :: Int32
+  , _latency_lmax  :: Int32
     -- ^ Maximum latency
-  , latency_current :: Int32
+  , _latency_current :: Int32
     -- ^ Smoothed estimate of the current latency
   } deriving ( Show, Read, Eq )
 
 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 <- liftM fromIntegral getWord32le
+    _latency_lmin <- liftM fromIntegral getWord32le
+    _latency_lmax <- liftM fromIntegral getWord32le
+    _latency_current <- liftM fromIntegral getWord32le
     return Latency {..}
 
   put Latency {..} = do
-    putWord32le $ fromIntegral latency_avg
-    putWord32le $ fromIntegral latency_lmin
-    putWord32le $ fromIntegral latency_lmax
-    putWord32le $ fromIntegral latency_current
+    putWord32le $ fromIntegral _latency_avg
+    putWord32le $ fromIntegral _latency_lmin
+    putWord32le $ fromIntegral _latency_lmax
+    putWord32le $ fromIntegral _latency_current
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "latency_" . stripPrefix "latency_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_latency_" . stripPrefix "_latency_"}
              ''Latency)
+$(makeLenses ''Latency)
 
 msgUartState :: Word16
 msgUartState = 0x0018
@@ -291,32 +301,33 @@
 -- hosts, or other interfaces in future. The reported percentage values require
 -- to be normalized.
 data MsgUartState = MsgUartState
-  { msgUartState_uart_a   :: UARTChannel
+  { _msgUartState_uart_a  :: UARTChannel
     -- ^ State of UART A
-  , msgUartState_uart_b   :: UARTChannel
+  , _msgUartState_uart_b  :: UARTChannel
     -- ^ State of UART B
-  , msgUartState_uart_ftdi :: UARTChannel
+  , _msgUartState_uart_ftdi :: UARTChannel
     -- ^ State of UART FTDI (USB logger)
-  , msgUartState_latency  :: Latency
+  , _msgUartState_latency :: Latency
     -- ^ UART communication latency
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgUartState where
   get = do
-    msgUartState_uart_a <- get
-    msgUartState_uart_b <- get
-    msgUartState_uart_ftdi <- get
-    msgUartState_latency <- get
+    _msgUartState_uart_a <- get
+    _msgUartState_uart_b <- get
+    _msgUartState_uart_ftdi <- get
+    _msgUartState_latency <- get
     return MsgUartState {..}
 
   put MsgUartState {..} = do
-    put msgUartState_uart_a
-    put msgUartState_uart_b
-    put msgUartState_uart_ftdi
-    put msgUartState_latency
+    put _msgUartState_uart_a
+    put _msgUartState_uart_b
+    put _msgUartState_uart_ftdi
+    put _msgUartState_latency
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgUartState_" . stripPrefix "msgUartState_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgUartState_" . stripPrefix "_msgUartState_"}
              ''MsgUartState)
+$(makeLenses ''MsgUartState)
 
 msgIarState :: Word16
 msgIarState = 0x0019
@@ -327,20 +338,21 @@
 -- process, which resolves unknown integer ambiguities from double-differenced
 -- carrier-phase measurements from satellite observations.
 data MsgIarState = MsgIarState
-  { msgIarState_num_hyps :: Word32
+  { _msgIarState_num_hyps :: Word32
     -- ^ Number of integer ambiguity hypotheses remaining
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgIarState where
   get = do
-    msgIarState_num_hyps <- getWord32le
+    _msgIarState_num_hyps <- getWord32le
     return MsgIarState {..}
 
   put MsgIarState {..} = do
-    putWord32le msgIarState_num_hyps
+    putWord32le _msgIarState_num_hyps
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgIarState_" . stripPrefix "msgIarState_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgIarState_" . stripPrefix "_msgIarState_"}
              ''MsgIarState)
+$(makeLenses ''MsgIarState)
 
 msgMaskSatellite :: Word16
 msgMaskSatellite = 0x001B
@@ -350,21 +362,22 @@
 -- This message allows setting a mask to prevent a particular satellite from
 -- being used in various Piksi subsystems.
 data MsgMaskSatellite = MsgMaskSatellite
-  { msgMaskSatellite_mask :: Word8
+  { _msgMaskSatellite_mask :: Word8
     -- ^ Mask of systems that should ignore this satellite.
-  , msgMaskSatellite_sid :: Word32
+  , _msgMaskSatellite_sid :: Word32
     -- ^ Signal identifier for which the mask is applied
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgMaskSatellite where
   get = do
-    msgMaskSatellite_mask <- getWord8
-    msgMaskSatellite_sid <- getWord32le
+    _msgMaskSatellite_mask <- getWord8
+    _msgMaskSatellite_sid <- getWord32le
     return MsgMaskSatellite {..}
 
   put MsgMaskSatellite {..} = do
-    putWord8 msgMaskSatellite_mask
-    putWord32le msgMaskSatellite_sid
+    putWord8 _msgMaskSatellite_mask
+    putWord32le _msgMaskSatellite_sid
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgMaskSatellite_" . stripPrefix "msgMaskSatellite_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgMaskSatellite_" . stripPrefix "_msgMaskSatellite_"}
              ''MsgMaskSatellite)
+$(makeLenses ''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
@@ -16,7 +16,7 @@
 module SwiftNav.SBP.Settings where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -46,8 +46,9 @@
   put MsgSettingsSave =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsSave_" . stripPrefix "msgSettingsSave_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgSettingsSave_" . stripPrefix "_msgSettingsSave_"}
              ''MsgSettingsSave)
+$(makeLenses ''MsgSettingsSave)
 
 msgSettingsWrite :: Word16
 msgSettingsWrite = 0x00A0
@@ -56,21 +57,22 @@
 --
 -- The setting message writes the device's configuration.
 data MsgSettingsWrite = MsgSettingsWrite
-  { msgSettingsWrite_setting :: ByteString
+  { _msgSettingsWrite_setting :: ByteString
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
     -- SETTING, VALUE].
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSettingsWrite where
   get = do
-    msgSettingsWrite_setting <- liftM toStrict getRemainingLazyByteString
+    _msgSettingsWrite_setting <- liftM toStrict getRemainingLazyByteString
     return MsgSettingsWrite {..}
 
   put MsgSettingsWrite {..} = do
-    putByteString msgSettingsWrite_setting
+    putByteString _msgSettingsWrite_setting
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsWrite_" . stripPrefix "msgSettingsWrite_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgSettingsWrite_" . stripPrefix "_msgSettingsWrite_"}
              ''MsgSettingsWrite)
+$(makeLenses ''MsgSettingsWrite)
 
 msgSettingsReadReq :: Word16
 msgSettingsReadReq = 0x00A4
@@ -79,21 +81,22 @@
 --
 -- The setting message reads the device's configuration.
 data MsgSettingsReadReq = MsgSettingsReadReq
-  { msgSettingsReadReq_setting :: ByteString
+  { _msgSettingsReadReq_setting :: ByteString
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
     -- SETTING].
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSettingsReadReq where
   get = do
-    msgSettingsReadReq_setting <- liftM toStrict getRemainingLazyByteString
+    _msgSettingsReadReq_setting <- liftM toStrict getRemainingLazyByteString
     return MsgSettingsReadReq {..}
 
   put MsgSettingsReadReq {..} = do
-    putByteString msgSettingsReadReq_setting
+    putByteString _msgSettingsReadReq_setting
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsReadReq_" . stripPrefix "msgSettingsReadReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgSettingsReadReq_" . stripPrefix "_msgSettingsReadReq_"}
              ''MsgSettingsReadReq)
+$(makeLenses ''MsgSettingsReadReq)
 
 msgSettingsReadResp :: Word16
 msgSettingsReadResp = 0x00A5
@@ -102,21 +105,22 @@
 --
 -- The setting message reads the device's configuration.
 data MsgSettingsReadResp = MsgSettingsReadResp
-  { msgSettingsReadResp_setting :: ByteString
+  { _msgSettingsReadResp_setting :: ByteString
     -- ^ 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 <- liftM toStrict getRemainingLazyByteString
     return MsgSettingsReadResp {..}
 
   put MsgSettingsReadResp {..} = do
-    putByteString msgSettingsReadResp_setting
+    putByteString _msgSettingsReadResp_setting
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsReadResp_" . stripPrefix "msgSettingsReadResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgSettingsReadResp_" . stripPrefix "_msgSettingsReadResp_"}
              ''MsgSettingsReadResp)
+$(makeLenses ''MsgSettingsReadResp)
 
 msgSettingsReadByIndexReq :: Word16
 msgSettingsReadByIndexReq = 0x00A2
@@ -127,21 +131,22 @@
 -- the setting at an index, returning a NULL-terminated and delimited string
 -- with contents [SECTION_SETTING, SETTING, VALUE].
 data MsgSettingsReadByIndexReq = MsgSettingsReadByIndexReq
-  { msgSettingsReadByIndexReq_index :: Word16
+  { _msgSettingsReadByIndexReq_index :: Word16
     -- ^ An index into the device settings, with values ranging from 0 to
     -- length(settings)
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSettingsReadByIndexReq where
   get = do
-    msgSettingsReadByIndexReq_index <- getWord16le
+    _msgSettingsReadByIndexReq_index <- getWord16le
     return MsgSettingsReadByIndexReq {..}
 
   put MsgSettingsReadByIndexReq {..} = do
-    putWord16le msgSettingsReadByIndexReq_index
+    putWord16le _msgSettingsReadByIndexReq_index
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsReadByIndexReq_" . stripPrefix "msgSettingsReadByIndexReq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgSettingsReadByIndexReq_" . stripPrefix "_msgSettingsReadByIndexReq_"}
              ''MsgSettingsReadByIndexReq)
+$(makeLenses ''MsgSettingsReadByIndexReq)
 
 msgSettingsReadByIndexResp :: Word16
 msgSettingsReadByIndexResp = 0x00A7
@@ -152,26 +157,27 @@
 -- the setting at an index, returning a NULL-terminated and delimited string
 -- with contents [SECTION_SETTING, SETTING, VALUE].
 data MsgSettingsReadByIndexResp = MsgSettingsReadByIndexResp
-  { msgSettingsReadByIndexResp_index  :: Word16
+  { _msgSettingsReadByIndexResp_index :: Word16
     -- ^ An index into the device settings, with values ranging from 0 to
     -- length(settings)
-  , msgSettingsReadByIndexResp_setting :: ByteString
+  , _msgSettingsReadByIndexResp_setting :: ByteString
     -- ^ A NULL-terminated and delimited string with contents [SECTION_SETTING,
     -- SETTING, VALUE].
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgSettingsReadByIndexResp where
   get = do
-    msgSettingsReadByIndexResp_index <- getWord16le
-    msgSettingsReadByIndexResp_setting <- liftM toStrict getRemainingLazyByteString
+    _msgSettingsReadByIndexResp_index <- getWord16le
+    _msgSettingsReadByIndexResp_setting <- liftM toStrict getRemainingLazyByteString
     return MsgSettingsReadByIndexResp {..}
 
   put MsgSettingsReadByIndexResp {..} = do
-    putWord16le msgSettingsReadByIndexResp_index
-    putByteString msgSettingsReadByIndexResp_setting
+    putWord16le _msgSettingsReadByIndexResp_index
+    putByteString _msgSettingsReadByIndexResp_setting
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsReadByIndexResp_" . stripPrefix "msgSettingsReadByIndexResp_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgSettingsReadByIndexResp_" . stripPrefix "_msgSettingsReadByIndexResp_"}
              ''MsgSettingsReadByIndexResp)
+$(makeLenses ''MsgSettingsReadByIndexResp)
 
 msgSettingsReadByIndexDone :: Word16
 msgSettingsReadByIndexDone = 0x00A6
@@ -189,5 +195,6 @@
   put MsgSettingsReadByIndexDone =
     return ()
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgSettingsReadByIndexDone_" . stripPrefix "msgSettingsReadByIndexDone_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgSettingsReadByIndexDone_" . stripPrefix "_msgSettingsReadByIndexDone_"}
              ''MsgSettingsReadByIndexDone)
+$(makeLenses ''MsgSettingsReadByIndexDone)
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
@@ -11,7 +11,7 @@
 module SwiftNav.SBP.System where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -33,20 +33,21 @@
 -- 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_reserved :: Word32
+  { _msgStartup_reserved :: Word32
     -- ^ Reserved
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgStartup where
   get = do
-    msgStartup_reserved <- getWord32le
+    _msgStartup_reserved <- getWord32le
     return MsgStartup {..}
 
   put MsgStartup {..} = do
-    putWord32le msgStartup_reserved
+    putWord32le _msgStartup_reserved
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgStartup_" . stripPrefix "msgStartup_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgStartup_" . stripPrefix "_msgStartup_"}
              ''MsgStartup)
+$(makeLenses ''MsgStartup)
 
 msgHeartbeat :: Word16
 msgHeartbeat = 0xFFFF
@@ -61,17 +62,18 @@
 -- 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
+  { _msgHeartbeat_flags :: Word32
     -- ^ Status flags
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgHeartbeat where
   get = do
-    msgHeartbeat_flags <- getWord32le
+    _msgHeartbeat_flags <- getWord32le
     return MsgHeartbeat {..}
 
   put MsgHeartbeat {..} = do
-    putWord32le msgHeartbeat_flags
+    putWord32le _msgHeartbeat_flags
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgHeartbeat_" . stripPrefix "msgHeartbeat_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgHeartbeat_" . stripPrefix "_msgHeartbeat_"}
              ''MsgHeartbeat)
+$(makeLenses ''MsgHeartbeat)
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
@@ -11,7 +11,7 @@
 module SwiftNav.SBP.Tracking where
 
 import BasicPrelude
-import Control.Monad
+import Control.Lens
 import Control.Monad.Loops
 import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
@@ -29,30 +29,31 @@
 -- Tracking channel state for a specific satellite PRN and measured signal
 -- power.
 data TrackingChannelState = TrackingChannelState
-  { trackingChannelState_state :: Word8
+  { _trackingChannelState_state :: Word8
     -- ^ Status of tracking channel
-  , trackingChannelState_sid  :: Word32
+  , _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_cn0  :: Float
+  , _trackingChannelState_cn0 :: Float
     -- ^ Carrier-to-noise density
   } deriving ( Show, Read, Eq )
 
 instance Binary TrackingChannelState where
   get = do
-    trackingChannelState_state <- getWord8
-    trackingChannelState_sid <- getWord32le
-    trackingChannelState_cn0 <- getFloat32le
+    _trackingChannelState_state <- getWord8
+    _trackingChannelState_sid <- getWord32le
+    _trackingChannelState_cn0 <- getFloat32le
     return TrackingChannelState {..}
 
   put TrackingChannelState {..} = do
-    putWord8 trackingChannelState_state
-    putWord32le trackingChannelState_sid
-    putFloat32le trackingChannelState_cn0
+    putWord8 _trackingChannelState_state
+    putWord32le _trackingChannelState_sid
+    putFloat32le _trackingChannelState_cn0
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "trackingChannelState_" . stripPrefix "trackingChannelState_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_trackingChannelState_" . stripPrefix "_trackingChannelState_"}
              ''TrackingChannelState)
+$(makeLenses ''TrackingChannelState)
 
 msgTrackingState :: Word16
 msgTrackingState = 0x0013
@@ -63,43 +64,45 @@
 -- states. It reports status and snr power measurements for all tracked
 -- satellites.
 data MsgTrackingState = MsgTrackingState
-  { msgTrackingState_states :: [TrackingChannelState]
+  { _msgTrackingState_states :: [TrackingChannelState]
     -- ^ Satellite tracking channel state
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgTrackingState where
   get = do
-    msgTrackingState_states <- whileM (liftM not isEmpty) get
+    _msgTrackingState_states <- whileM (liftM not isEmpty) get
     return MsgTrackingState {..}
 
   put MsgTrackingState {..} = do
-    mapM_ put msgTrackingState_states
+    mapM_ put _msgTrackingState_states
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgTrackingState_" . stripPrefix "msgTrackingState_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgTrackingState_" . stripPrefix "_msgTrackingState_"}
              ''MsgTrackingState)
+$(makeLenses ''MsgTrackingState)
 
 -- | TrackingChannelCorrelation.
 --
 -- Structure containing in-phase and quadrature correlation components.
 data TrackingChannelCorrelation = TrackingChannelCorrelation
-  { trackingChannelCorrelation_I :: Int32
+  { _trackingChannelCorrelation_I :: Int32
     -- ^ In-phase correlation
-  , trackingChannelCorrelation_Q :: Int32
+  , _trackingChannelCorrelation_Q :: Int32
     -- ^ Quadrature correlation
   } deriving ( Show, Read, Eq )
 
 instance Binary TrackingChannelCorrelation where
   get = do
-    trackingChannelCorrelation_I <- liftM fromIntegral getWord32le
-    trackingChannelCorrelation_Q <- liftM fromIntegral getWord32le
+    _trackingChannelCorrelation_I <- liftM fromIntegral getWord32le
+    _trackingChannelCorrelation_Q <- liftM fromIntegral getWord32le
     return TrackingChannelCorrelation {..}
 
   put TrackingChannelCorrelation {..} = do
-    putWord32le $ fromIntegral trackingChannelCorrelation_I
-    putWord32le $ fromIntegral trackingChannelCorrelation_Q
+    putWord32le $ fromIntegral _trackingChannelCorrelation_I
+    putWord32le $ fromIntegral _trackingChannelCorrelation_Q
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "trackingChannelCorrelation_" . stripPrefix "trackingChannelCorrelation_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_trackingChannelCorrelation_" . stripPrefix "_trackingChannelCorrelation_"}
              ''TrackingChannelCorrelation)
+$(makeLenses ''TrackingChannelCorrelation)
 
 msgTrackingIq :: Word16
 msgTrackingIq = 0x001C
@@ -109,57 +112,59 @@
 -- When enabled, a tracking channel can output the correlations at each update
 -- interval.
 data MsgTrackingIq = MsgTrackingIq
-  { msgTrackingIq_channel :: Word8
+  { _msgTrackingIq_channel :: Word8
     -- ^ Tracking channel of origin
-  , msgTrackingIq_sid    :: Word32
+  , _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_corrs  :: [TrackingChannelCorrelation]
+  , _msgTrackingIq_corrs :: [TrackingChannelCorrelation]
     -- ^ Early, Prompt and Late correlations
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgTrackingIq where
   get = do
-    msgTrackingIq_channel <- getWord8
-    msgTrackingIq_sid <- getWord32le
-    msgTrackingIq_corrs <- replicateM 3 get
+    _msgTrackingIq_channel <- getWord8
+    _msgTrackingIq_sid <- getWord32le
+    _msgTrackingIq_corrs <- replicateM 3 get
     return MsgTrackingIq {..}
 
   put MsgTrackingIq {..} = do
-    putWord8 msgTrackingIq_channel
-    putWord32le msgTrackingIq_sid
-    mapM_ put msgTrackingIq_corrs
+    putWord8 _msgTrackingIq_channel
+    putWord32le _msgTrackingIq_sid
+    mapM_ put _msgTrackingIq_corrs
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgTrackingIq_" . stripPrefix "msgTrackingIq_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgTrackingIq_" . stripPrefix "_msgTrackingIq_"}
              ''MsgTrackingIq)
+$(makeLenses ''MsgTrackingIq)
 
 -- | TrackingChannelStateDepA.
 --
 -- Deprecated.
 data TrackingChannelStateDepA = TrackingChannelStateDepA
-  { trackingChannelStateDepA_state :: Word8
+  { _trackingChannelStateDepA_state :: Word8
     -- ^ Status of tracking channel
-  , trackingChannelStateDepA_prn  :: Word8
+  , _trackingChannelStateDepA_prn :: Word8
     -- ^ PRN-1 being tracked
-  , trackingChannelStateDepA_cn0  :: Float
+  , _trackingChannelStateDepA_cn0 :: Float
     -- ^ Carrier-to-noise density
   } deriving ( Show, Read, Eq )
 
 instance Binary TrackingChannelStateDepA where
   get = do
-    trackingChannelStateDepA_state <- getWord8
-    trackingChannelStateDepA_prn <- getWord8
-    trackingChannelStateDepA_cn0 <- getFloat32le
+    _trackingChannelStateDepA_state <- getWord8
+    _trackingChannelStateDepA_prn <- getWord8
+    _trackingChannelStateDepA_cn0 <- getFloat32le
     return TrackingChannelStateDepA {..}
 
   put TrackingChannelStateDepA {..} = do
-    putWord8 trackingChannelStateDepA_state
-    putWord8 trackingChannelStateDepA_prn
-    putFloat32le trackingChannelStateDepA_cn0
+    putWord8 _trackingChannelStateDepA_state
+    putWord8 _trackingChannelStateDepA_prn
+    putFloat32le _trackingChannelStateDepA_cn0
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "trackingChannelStateDepA_" . stripPrefix "trackingChannelStateDepA_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_trackingChannelStateDepA_" . stripPrefix "_trackingChannelStateDepA_"}
              ''TrackingChannelStateDepA)
+$(makeLenses ''TrackingChannelStateDepA)
 
 msgTrackingStateDepA :: Word16
 msgTrackingStateDepA = 0x0016
@@ -168,17 +173,18 @@
 --
 -- Deprecated.
 data MsgTrackingStateDepA = MsgTrackingStateDepA
-  { msgTrackingStateDepA_states :: [TrackingChannelStateDepA]
+  { _msgTrackingStateDepA_states :: [TrackingChannelStateDepA]
     -- ^ Satellite tracking channel state
   } deriving ( Show, Read, Eq )
 
 instance Binary MsgTrackingStateDepA where
   get = do
-    msgTrackingStateDepA_states <- whileM (liftM not isEmpty) get
+    _msgTrackingStateDepA_states <- whileM (liftM not isEmpty) get
     return MsgTrackingStateDepA {..}
 
   put MsgTrackingStateDepA {..} = do
-    mapM_ put msgTrackingStateDepA_states
+    mapM_ put _msgTrackingStateDepA_states
 
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "msgTrackingStateDepA_" . stripPrefix "msgTrackingStateDepA_"}
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_msgTrackingStateDepA_" . stripPrefix "_msgTrackingStateDepA_"}
              ''MsgTrackingStateDepA)
+$(makeLenses ''MsgTrackingStateDepA)
