{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TemplateHaskell #-} -- | -- Module: SwiftNav.SBP.Types -- Copyright: Copyright (C) 2015 Swift Navigation, Inc. -- License: LGPL-3 -- Maintainer: Mark Fine -- Stability: experimental -- Portability: portable -- -- Common SBP type requirements, containers, and serialization -- utilities. -- module SwiftNav.SBP.Types ( module SwiftNav.SBP.Types ) where import BasicPrelude import Control.Lens hiding ((.=)) import Data.Aeson import Data.Binary import Data.Binary.Get import Data.Binary.Put import Data.ByteString.Base64 as Base64 import Data.ByteString.Builder import Data.Text.Encoding import Data.Text.Encoding.Error import SwiftNav.CRC16 -- | Denotes the start of frame transmission. For v1.0, always 0x55. msgSBPPreamble :: Word8 msgSBPPreamble = 0x55 -- | Default sender ID. Intended for messages sent from the host to -- the device. defaultSender :: Word16 defaultSender = 0x42 -- | Wrapper around ByteString for *JSON and Binary typeclass instances. newtype Bytes = Bytes { unBytes :: ByteString } deriving ( Show, Read, Eq ) -- | Packet structure for Swift Navigation Binary Protocol (SBP). -- -- Definition of the over-the-wire message framing format and packet -- structure for Swift Navigation Binary Protocol (SBP), a minimal -- binary protocol for communicating with Swift devices. It is used -- to transmit solutions, observations, status and debugging -- messages, as well as receive messages from the host operating -- system. data Msg = Msg { _msgSBPType :: !Word16 -- ^ Uniquely identifies the type of the payload contents , _msgSBPSender :: !Word16 -- ^ A unique identifier of the sending hardware. For v1.0, -- set to the 2 least significant bytes of the device serial -- number , _msgSBPLen :: !Word8 -- ^ Byte-length of the payload field , _msgSBPPayload :: !Bytes -- ^ Binary data of the message, as identified by Message Type and -- Length. Usually contains the in-memory binary representation of -- a C struct (see documentation on individual message types) , _msgSBPCrc :: !Word16 -- ^ Cyclic Redundancy Check (CRC) of the packet's binary data from -- the Message Type up to the end of Payload (does not include the -- Preamble) } deriving ( Show, Read, Eq ) $(makeClassy ''Msg) instance Binary Msg where get = do _msgSBPType <- getWord16le _msgSBPSender <- getWord16le _msgSBPLen <- getWord8 _msgSBPPayload <- fmap Bytes $ getByteString $ fromIntegral _msgSBPLen _msgSBPCrc <- getWord16le pure Msg {..} put Msg {..} = do putWord16le _msgSBPType putWord16le _msgSBPSender putWord8 _msgSBPLen putByteString (unBytes _msgSBPPayload) putWord16le _msgSBPCrc instance FromJSON Bytes where parseJSON = withText "ByteString" (pure . Bytes . Base64.decodeLenient . encodeUtf8) instance FromJSON Msg where parseJSON (Object v) = Msg <$> v .: "msg_type" <*> v .: "sender" <*> v .: "length" <*> v .: "payload" <*> v .: "crc" parseJSON _ = mzero instance ToJSON Bytes where toJSON = toJSON . decodeUtf8With ignore . Base64.encode . unBytes instance ToJSON Msg where toJSON Msg {..} = object [ "preamble" .= msgSBPPreamble , "msg_type" .= _msgSBPType , "sender" .= _msgSBPSender , "length" .= _msgSBPLen , "payload" .= _msgSBPPayload , "crc" .= _msgSBPCrc ] -- | Class of generic representation of specialized SBP messages into -- SBP message frames. class Binary a => ToSBP a where -- | Convert an SBP message record that is serializable and a two-byte -- senderID to a binary into an SBP message frame. toSBP :: a -> Word16 -> Msg checkCrc :: Msg -> Word16 checkCrc Msg {..} = crc16 $ toLazyByteString $ word16LE _msgSBPType <> word16LE _msgSBPSender <> word8 _msgSBPLen <> byteString (unBytes _msgSBPPayload)