diff --git a/main/JSON2JSON.hs b/main/JSON2JSON.hs
new file mode 100644
--- /dev/null
+++ b/main/JSON2JSON.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+
+-- |
+-- Module:      JSON2JSON
+-- Copyright:   Copyright (C) 2015 Swift Navigation, Inc.
+-- License:     LGPL-3
+-- Maintainer:  Mark Fine <dev@swiftnav.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- JSON to JSON tool - reads SBP JSON from stdin and sends SBP JSON to
+-- stdout.
+
+import BasicPrelude                 hiding (lines, map, mapMaybe)
+import Control.Monad.Trans.Resource
+import Data.Aeson
+import Data.Aeson.TH
+import Data.ByteString.Lazy         hiding (ByteString, drop, map)
+import Data.Conduit
+import Data.Conduit.Binary          hiding (drop)
+import Data.Conduit.List            hiding (drop)
+import Data.Time
+import SwiftNav.SBP
+import System.IO
+
+data Data = Data
+  { _data :: SBPMsg
+  , _time :: UTCTime
+  } deriving Show
+
+deriveJSON defaultOptions { fieldLabelModifier = drop 1 } ''Data
+
+-- | Encode a SBPMsg to a line of JSON.
+encodeLine :: Data -> ByteString
+encodeLine v = toStrict $ encode v <> "\n"
+
+main :: IO ()
+main =
+  runResourceT $
+    sourceHandle stdin
+      =$= lines
+      =$= mapMaybe decodeStrict
+      =$= map encodeLine
+      $$  sinkHandle stdout
diff --git a/sbp.cabal b/sbp.cabal
--- a/sbp.cabal
+++ b/sbp.cabal
@@ -1,6 +1,6 @@
 cabal-version: >=1.22
 name: sbp
-version: 2.3.15
+version: 2.3.16
 license: LGPL-3
 copyright: Copyright (C) 2015-2018 Swift Navigation, Inc.
 maintainer: Swift Navigation <dev@swiftnav.com>
@@ -100,6 +100,22 @@
         conduit-extra >=1.2.3.2,
         resourcet >=1.1.11,
         sbp -any
+
+executable json2json
+    main-is: JSON2JSON.hs
+    hs-source-dirs: main
+    default-language: Haskell2010
+    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
+    build-depends:
+        aeson >=1.2.4.0,
+        base >=4.10.1.0,
+        basic-prelude >=0.7.0,
+        bytestring >=0.10.8.2,
+        conduit >=1.2.13.1,
+        conduit-extra >=1.2.3.2,
+        resourcet >=1.1.11,
+        sbp -any,
+        time >=1.8.0.2
 
 executable sbp2yaml
     main-is: SBP2YAML.hs
diff --git a/src/SwiftNav/SBP/Gnss.hs b/src/SwiftNav/SBP/Gnss.hs
--- a/src/SwiftNav/SBP/Gnss.hs
+++ b/src/SwiftNav/SBP/Gnss.hs
@@ -40,7 +40,8 @@
 -- Signal identifier containing constellation, band, and satellite identifier
 data GnssSignal = GnssSignal
   { _gnssSignal_sat :: !Word8
-    -- ^ Constellation-specific satellite identifier
+    -- ^ Constellation-specific satellite identifier. This field for Glonass can
+    -- either be (100+FCN) where FCN is in [-7,+6] or  the Slot ID in [1,28]
   , _gnssSignal_code :: !Word8
     -- ^ Signal constellation, band and code
   } deriving ( Show, Read, Eq )
diff --git a/src/SwiftNav/SBP/Msg.hs b/src/SwiftNav/SBP/Msg.hs
--- a/src/SwiftNav/SBP/Msg.hs
+++ b/src/SwiftNav/SBP/Msg.hs
@@ -132,6 +132,7 @@
    | SBPMsgMagRaw MsgMagRaw Msg
    | SBPMsgMaskSatellite MsgMaskSatellite Msg
    | SBPMsgMaskSatelliteDep MsgMaskSatelliteDep Msg
+   | SBPMsgMeasurementState MsgMeasurementState Msg
    | SBPMsgNapDeviceDnaReq MsgNapDeviceDnaReq Msg
    | SBPMsgNapDeviceDnaResp MsgNapDeviceDnaResp Msg
    | SBPMsgNdbEvent MsgNdbEvent Msg
@@ -289,6 +290,7 @@
           | _msgSBPType == msgMagRaw = SBPMsgMagRaw (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgMaskSatellite = SBPMsgMaskSatellite (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgMaskSatelliteDep = SBPMsgMaskSatelliteDep (decode (fromStrict (unBytes _msgSBPPayload))) m
+          | _msgSBPType == msgMeasurementState = SBPMsgMeasurementState (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp (decode (fromStrict (unBytes _msgSBPPayload))) m
           | _msgSBPType == msgNdbEvent = SBPMsgNdbEvent (decode (fromStrict (unBytes _msgSBPPayload))) m
@@ -438,6 +440,7 @@
       encoder (SBPMsgMagRaw _ m) = put m
       encoder (SBPMsgMaskSatellite _ m) = put m
       encoder (SBPMsgMaskSatelliteDep _ m) = put m
+      encoder (SBPMsgMeasurementState _ m) = put m
       encoder (SBPMsgNapDeviceDnaReq _ m) = put m
       encoder (SBPMsgNapDeviceDnaResp _ m) = put m
       encoder (SBPMsgNdbEvent _ m) = put m
@@ -591,6 +594,7 @@
         | msgType == msgMagRaw = SBPMsgMagRaw <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgMaskSatellite = SBPMsgMaskSatellite <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgMaskSatelliteDep = SBPMsgMaskSatelliteDep <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
+        | msgType == msgMeasurementState = SBPMsgMeasurementState <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgNapDeviceDnaReq = SBPMsgNapDeviceDnaReq <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgNapDeviceDnaResp = SBPMsgNapDeviceDnaResp <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
         | msgType == msgNdbEvent = SBPMsgNdbEvent <$> pure (decode (fromStrict (unBytes payload))) <*> parseJSON obj
@@ -745,6 +749,7 @@
   toJSON (SBPMsgMagRaw n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgMaskSatellite n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgMaskSatelliteDep n m) = toJSON n <<>> toJSON m
+  toJSON (SBPMsgMeasurementState n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgNapDeviceDnaReq n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgNapDeviceDnaResp n m) = toJSON n <<>> toJSON m
   toJSON (SBPMsgNdbEvent n m) = toJSON n <<>> toJSON m
@@ -893,6 +898,7 @@
   msg f (SBPMsgMagRaw n m) = SBPMsgMagRaw n <$> f m
   msg f (SBPMsgMaskSatellite n m) = SBPMsgMaskSatellite n <$> f m
   msg f (SBPMsgMaskSatelliteDep n m) = SBPMsgMaskSatelliteDep n <$> f m
+  msg f (SBPMsgMeasurementState n m) = SBPMsgMeasurementState n <$> f m
   msg f (SBPMsgNapDeviceDnaReq n m) = SBPMsgNapDeviceDnaReq n <$> f m
   msg f (SBPMsgNapDeviceDnaResp n m) = SBPMsgNapDeviceDnaResp n <$> f m
   msg f (SBPMsgNdbEvent n m) = SBPMsgNdbEvent n <$> f m
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
@@ -308,6 +308,57 @@
 $(makeJSON "_msgTrackingState_" ''MsgTrackingState)
 $(makeLenses ''MsgTrackingState)
 
+-- | MeasurementState.
+--
+-- Measurement Engine tracking channel state for a specific satellite signal
+-- and measured signal power.  The mesid field for Glonass can either  carry
+-- the FCN as 100 + FCN where FCN is in [-7, +6] or  the Slot ID (from 1 to 28)
+data MeasurementState = MeasurementState
+  { _measurementState_mesid :: !GnssSignal
+    -- ^ Measurement Engine GNSS signal being tracked (carries either Glonass FCN
+    -- or SLOT)
+  , _measurementState_cn0 :: !Word8
+    -- ^ Carrier-to-Noise density.  Zero implies invalid cn0.
+  } deriving ( Show, Read, Eq )
+
+instance Binary MeasurementState where
+  get = do
+    _measurementState_mesid <- get
+    _measurementState_cn0 <- getWord8
+    pure MeasurementState {..}
+
+  put MeasurementState {..} = do
+    put _measurementState_mesid
+    putWord8 _measurementState_cn0
+
+$(makeJSON "_measurementState_" ''MeasurementState)
+$(makeLenses ''MeasurementState)
+
+msgMeasurementState :: Word16
+msgMeasurementState = 0x0061
+
+-- | SBP class for message MSG_MEASUREMENT_STATE (0x0061).
+--
+-- The tracking message returns a variable-length array of tracking channel
+-- states. It reports status and carrier-to-noise density measurements for all
+-- tracked satellites.
+data MsgMeasurementState = MsgMeasurementState
+  { _msgMeasurementState_states :: ![MeasurementState]
+    -- ^ ME signal tracking channel state
+  } deriving ( Show, Read, Eq )
+
+instance Binary MsgMeasurementState where
+  get = do
+    _msgMeasurementState_states <- whileM (not <$> isEmpty) get
+    pure MsgMeasurementState {..}
+
+  put MsgMeasurementState {..} = do
+    mapM_ put _msgMeasurementState_states
+
+$(makeSBP 'msgMeasurementState ''MsgMeasurementState)
+$(makeJSON "_msgMeasurementState_" ''MsgMeasurementState)
+$(makeLenses ''MsgMeasurementState)
+
 -- | TrackingChannelCorrelation.
 --
 -- Structure containing in-phase and quadrature correlation components.
