diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,14 +2,6 @@
 
 Haskell client for Swift Binary Protocol (SBP).
 
-## Requirements
-
-`haskell-stack`: [Instructions here](https://github.com/commercialhaskell/stack/blob/master/doc/install_and_upgrade.md).
-
-## Install from Hackage
-
-Available on [Hackage as `sbp`](http://hackage.haskell.org/package/sbp).
-
 ## Setup
 
 To build:
diff --git a/main/JSON2SBP.hs b/main/JSON2SBP.hs
--- a/main/JSON2SBP.hs
+++ b/main/JSON2SBP.hs
@@ -9,13 +9,14 @@
 -- JSON to SBP tool - reads SBP JSON from stdin and sends SBP JSON to
 -- stdout.
 
-import           BasicPrelude hiding (lines)
+import           BasicPrelude
 import           Control.Monad.Trans.Resource
 import           Data.Aeson
 import           Data.Conduit
 import           Data.Conduit.Binary
 import qualified Data.Conduit.List as CL
 import           Data.Conduit.Serialization.Binary
+import qualified Data.Conduit.Text as CT
 import           SwiftNav.SBP
 import           System.IO
 
@@ -32,10 +33,8 @@
 decodeSBPMsg v = decodeStrict v <|> sbpMsgData <$> decodeStrict v
 
 main :: IO ()
-main =
-  runResourceT $
-    sourceHandle stdin       =$=
-    lines                    =$=
-    CL.mapMaybe decodeSBPMsg =$=
-    conduitEncode            $$
-    sinkHandle stdout
+main = runResourceT $
+  sourceHandle stdin =$=
+  CT.decode CT.utf8 =$= CT.lines =$= CL.mapMaybe (decodeSBPMsg . encodeUtf8) =$=
+  conduitEncode $$
+  sinkHandle stdout
diff --git a/main/SBP2JSON.hs b/main/SBP2JSON.hs
--- a/main/SBP2JSON.hs
+++ b/main/SBP2JSON.hs
@@ -24,12 +24,12 @@
 
 -- | Encode a SBPMsg to a line of JSON.
 encodeLine :: SBPMsg -> ByteString
-encodeLine v = BL.toStrict $ toLazyByteString $ encodeToBuilder (toJSON v) <> "\n"
+encodeLine v = BL.toStrict $ toLazyByteString $
+  encodeToByteStringBuilder (toJSON v) <> "\n"
 
 main :: IO ()
-main =
-  runResourceT $
-    sourceHandle stdin  =$=
-      conduitDecode     =$=
-      CL.map encodeLine $$
-      sinkHandle stdout
+main = runResourceT $
+  sourceHandle stdin =$=
+  conduitDecode =$=
+  CL.map encodeLine $$
+  sinkHandle stdout
diff --git a/main/SBP2YAML.hs b/main/SBP2YAML.hs
--- a/main/SBP2YAML.hs
+++ b/main/SBP2YAML.hs
@@ -20,9 +20,8 @@
 import           System.IO
 
 main :: IO ()
-main =
-  runResourceT $
-    sourceHandle stdin                      =$=
-    conduitDecode                           =$=
-    CL.map (encode :: SBPMsg -> ByteString) $$
-    sinkHandle stdout
+main = runResourceT $
+  sourceHandle stdin =$=
+  conduitDecode =$=
+  CL.map (encode :: SBPMsg -> ByteString) $$
+  sinkHandle stdout
diff --git a/sbp.cabal b/sbp.cabal
--- a/sbp.cabal
+++ b/sbp.cabal
@@ -1,5 +1,5 @@
 name:                  sbp
-version:               0.51.11
+version:               0.52.0
 synopsis:              SwiftNav's SBP Library
 homepage:              https://github.com/swift-nav/libsbp
 license:               LGPL-3
@@ -137,7 +137,18 @@
   hs-source-dirs:      test
   main-is:             Test.hs
   other-modules:       Test.SwiftNav.CRC16
+                     , Test.SwiftNav.SBP.Acquisition
+                     , Test.SwiftNav.SBP.Bootload
                      , Test.SwiftNav.SBP.Encoding
+                     , Test.SwiftNav.SBP.ExtEvents
+                     , Test.SwiftNav.SBP.Logging
+                     , Test.SwiftNav.SBP.Navigation
+                     , Test.SwiftNav.SBP.Observation
+                     , Test.SwiftNav.SBP.Piksi
+                     , Test.SwiftNav.SBP.System
+                     , Test.SwiftNav.SBP.Table
+                     , Test.SwiftNav.SBP.Tracking
+                     , Test.SwiftNav.SBP.Utils
   build-depends:       aeson
                      , base
                      , base64-bytestring
@@ -148,6 +159,7 @@
                      , tasty
                      , tasty-hunit
                      , tasty-quickcheck
+                     , yaml-light
   ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall
   default-language:    Haskell2010
   default-extensions:  NoImplicitPrelude
diff --git a/src/SwiftNav/CRC16.hs b/src/SwiftNav/CRC16.hs
--- a/src/SwiftNav/CRC16.hs
+++ b/src/SwiftNav/CRC16.hs
@@ -12,7 +12,7 @@
   ( crc16
   ) where
 
-import BasicPrelude         hiding (ByteString, foldl')
+import BasicPrelude hiding ( ByteString, foldl' )
 import Data.Array
 import Data.Bits
 import Data.ByteString.Lazy
diff --git a/src/SwiftNav/SBP.hs b/src/SwiftNav/SBP.hs
--- a/src/SwiftNav/SBP.hs
+++ b/src/SwiftNav/SBP.hs
@@ -27,11 +27,16 @@
   , module SwiftNav.SBP.User
   ) where
 
-import BasicPrelude             hiding (lookup)
-import Control.Lens             hiding ((.=))
-import Data.Aeson               hiding (decode, decode')
+import BasicPrelude hiding (lookup)
+import Control.Lens hiding ((.=))
+import Data.Aeson hiding (decode, decode')
 import Data.Binary
-import Data.ByteString.Lazy     hiding (ByteString)
+import Data.Binary.Get
+import Data.Binary.Put
+import Data.ByteString.Lazy hiding (ByteString)
+import Data.ByteString.Builder
+import Data.HashMap.Strict
+import SwiftNav.CRC16
 import SwiftNav.SBP.Acquisition
 import SwiftNav.SBP.Bootload
 import SwiftNav.SBP.ExtEvents
@@ -127,160 +132,160 @@
    | SBPMsgUnknown Msg
   deriving ( Show, Read, Eq )
 
-$(makePrisms ''SBPMsg)
-
 instance Binary SBPMsg where
   get = do
     preamble <- getWord8
     if preamble /= msgSBPPreamble then get else do
-      decode' <$> get where
-        decode' m@Msg {..}
-          | checkCrc m /= _msgSBPCrc = SBPMsgBadCrc m
-          | _msgSBPType == msgAcqResult = SBPMsgAcqResult (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgAcqResultDepA = SBPMsgAcqResultDepA (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgAlmanac = SBPMsgAlmanac (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBasePosEcef = SBPMsgBasePosEcef (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBasePosLlh = SBPMsgBasePosLlh (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBaselineEcef = SBPMsgBaselineEcef (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBaselineHeading = SBPMsgBaselineHeading (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBaselineNed = SBPMsgBaselineNed (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBootloaderHandshakeDepA = SBPMsgBootloaderHandshakeDepA (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBootloaderHandshakeReq = SBPMsgBootloaderHandshakeReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBootloaderHandshakeResp = SBPMsgBootloaderHandshakeResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgBootloaderJumpToApp = SBPMsgBootloaderJumpToApp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgCwResults = SBPMsgCwResults (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgCwStart = SBPMsgCwStart (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgDops = SBPMsgDops (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgEphemeris = SBPMsgEphemeris (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgEphemerisDepA = SBPMsgEphemerisDepA (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgEphemerisDepB = SBPMsgEphemerisDepB (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgExtEvent = SBPMsgExtEvent (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFileioReadDirReq = SBPMsgFileioReadDirReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFileioReadDirResp = SBPMsgFileioReadDirResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFileioReadReq = SBPMsgFileioReadReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFileioReadResp = SBPMsgFileioReadResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFileioRemove = SBPMsgFileioRemove (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFileioWriteReq = SBPMsgFileioWriteReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFileioWriteResp = SBPMsgFileioWriteResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFlashDone = SBPMsgFlashDone (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFlashErase = SBPMsgFlashErase (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFlashProgram = SBPMsgFlashProgram (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFlashReadReq = SBPMsgFlashReadReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgFlashReadResp = SBPMsgFlashReadResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgGpsTime = SBPMsgGpsTime (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgHeartbeat = SBPMsgHeartbeat (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgIarState = SBPMsgIarState (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgInitBase = SBPMsgInitBase (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgLog = SBPMsgLog (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgM25FlashWriteStatus = SBPMsgM25FlashWriteStatus (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgMaskSatellite = SBPMsgMaskSatellite (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgObs = SBPMsgObs (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgObsDepA = SBPMsgObsDepA (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgPosEcef = SBPMsgPosEcef (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgPosLlh = SBPMsgPosLlh (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgPrintDep = SBPMsgPrintDep (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgReset = SBPMsgReset (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgResetFilters = SBPMsgResetFilters (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgSetTime = SBPMsgSetTime (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgSettingsReadByIndexDone = SBPMsgSettingsReadByIndexDone (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgSettingsReadByIndexReq = SBPMsgSettingsReadByIndexReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgSettingsReadByIndexResp = SBPMsgSettingsReadByIndexResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgSettingsReadReq = SBPMsgSettingsReadReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgSettingsReadResp = SBPMsgSettingsReadResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgSettingsSave = SBPMsgSettingsSave (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgSettingsWrite = SBPMsgSettingsWrite (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgStartup = SBPMsgStartup (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgStmFlashLockSector = SBPMsgStmFlashLockSector (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgStmFlashUnlockSector = SBPMsgStmFlashUnlockSector (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgStmUniqueIdReq = SBPMsgStmUniqueIdReq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgStmUniqueIdResp = SBPMsgStmUniqueIdResp (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgThreadState = SBPMsgThreadState (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgTrackingIq = SBPMsgTrackingIq (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgTrackingState = SBPMsgTrackingState (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgTrackingStateDepA = SBPMsgTrackingStateDepA (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgTweet = SBPMsgTweet (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgUartState = SBPMsgUartState (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgUserData = SBPMsgUserData (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgVelEcef = SBPMsgVelEcef (decode (fromStrict _msgSBPPayload)) m
-          | _msgSBPType == msgVelNed = SBPMsgVelNed (decode (fromStrict _msgSBPPayload)) m
-          | otherwise = SBPMsgUnknown m
+      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 == msgBasePosEcef = SBPMsgBasePosEcef (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBasePosLlh = SBPMsgBasePosLlh (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBaselineEcef = SBPMsgBaselineEcef (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgBaselineHeading = SBPMsgBaselineHeading (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 == msgUserData = SBPMsgUserData (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgVelEcef = SBPMsgVelEcef (decode (fromStrict _msgSBPPayload)) sbp
+          | _msgSBPType == msgVelNed = SBPMsgVelNed (decode (fromStrict _msgSBPPayload)) sbp
+          | otherwise = SBPMsgUnknown sbp
 
-  put sm = do
+  put msg = do
     putWord8 msgSBPPreamble
-    encode' sm where
-      encode' (SBPMsgAcqResult _ m) = put m
-      encode' (SBPMsgAcqResultDepA _ m) = put m
-      encode' (SBPMsgAlmanac _ m) = put m
-      encode' (SBPMsgBasePosEcef _ m) = put m
-      encode' (SBPMsgBasePosLlh _ m) = put m
-      encode' (SBPMsgBaselineEcef _ m) = put m
-      encode' (SBPMsgBaselineHeading _ m) = put m
-      encode' (SBPMsgBaselineNed _ m) = put m
-      encode' (SBPMsgBootloaderHandshakeDepA _ m) = put m
-      encode' (SBPMsgBootloaderHandshakeReq _ m) = put m
-      encode' (SBPMsgBootloaderHandshakeResp _ m) = put m
-      encode' (SBPMsgBootloaderJumpToApp _ m) = put m
-      encode' (SBPMsgCwResults _ m) = put m
-      encode' (SBPMsgCwStart _ m) = put m
-      encode' (SBPMsgDops _ m) = put m
-      encode' (SBPMsgEphemeris _ m) = put m
-      encode' (SBPMsgEphemerisDepA _ m) = put m
-      encode' (SBPMsgEphemerisDepB _ m) = put m
-      encode' (SBPMsgExtEvent _ m) = put m
-      encode' (SBPMsgFileioReadDirReq _ m) = put m
-      encode' (SBPMsgFileioReadDirResp _ m) = put m
-      encode' (SBPMsgFileioReadReq _ m) = put m
-      encode' (SBPMsgFileioReadResp _ m) = put m
-      encode' (SBPMsgFileioRemove _ m) = put m
-      encode' (SBPMsgFileioWriteReq _ m) = put m
-      encode' (SBPMsgFileioWriteResp _ m) = put m
-      encode' (SBPMsgFlashDone _ m) = put m
-      encode' (SBPMsgFlashErase _ m) = put m
-      encode' (SBPMsgFlashProgram _ m) = put m
-      encode' (SBPMsgFlashReadReq _ m) = put m
-      encode' (SBPMsgFlashReadResp _ m) = put m
-      encode' (SBPMsgGpsTime _ m) = put m
-      encode' (SBPMsgHeartbeat _ m) = put m
-      encode' (SBPMsgIarState _ m) = put m
-      encode' (SBPMsgInitBase _ m) = put m
-      encode' (SBPMsgLog _ m) = put m
-      encode' (SBPMsgM25FlashWriteStatus _ m) = put m
-      encode' (SBPMsgMaskSatellite _ m) = put m
-      encode' (SBPMsgNapDeviceDnaReq _ m) = put m
-      encode' (SBPMsgNapDeviceDnaResp _ m) = put m
-      encode' (SBPMsgObs _ m) = put m
-      encode' (SBPMsgObsDepA _ m) = put m
-      encode' (SBPMsgPosEcef _ m) = put m
-      encode' (SBPMsgPosLlh _ m) = put m
-      encode' (SBPMsgPrintDep _ m) = put m
-      encode' (SBPMsgReset _ m) = put m
-      encode' (SBPMsgResetFilters _ m) = put m
-      encode' (SBPMsgSetTime _ m) = put m
-      encode' (SBPMsgSettingsReadByIndexDone _ m) = put m
-      encode' (SBPMsgSettingsReadByIndexReq _ m) = put m
-      encode' (SBPMsgSettingsReadByIndexResp _ m) = put m
-      encode' (SBPMsgSettingsReadReq _ m) = put m
-      encode' (SBPMsgSettingsReadResp _ m) = put m
-      encode' (SBPMsgSettingsSave _ m) = put m
-      encode' (SBPMsgSettingsWrite _ m) = put m
-      encode' (SBPMsgStartup _ m) = put m
-      encode' (SBPMsgStmFlashLockSector _ m) = put m
-      encode' (SBPMsgStmFlashUnlockSector _ m) = put m
-      encode' (SBPMsgStmUniqueIdReq _ m) = put m
-      encode' (SBPMsgStmUniqueIdResp _ m) = put m
-      encode' (SBPMsgThreadState _ m) = put m
-      encode' (SBPMsgTrackingIq _ m) = put m
-      encode' (SBPMsgTrackingState _ m) = put m
-      encode' (SBPMsgTrackingStateDepA _ m) = put m
-      encode' (SBPMsgTweet _ m) = put m
-      encode' (SBPMsgUartState _ m) = put m
-      encode' (SBPMsgUserData _ m) = put m
-      encode' (SBPMsgVelEcef _ m) = put m
-      encode' (SBPMsgVelNed _ m) = put m
-      encode' (SBPMsgUnknown m) = put m
-      encode' (SBPMsgBadCrc m) = put m
+    put $ encode' msg
+    where
+      encode' (SBPMsgAcqResult _ sbp) = sbp
+      encode' (SBPMsgAcqResultDepA _ sbp) = sbp
+      encode' (SBPMsgAlmanac _ sbp) = sbp
+      encode' (SBPMsgBasePosEcef _ sbp) = sbp
+      encode' (SBPMsgBasePosLlh _ sbp) = sbp
+      encode' (SBPMsgBaselineEcef _ sbp) = sbp
+      encode' (SBPMsgBaselineHeading _ sbp) = sbp
+      encode' (SBPMsgBaselineNed _ sbp) = sbp
+      encode' (SBPMsgBootloaderHandshakeDepA _ sbp) = sbp
+      encode' (SBPMsgBootloaderHandshakeReq _ sbp) = sbp
+      encode' (SBPMsgBootloaderHandshakeResp _ sbp) = sbp
+      encode' (SBPMsgBootloaderJumpToApp _ sbp) = sbp
+      encode' (SBPMsgCwResults _ sbp) = sbp
+      encode' (SBPMsgCwStart _ sbp) = sbp
+      encode' (SBPMsgDops _ sbp) = sbp
+      encode' (SBPMsgEphemeris _ sbp) = sbp
+      encode' (SBPMsgEphemerisDepA _ sbp) = sbp
+      encode' (SBPMsgEphemerisDepB _ sbp) = sbp
+      encode' (SBPMsgExtEvent _ sbp) = sbp
+      encode' (SBPMsgFileioReadDirReq _ sbp) = sbp
+      encode' (SBPMsgFileioReadDirResp _ sbp) = sbp
+      encode' (SBPMsgFileioReadReq _ sbp) = sbp
+      encode' (SBPMsgFileioReadResp _ sbp) = sbp
+      encode' (SBPMsgFileioRemove _ sbp) = sbp
+      encode' (SBPMsgFileioWriteReq _ sbp) = sbp
+      encode' (SBPMsgFileioWriteResp _ sbp) = sbp
+      encode' (SBPMsgFlashDone _ sbp) = sbp
+      encode' (SBPMsgFlashErase _ sbp) = sbp
+      encode' (SBPMsgFlashProgram _ sbp) = sbp
+      encode' (SBPMsgFlashReadReq _ sbp) = sbp
+      encode' (SBPMsgFlashReadResp _ sbp) = sbp
+      encode' (SBPMsgGpsTime _ sbp) = sbp
+      encode' (SBPMsgHeartbeat _ sbp) = sbp
+      encode' (SBPMsgIarState _ sbp) = sbp
+      encode' (SBPMsgInitBase _ sbp) = sbp
+      encode' (SBPMsgLog _ sbp) = sbp
+      encode' (SBPMsgM25FlashWriteStatus _ sbp) = sbp
+      encode' (SBPMsgMaskSatellite _ sbp) = sbp
+      encode' (SBPMsgNapDeviceDnaReq _ sbp) = sbp
+      encode' (SBPMsgNapDeviceDnaResp _ sbp) = sbp
+      encode' (SBPMsgObs _ sbp) = sbp
+      encode' (SBPMsgObsDepA _ sbp) = sbp
+      encode' (SBPMsgPosEcef _ sbp) = sbp
+      encode' (SBPMsgPosLlh _ sbp) = sbp
+      encode' (SBPMsgPrintDep _ sbp) = sbp
+      encode' (SBPMsgReset _ sbp) = sbp
+      encode' (SBPMsgResetFilters _ sbp) = sbp
+      encode' (SBPMsgSetTime _ sbp) = sbp
+      encode' (SBPMsgSettingsReadByIndexDone _ sbp) = sbp
+      encode' (SBPMsgSettingsReadByIndexReq _ sbp) = sbp
+      encode' (SBPMsgSettingsReadByIndexResp _ sbp) = sbp
+      encode' (SBPMsgSettingsReadReq _ sbp) = sbp
+      encode' (SBPMsgSettingsReadResp _ sbp) = sbp
+      encode' (SBPMsgSettingsSave _ sbp) = sbp
+      encode' (SBPMsgSettingsWrite _ sbp) = sbp
+      encode' (SBPMsgStartup _ sbp) = sbp
+      encode' (SBPMsgStmFlashLockSector _ sbp) = sbp
+      encode' (SBPMsgStmFlashUnlockSector _ sbp) = sbp
+      encode' (SBPMsgStmUniqueIdReq _ sbp) = sbp
+      encode' (SBPMsgStmUniqueIdResp _ sbp) = sbp
+      encode' (SBPMsgThreadState _ sbp) = sbp
+      encode' (SBPMsgTrackingIq _ sbp) = sbp
+      encode' (SBPMsgTrackingState _ sbp) = sbp
+      encode' (SBPMsgTrackingStateDepA _ sbp) = sbp
+      encode' (SBPMsgTweet _ sbp) = sbp
+      encode' (SBPMsgUartState _ sbp) = sbp
+      encode' (SBPMsgUserData _ sbp) = sbp
+      encode' (SBPMsgVelEcef _ sbp) = sbp
+      encode' (SBPMsgVelNed _ sbp) = sbp
+      encode' (SBPMsgUnknown sbp) = sbp
+      encode' (SBPMsgBadCrc sbp) = sbp
 
 instance FromJSON SBPMsg where
   parseJSON obj@(Object o) = do
@@ -359,154 +364,81 @@
         | otherwise = SBPMsgUnknown <$> parseJSON obj
   parseJSON _ = mzero
 
-mergeValues :: Value -> Value -> Value
-mergeValues (Object a) (Object b) = Object (a <> b)
-mergeValues (Object a) _          = Object a
-mergeValues _          (Object b) = Object b
-mergeValues _          v          = v
+merge :: Value -> Value -> Value
+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 n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgAcqResultDepA n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgAlmanac n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBasePosEcef n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBasePosLlh n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBaselineEcef n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBaselineHeading n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBaselineNed n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBootloaderHandshakeDepA n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBootloaderHandshakeReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBootloaderHandshakeResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBootloaderJumpToApp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgCwResults n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgCwStart n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgDops n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgEphemeris n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgEphemerisDepA n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgEphemerisDepB n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgExtEvent n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFileioReadDirReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFileioReadDirResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFileioReadReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFileioReadResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFileioRemove n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFileioWriteReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFileioWriteResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFlashDone n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFlashErase n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFlashProgram n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFlashReadReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgFlashReadResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgGpsTime n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgHeartbeat n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgIarState n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgInitBase n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgLog n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgM25FlashWriteStatus n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgMaskSatellite n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgNapDeviceDnaReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgNapDeviceDnaResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgObs n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgObsDepA n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgPosEcef n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgPosLlh n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgPrintDep n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgReset n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgResetFilters n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgSetTime n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgSettingsReadByIndexDone n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgSettingsReadByIndexReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgSettingsReadByIndexResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgSettingsReadReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgSettingsReadResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgSettingsSave n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgSettingsWrite n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgStartup n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgStmFlashLockSector n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgStmFlashUnlockSector n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgStmUniqueIdReq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgStmUniqueIdResp n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgThreadState n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgTrackingIq n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgTrackingState n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgTrackingStateDepA n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgTweet n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgUartState n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgUserData n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgVelEcef n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgVelNed n m) = toJSON n `mergeValues` toJSON m
-  toJSON (SBPMsgBadCrc m) = toJSON m
-  toJSON (SBPMsgUnknown m) = toJSON m
-
-instance HasMsg SBPMsg where
-  msg f (SBPMsgAcqResult n m) = SBPMsgAcqResult n <$> f m
-  msg f (SBPMsgAcqResultDepA n m) = SBPMsgAcqResultDepA n <$> f m
-  msg f (SBPMsgAlmanac n m) = SBPMsgAlmanac n <$> f m
-  msg f (SBPMsgBasePosEcef n m) = SBPMsgBasePosEcef n <$> f m
-  msg f (SBPMsgBasePosLlh n m) = SBPMsgBasePosLlh n <$> f m
-  msg f (SBPMsgBaselineEcef n m) = SBPMsgBaselineEcef n <$> f m
-  msg f (SBPMsgBaselineHeading n m) = SBPMsgBaselineHeading n <$> f m
-  msg f (SBPMsgBaselineNed n m) = SBPMsgBaselineNed n <$> f m
-  msg f (SBPMsgBootloaderHandshakeDepA n m) = SBPMsgBootloaderHandshakeDepA n <$> f m
-  msg f (SBPMsgBootloaderHandshakeReq n m) = SBPMsgBootloaderHandshakeReq n <$> f m
-  msg f (SBPMsgBootloaderHandshakeResp n m) = SBPMsgBootloaderHandshakeResp n <$> f m
-  msg f (SBPMsgBootloaderJumpToApp n m) = SBPMsgBootloaderJumpToApp n <$> f m
-  msg f (SBPMsgCwResults n m) = SBPMsgCwResults n <$> f m
-  msg f (SBPMsgCwStart n m) = SBPMsgCwStart n <$> f m
-  msg f (SBPMsgDops n m) = SBPMsgDops n <$> f m
-  msg f (SBPMsgEphemeris n m) = SBPMsgEphemeris n <$> f m
-  msg f (SBPMsgEphemerisDepA n m) = SBPMsgEphemerisDepA n <$> f m
-  msg f (SBPMsgEphemerisDepB n m) = SBPMsgEphemerisDepB n <$> f m
-  msg f (SBPMsgExtEvent n m) = SBPMsgExtEvent n <$> f m
-  msg f (SBPMsgFileioReadDirReq n m) = SBPMsgFileioReadDirReq n <$> f m
-  msg f (SBPMsgFileioReadDirResp n m) = SBPMsgFileioReadDirResp n <$> f m
-  msg f (SBPMsgFileioReadReq n m) = SBPMsgFileioReadReq n <$> f m
-  msg f (SBPMsgFileioReadResp n m) = SBPMsgFileioReadResp n <$> f m
-  msg f (SBPMsgFileioRemove n m) = SBPMsgFileioRemove n <$> f m
-  msg f (SBPMsgFileioWriteReq n m) = SBPMsgFileioWriteReq n <$> f m
-  msg f (SBPMsgFileioWriteResp n m) = SBPMsgFileioWriteResp n <$> f m
-  msg f (SBPMsgFlashDone n m) = SBPMsgFlashDone n <$> f m
-  msg f (SBPMsgFlashErase n m) = SBPMsgFlashErase n <$> f m
-  msg f (SBPMsgFlashProgram n m) = SBPMsgFlashProgram n <$> f m
-  msg f (SBPMsgFlashReadReq n m) = SBPMsgFlashReadReq n <$> f m
-  msg f (SBPMsgFlashReadResp n m) = SBPMsgFlashReadResp n <$> f m
-  msg f (SBPMsgGpsTime n m) = SBPMsgGpsTime n <$> f m
-  msg f (SBPMsgHeartbeat n m) = SBPMsgHeartbeat n <$> f m
-  msg f (SBPMsgIarState n m) = SBPMsgIarState n <$> f m
-  msg f (SBPMsgInitBase n m) = SBPMsgInitBase n <$> f m
-  msg f (SBPMsgLog n m) = SBPMsgLog n <$> f m
-  msg f (SBPMsgM25FlashWriteStatus n m) = SBPMsgM25FlashWriteStatus n <$> f m
-  msg f (SBPMsgMaskSatellite n m) = SBPMsgMaskSatellite n <$> f m
-  msg f (SBPMsgNapDeviceDnaReq n m) = SBPMsgNapDeviceDnaReq n <$> f m
-  msg f (SBPMsgNapDeviceDnaResp n m) = SBPMsgNapDeviceDnaResp n <$> f m
-  msg f (SBPMsgObs n m) = SBPMsgObs n <$> f m
-  msg f (SBPMsgObsDepA n m) = SBPMsgObsDepA n <$> f m
-  msg f (SBPMsgPosEcef n m) = SBPMsgPosEcef n <$> f m
-  msg f (SBPMsgPosLlh n m) = SBPMsgPosLlh n <$> f m
-  msg f (SBPMsgPrintDep n m) = SBPMsgPrintDep n <$> f m
-  msg f (SBPMsgReset n m) = SBPMsgReset n <$> f m
-  msg f (SBPMsgResetFilters n m) = SBPMsgResetFilters n <$> f m
-  msg f (SBPMsgSetTime n m) = SBPMsgSetTime n <$> f m
-  msg f (SBPMsgSettingsReadByIndexDone n m) = SBPMsgSettingsReadByIndexDone n <$> f m
-  msg f (SBPMsgSettingsReadByIndexReq n m) = SBPMsgSettingsReadByIndexReq n <$> f m
-  msg f (SBPMsgSettingsReadByIndexResp n m) = SBPMsgSettingsReadByIndexResp n <$> f m
-  msg f (SBPMsgSettingsReadReq n m) = SBPMsgSettingsReadReq n <$> f m
-  msg f (SBPMsgSettingsReadResp n m) = SBPMsgSettingsReadResp n <$> f m
-  msg f (SBPMsgSettingsSave n m) = SBPMsgSettingsSave n <$> f m
-  msg f (SBPMsgSettingsWrite n m) = SBPMsgSettingsWrite n <$> f m
-  msg f (SBPMsgStartup n m) = SBPMsgStartup n <$> f m
-  msg f (SBPMsgStmFlashLockSector n m) = SBPMsgStmFlashLockSector n <$> f m
-  msg f (SBPMsgStmFlashUnlockSector n m) = SBPMsgStmFlashUnlockSector n <$> f m
-  msg f (SBPMsgStmUniqueIdReq n m) = SBPMsgStmUniqueIdReq n <$> f m
-  msg f (SBPMsgStmUniqueIdResp n m) = SBPMsgStmUniqueIdResp n <$> f m
-  msg f (SBPMsgThreadState n m) = SBPMsgThreadState n <$> f m
-  msg f (SBPMsgTrackingIq n m) = SBPMsgTrackingIq n <$> f m
-  msg f (SBPMsgTrackingState n m) = SBPMsgTrackingState n <$> f m
-  msg f (SBPMsgTrackingStateDepA n m) = SBPMsgTrackingStateDepA n <$> f m
-  msg f (SBPMsgTweet n m) = SBPMsgTweet n <$> f m
-  msg f (SBPMsgUartState n m) = SBPMsgUartState n <$> f m
-  msg f (SBPMsgUserData n m) = SBPMsgUserData n <$> f m
-  msg f (SBPMsgVelEcef n m) = SBPMsgVelEcef n <$> f m
-  msg f (SBPMsgVelNed n m) = SBPMsgVelNed n <$> f m
-  msg f (SBPMsgUnknown m) = SBPMsgUnknown <$> f m
-  msg f (SBPMsgBadCrc m) = SBPMsgBadCrc <$> f m
+   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 (SBPMsgBasePosEcef msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBasePosLlh msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBaselineEcef msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBaselineHeading 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 (SBPMsgUserData msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgVelEcef msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgVelNed msg sbp) = toJSON msg `merge` toJSON sbp
+   toJSON (SBPMsgBadCrc sbp) = toJSON sbp
+   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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Acquisition
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -14,13 +13,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
@@ -45,7 +44,7 @@
     -- ^ Code phase of best point
   , _msgAcqResult_cf :: Float
     -- ^ Carrier frequency of best point
-  , _msgAcqResult_sid :: GnssSignal
+  , _msgAcqResult_sid :: SBPGnssSignal
     -- ^ GNSS signal for which acquisition was attempted
   } deriving ( Show, Read, Eq )
 
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Bootload
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -16,13 +15,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
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
@@ -1,4 +1,3 @@
-{-# OPTIONS -fno-warn-orphans #-}
 -- |
 -- Module:      SwiftNav.SBP.Encoding
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -11,12 +10,12 @@
 
 module SwiftNav.SBP.Encoding where
 
-import           BasicPrelude
-import           Data.Aeson
-import qualified Data.ByteString          as B
-import           Data.ByteString.Base64   as Base64
-import           Data.Text.Encoding       (decodeUtf8With)
-import           Data.Text.Encoding.Error
+import BasicPrelude
+import Data.Aeson
+import qualified Data.ByteString as B
+import Data.ByteString.Base64 as Base64
+import Data.Text.Encoding (decodeUtf8With)
+import Data.Text.Encoding.Error
 
 -- ByteString doesn't have Aeson instances defined for it since
 -- arbitrary ByteString's aren't really valid JSON. This defines
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.ExtEvents
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -15,13 +14,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.FileIo
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -19,13 +18,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Flash
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -16,13 +15,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
diff --git a/src/SwiftNav/SBP/GnssSignal.hs b/src/SwiftNav/SBP/GnssSignal.hs
--- a/src/SwiftNav/SBP/GnssSignal.hs
+++ b/src/SwiftNav/SBP/GnssSignal.hs
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.GnssSignal
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -14,42 +13,42 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
 import SwiftNav.SBP.TH
 import SwiftNav.SBP.Types
 
--- | GnssSignal.
+-- | SBPGnssSignal.
 --
 -- Signal identifier containing constellation, band, and satellite identifier
-data GnssSignal = GnssSignal
-  { _gnssSignal_sat    :: Word16
+data SBPGnssSignal = SBPGnssSignal
+  { _sBPGnssSignal_sat         :: Word16
     -- ^ Constellation-specific satellite identifier
-  , _gnssSignal_code   :: Word8
-    -- ^ Signal constellation, band and code
-  , _gnssSignal_reserved :: Word8
-    -- ^ Reserved
+  , _sBPGnssSignal_band        :: Word8
+    -- ^ Signal band
+  , _sBPGnssSignal_constellation :: Word8
+    -- ^ Constellation to which the satellite belongs
   } deriving ( Show, Read, Eq )
 
-instance Binary GnssSignal where
+instance Binary SBPGnssSignal where
   get = do
-    _gnssSignal_sat <- getWord16le
-    _gnssSignal_code <- getWord8
-    _gnssSignal_reserved <- getWord8
-    return GnssSignal {..}
+    _sBPGnssSignal_sat <- getWord16le
+    _sBPGnssSignal_band <- getWord8
+    _sBPGnssSignal_constellation <- getWord8
+    return SBPGnssSignal {..}
 
-  put GnssSignal {..} = do
-    putWord16le _gnssSignal_sat
-    putWord8 _gnssSignal_code
-    putWord8 _gnssSignal_reserved
-$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_gnssSignal_" . stripPrefix "_gnssSignal_"}
-             ''GnssSignal)
-$(makeLenses ''GnssSignal)
+  put SBPGnssSignal {..} = do
+    putWord16le _sBPGnssSignal_sat
+    putWord8 _sBPGnssSignal_band
+    putWord8 _sBPGnssSignal_constellation
+$(deriveJSON defaultOptions {fieldLabelModifier = fromMaybe "_sBPGnssSignal_" . stripPrefix "_sBPGnssSignal_"}
+             ''SBPGnssSignal)
+$(makeLenses ''SBPGnssSignal)
diff --git a/src/SwiftNav/SBP/Logging.hs b/src/SwiftNav/SBP/Logging.hs
--- a/src/SwiftNav/SBP/Logging.hs
+++ b/src/SwiftNav/SBP/Logging.hs
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Logging
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -14,13 +13,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Navigation
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -22,13 +21,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Observation
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -14,13 +13,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
@@ -115,7 +114,7 @@
     -- ^ 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 :: GnssSignal
+  , _packedObsContent_sid :: SBPGnssSignal
     -- ^ GNSS signal identifier
   } deriving ( Show, Read, Eq )
 
@@ -307,7 +306,7 @@
     -- ^ Is valid?
   , _msgEphemeris_healthy :: Word8
     -- ^ Satellite is healthy?
-  , _msgEphemeris_sid    :: GnssSignal
+  , _msgEphemeris_sid    :: SBPGnssSignal
     -- ^ GNSS signal identifier
   , _msgEphemeris_iode   :: Word8
     -- ^ Issue of ephemeris data
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Piksi
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -16,13 +15,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
@@ -385,7 +384,7 @@
 data MsgMaskSatellite = MsgMaskSatellite
   { _msgMaskSatellite_mask :: Word8
     -- ^ Mask of systems that should ignore this satellite.
-  , _msgMaskSatellite_sid :: GnssSignal
+  , _msgMaskSatellite_sid :: SBPGnssSignal
     -- ^ GNSS signal for which the mask is applied
   } deriving ( Show, Read, Eq )
 
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Settings
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -18,13 +17,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.System
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -14,13 +13,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
diff --git a/src/SwiftNav/SBP/TH.hs b/src/SwiftNav/SBP/TH.hs
--- a/src/SwiftNav/SBP/TH.hs
+++ b/src/SwiftNav/SBP/TH.hs
@@ -14,18 +14,18 @@
 import Control.Lens
 import Data.Binary
 import Data.ByteString
-import Data.ByteString.Lazy hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
+import Data.Word
 import Language.Haskell.TH
 import SwiftNav.SBP.Types
 
 -- | Derive ToSBP typeclass, given an SBP message type name and the
 -- name of the implemented type.
 deriveSBP :: Name -> Name -> Q [Dec]
-deriveSBP msgType name =
-  [d|instance ToSBP $(conT name) where
-       toSBP m senderID = encoded & msgSBPCrc .~ checkCrc encoded
-         where
-           payload = toStrict $ encode m
-           len = fromIntegral $ Data.ByteString.length payload
-           encoded = Msg $(varE msgType) senderID len payload 0
-    |]
+deriveSBP msgType name = [d|instance ToSBP $(conT name) where
+                              toSBP msg senderID = encoded & msgSBPCrc .~ checkCrc encoded
+                                where
+                                  payload = toStrict $ encode msg
+                                  len = fromIntegral $ Data.ByteString.length payload
+                                  encoded = Msg $(varE msgType) senderID len payload 0
+                           |]
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
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.Tracking
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -14,13 +13,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
@@ -35,7 +34,7 @@
 data TrackingChannelState = TrackingChannelState
   { _trackingChannelState_state :: Word8
     -- ^ Status of tracking channel
-  , _trackingChannelState_sid :: GnssSignal
+  , _trackingChannelState_sid :: SBPGnssSignal
     -- ^ GNSS signal being tracked
   , _trackingChannelState_cn0 :: Float
     -- ^ Carrier-to-noise density
@@ -116,7 +115,7 @@
 data MsgTrackingIq = MsgTrackingIq
   { _msgTrackingIq_channel :: Word8
     -- ^ Tracking channel of origin
-  , _msgTrackingIq_sid   :: GnssSignal
+  , _msgTrackingIq_sid   :: SBPGnssSignal
     -- ^ GNSS signal identifier
   , _msgTrackingIq_corrs :: [TrackingChannelCorrelation]
     -- ^ Early, Prompt and Late correlations
diff --git a/src/SwiftNav/SBP/Types.hs b/src/SwiftNav/SBP/Types.hs
--- a/src/SwiftNav/SBP/Types.hs
+++ b/src/SwiftNav/SBP/Types.hs
@@ -11,15 +11,16 @@
 
 module SwiftNav.SBP.Types where
 
-import BasicPrelude            hiding (lookup)
-import Control.Lens            hiding ((.=))
-import Data.Aeson              hiding (decode, decode')
+import BasicPrelude hiding (lookup)
+import Control.Lens hiding ((.=))
+import Data.Aeson hiding (decode, decode')
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.Put
+import Data.ByteString.Lazy hiding (ByteString)
 import Data.ByteString.Builder
 import SwiftNav.CRC16
-import SwiftNav.SBP.Encoding   ()
+import SwiftNav.SBP.Encoding
 
 -- | Denotes the start of frame transmission. For v1.0, always 0x55.
 msgSBPPreamble :: Word8
@@ -57,7 +58,7 @@
     -- Preamble)
   } deriving ( Show, Read, Eq )
 
-$(makeClassy ''Msg)
+$(makeLenses ''Msg)
 
 instance Binary Msg where
   get = do
diff --git a/src/SwiftNav/SBP/User.hs b/src/SwiftNav/SBP/User.hs
--- a/src/SwiftNav/SBP/User.hs
+++ b/src/SwiftNav/SBP/User.hs
@@ -1,4 +1,3 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 -- |
 -- Module:      SwiftNav.SBP.User
 -- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
@@ -14,13 +13,13 @@
 import BasicPrelude
 import Control.Lens
 import Control.Monad.Loops
-import Data.Aeson.TH           (defaultOptions, deriveJSON, fieldLabelModifier)
+import Data.Aeson.TH (deriveJSON, defaultOptions, fieldLabelModifier)
 import Data.Binary
 import Data.Binary.Get
 import Data.Binary.IEEE754
 import Data.Binary.Put
 import Data.ByteString
-import Data.ByteString.Lazy    hiding (ByteString)
+import Data.ByteString.Lazy hiding ( ByteString )
 import Data.Int
 import Data.Word
 import SwiftNav.SBP.Encoding
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -1,13 +1,33 @@
 import           BasicPrelude
-import qualified Test.SwiftNav.CRC16        as CRC16
+import qualified Test.SwiftNav.CRC16 as CRC16
+import qualified Test.SwiftNav.SBP.Acquisition as Acquisition
+import qualified Test.SwiftNav.SBP.Bootload as Bootload
 import qualified Test.SwiftNav.SBP.Encoding as Encoding
+import qualified Test.SwiftNav.SBP.ExtEvents as ExtEvents
+import qualified Test.SwiftNav.SBP.Logging as Logging
+import qualified Test.SwiftNav.SBP.Navigation as Navigation
+import qualified Test.SwiftNav.SBP.Observation as Observation
+import qualified Test.SwiftNav.SBP.Piksi as Piksi
+import qualified Test.SwiftNav.SBP.System as System
+import qualified Test.SwiftNav.SBP.Table as Table
+import qualified Test.SwiftNav.SBP.Tracking as Tracking
 import           Test.Tasty
 
 tests :: TestTree
 tests =
   testGroup "Tests"
     [ CRC16.tests
+    , Acquisition.tests
+    , Bootload.tests
     , Encoding.tests
+    , ExtEvents.tests
+    , Logging.tests
+    , Navigation.tests
+    , Observation.tests
+    , Piksi.tests
+    , System.tests
+    , Table.tests
+    , Tracking.tests
     ]
 
 main :: IO ()
diff --git a/test/Test/SwiftNav/SBP/Acquisition.hs b/test/Test/SwiftNav/SBP/Acquisition.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Acquisition.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.Acquisition
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.Acquisition
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_acquisition.yaml"
+            moduleName = "sbp.acquisition"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "Acquisition Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Bootload.hs b/test/Test/SwiftNav/SBP/Bootload.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Bootload.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.Bootload
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.Bootload
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_bootload.yaml"
+            moduleName = "sbp.bootload"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "Bootload Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Encoding.hs b/test/Test/SwiftNav/SBP/Encoding.hs
--- a/test/Test/SwiftNav/SBP/Encoding.hs
+++ b/test/Test/SwiftNav/SBP/Encoding.hs
@@ -1,17 +1,15 @@
-{-# OPTIONS -fno-warn-orphans #-}
-
 module Test.SwiftNav.SBP.Encoding
   ( tests
   ) where
 
-import           BasicPrelude
-import qualified Data.Aeson            as A
-import           Data.ByteString       as BS
-import           SwiftNav.SBP.Encoding ()
-import           Test.QuickCheck
-import           Test.Tasty
-import           Test.Tasty.HUnit
-import           Test.Tasty.QuickCheck as QC
+import BasicPrelude
+import Data.ByteString as BS
+import qualified Data.Aeson as A
+import SwiftNav.SBP.Encoding ()
+import Test.QuickCheck
+import Test.Tasty
+import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck as QC
 
 instance Arbitrary BS.ByteString where
     arbitrary = BS.pack <$> arbitrary
@@ -24,12 +22,12 @@
 testParse =
   testGroup "Empty Test"
     [ testCase "Empty data" $ do
-        (A.decode . A.encode $ ("dddd" :: BS.ByteString)) @?= Just ("dddd" :: BS.ByteString)
+        (A.decode . A.encode $ ("dddd" :: BS.ByteString)) @?= Just ["dddd" :: String]
     ]
 
 testRoundtrip :: TestTree
 testRoundtrip = QC.testProperty "Aeson" prop
-  where prop ws = (A.decode . A.encode $ (ws :: BS.ByteString)) === Just (ws :: BS.ByteString)
+  where prop ws = (A.decode . A.encode $ (ws :: BS.ByteString)) === Just [ws]
 
 tests :: TestTree
 tests = testGroup "Roundtrip JSON serialization"
diff --git a/test/Test/SwiftNav/SBP/ExtEvents.hs b/test/Test/SwiftNav/SBP/ExtEvents.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/ExtEvents.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.ExtEvents
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.ExtEvents
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_ext_events.yaml"
+            moduleName = "sbp.ext_events"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "ExtEvents Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Logging.hs b/test/Test/SwiftNav/SBP/Logging.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Logging.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.Logging
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.Logging
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_logging.yaml"
+            moduleName = "sbp.logging"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "Logging Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Navigation.hs b/test/Test/SwiftNav/SBP/Navigation.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Navigation.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.Navigation
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.Navigation
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_navigation.yaml"
+            moduleName = "sbp.navigation"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "Navigation Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Observation.hs b/test/Test/SwiftNav/SBP/Observation.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Observation.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.Observation
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.Observation
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_observation.yaml"
+            moduleName = "sbp.observation"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "Observation Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Piksi.hs b/test/Test/SwiftNav/SBP/Piksi.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Piksi.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.Piksi
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.Piksi
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_piksi.yaml"
+            moduleName = "sbp.piksi"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "Piksi Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/System.hs b/test/Test/SwiftNav/SBP/System.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/System.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.System
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.System
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_system.yaml"
+            moduleName = "sbp.system"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "System Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Table.hs b/test/Test/SwiftNav/SBP/Table.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Table.hs
@@ -0,0 +1,22 @@
+module Test.SwiftNav.SBP.Table
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Empty Test"
+    [ testCase "Empty data" $ do
+        True @?= False
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "Table Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Tracking.hs b/test/Test/SwiftNav/SBP/Tracking.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Tracking.hs
@@ -0,0 +1,25 @@
+module Test.SwiftNav.SBP.Tracking
+  ( tests
+  ) where
+
+import BasicPrelude
+import SwiftNav.SBP.Tracking
+import Test.SwiftNav.SBP.Utils
+import Test.Tasty
+import Test.Tasty.HUnit
+
+testParse :: TestTree
+testParse =
+  testGroup "Package tests"
+    [ testCase "Parsing tests" $ do
+        let filepath = "../spec/tests/yaml/swiftnav/sbp/test_tracking.yaml"
+            moduleName = "sbp.tracking"
+        val <- assertPackage filepath moduleName
+        assertBool "Fail" val
+    ]
+
+tests :: TestTree
+tests =
+  testGroup "Tracking Tests"
+    [ testParse
+    ]
diff --git a/test/Test/SwiftNav/SBP/Utils.hs b/test/Test/SwiftNav/SBP/Utils.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/SwiftNav/SBP/Utils.hs
@@ -0,0 +1,84 @@
+-- |
+-- Module:      SwiftNav.SBP
+-- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
+-- License:     LGPL-3
+-- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- A straightforward port of the testing harness stuff for libsbp's
+-- Python client:
+-- https://github.com/swift-nav/libsbp/blob/master/python/tests/sbp/utils.py.
+-- Uses the spec'd out unit tests at
+-- https://github.com/swift-nav/libsbp/tree/master/spec/yaml/swiftnav/sbp.
+
+module Test.SwiftNav.SBP.Utils
+  ( assertPackage
+  ) where
+
+import BasicPrelude
+import Data.ByteString
+import Data.Yaml.YamlLight
+
+-- -- | Assert that a proper SBP parsing from a raw package of data.
+-- assertSBP :: undefined -- ^ SBP message parsed from unit test's raw packet
+--           -> undefined -- ^ Unit test case parsed from YAML
+--           -> Bool
+-- assertSBP sbp testCase = undefined
+
+-- -- | Checks the field values of a parsed message for equality against
+-- -- some ground truth value.
+-- fieldEq :: undefined -- ^ Parsed field contents
+--         -> undefined -- ^ Expected field contents
+--         -> Bool
+-- fieldEq p e = undefined
+
+-- -- | Asserts that the parsed payload of an SBP message has the
+-- -- expected field values.
+-- assertMsg :: undefined -- ^ Parsed SBP message
+--           -> undefined -- ^ Unit test case for this message
+--           -> Bool
+-- assertMsg msg testCase = undefined
+
+-- -- | Asserts that a msg gets serialized back into binary with the
+-- -- expected value.
+-- assertMsgRoundtrip :: undefined
+--                    -> undefined
+--                    -> undefined
+-- assertMsgRoundtrip msg rawPackage = undefined
+
+-- -- | Asserts that a msg gets serialized back into JSON with the
+-- -- expected value, as well as gets serialized from JSON into an
+-- -- expected object.
+-- assertMsgRoundtripJson :: undefined
+--                        -> undefined
+--                        -> undefined
+-- assertMsgRoundtripJson msg rawJSON = undefined
+
+-- -- | Asserts that a message materialized will get serialized into the
+-- -- right JSON object.
+-- assertMaterialization :: ByteString
+--                       -> undefined
+--                       -> undefined
+--                       -> undefined
+-- assertMaterialization msg sbp rawJSON = undefined
+
+-- | Sanity check the package collection of tests before actually
+-- running the tests.
+assertSanePackage :: ByteString
+                  -> ByteString
+                  -> Bool
+assertSanePackage pkgName pkg = undefined
+
+-- | Runs unit tests for message bindings by reading a YAML unit test
+-- specification, parsing a raw packet for each test, and then
+-- asserting that SBP messages and parsed payloads have their intended
+-- values.
+assertPackage :: String     -- ^ Filepath to unit test specifications
+              -> ByteString -- ^ Name of package to test
+              -> IO Bool
+assertPackage testFilename pkgName = do
+   pkg <- parseYamlFile testFilename
+   case pkg of
+     YMap x -> return True
+     _      -> return False
