sbp-4.0.3: main/SBP2NMEA.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
-- |
-- Module: SBP2JNMEA
-- Copyright: Copyright (C) 2020-2021 Swift Navigation, Inc.
-- License: MIT
-- Contact: https://support.swiftnav.com
-- Stability: experimental
-- Portability: portable
--
-- SBP to NMEA tool - reads SBP binary from stdin and sends NMEA
-- to stdout.
import BasicPrelude hiding (map)
import Control.Lens
import Control.Monad.Trans.Resource
import Data.Bits
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import Data.Conduit
import Data.Conduit.Binary
import Data.Conduit.List
import Data.Conduit.Serialization.Binary
import Data.Time
import Data.Time.Clock.POSIX
import Data.Word
import SwiftNav.SBP
import System.IO
import Text.Printf
-- | NMEA time.
--
timestamp :: MsgPosLlh -> ByteString
timestamp posLlh = BS8.pack $ printf "%s.%03u" times mods
where
epochs = 315964800
offsets = 18
(tows, mods) = divMod (posLlh ^. msgPosLlh_tow) 1000
seconds = epochs - offsets + tows
time = posixSecondsToUTCTime $ fromIntegral seconds
times = formatTime defaultTimeLocale "%H%M%S" time
-- | NMEA latitude.
--
lat :: MsgPosLlh -> ByteString
lat posLlh = BS8.pack $ printf "%02u%010.7f" i (60 * f)
where
(i, f) = properFraction (abs $ posLlh ^. msgPosLlh_lat) :: (Word16, Double)
-- | NMEA latitude direction.
--
latDir :: MsgPosLlh -> ByteString
latDir posLlh = bool "S" "N" $ posLlh ^. msgPosLlh_lat > 0
-- | NMEA longitude.
--
lon :: MsgPosLlh -> ByteString
lon posLlh = BS8.pack $ printf "%03u%010.7f" i (60 * f)
where
(i, f) = properFraction (abs $ posLlh ^. msgPosLlh_lon) :: (Word16, Double)
-- | NMEA longitude direction.
--
lonDir :: MsgPosLlh -> ByteString
lonDir posLlh = bool "W" "E" $ posLlh ^. msgPosLlh_lon > 0
-- | NMEA quality.
--
quality :: MsgPosLlh -> ByteString
quality posLlh = case posLlh ^. msgPosLlh_flags .&. 0x7 of
0 -> "0"
1 -> "1"
2 -> "2"
3 -> "5"
4 -> "4"
_ -> "0"
-- | NMEA number of satellites.
--
satellites :: MsgPosLlh -> ByteString
satellites posLlh = BS8.pack $ printf "%02u" $ posLlh ^. msgPosLlh_n_sats
-- | NMEA altitude.
--
height :: MsgPosLlh -> ByteString
height posLlh = BS8.pack $ printf "%.2f" $ posLlh ^. msgPosLlh_height
-- | NMEA sentence for GGA for GPS.
--
sentence :: MsgPosLlh -> ByteString
sentence posLlh =
BS8.intercalate ","
[ "GPGGA"
, timestamp posLlh
, lat posLlh
, latDir posLlh
, lon posLlh
, lonDir posLlh
, quality posLlh
, satellites posLlh
, "0.0"
, height posLlh
, "M"
, "0.0"
, "M"
, mempty
, mempty
]
-- | NMEA checksum.
--
checksum :: ByteString -> ByteString
checksum s = BS8.pack $ printf "%02x" $ BS.foldl' xor 0 s
-- | NMEA GGA for GPS.
--
gpgga :: MsgPosLlh -> ByteString
gpgga posLlh = "$" <> s <> "*" <> c <> "\r\n"
where
s = sentence posLlh
c = checksum s
-- | Encode a SBPMsg to a line of JSON.
encodeLine :: SBPMsg -> ByteString
encodeLine (SBPMsgPosLlh posLlh _msg) = gpgga posLlh
encodeLine _ = mempty
main :: IO ()
main = do
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
runResourceT $
sourceHandle stdin
=$= conduitDecode
=$= map encodeLine
$$ sinkHandle stdout