packages feed

gnss-converters 0.1.20 → 0.2.0

raw patch · 4 files changed

+153/−174 lines, 4 filesdep −random

Dependencies removed: random

Files

gnss-converters.cabal view
@@ -1,5 +1,5 @@ name:                  gnss-converters-version:               0.1.20+version:               0.2.0 synopsis:              GNSS Converters. description:           Haskell bindings for GNSS converters. homepage:              http://github.com/swift-nav/gnss-converters@@ -29,7 +29,6 @@                      , lens                      , monad-control                      , mtl-                     , random                      , resourcet                      , rtcm                      , sbp
src/Data/RTCM3/SBP.hs view
@@ -19,7 +19,6 @@   , toWn   , mjdEpoch   , updateGpsTime-  , updateLock   , convert   , newStore   ) where@@ -36,7 +35,6 @@ import           Data.Time import           Data.Word import           SwiftNav.SBP-import           System.Random   --------------------------------------------------------------------------------@@ -173,25 +171,26 @@ fromEcefVal :: Int64 -> Double fromEcefVal x = fromIntegral x / 10000 -newGpsTime :: MonadStore e m => Word32 -> m GpsTime+newGpsTime :: MonadStore e m => Word32 -> m GpsTimeNano newGpsTime tow = do   wn <- view storeWn >>= liftIO . readIORef-  return GpsTime-    { _gpsTime_tow = tow-    , _gpsTime_wn  = wn+  return GpsTimeNano+    { _gpsTimeNano_tow = tow+    , _gpsTimeNano_ns  = 0+    , _gpsTimeNano_wn  = wn     }  -- | If incoming TOW is less than stored TOW, rollover WN. ---updateGpsTime :: Word32 -> GpsTime -> GpsTime+updateGpsTime :: Word32 -> GpsTimeNano -> GpsTimeNano updateGpsTime tow gpsTime =-  gpsTime & gpsTime_tow .~ tow &-    if tow >= gpsTime ^. gpsTime_tow then id else-      gpsTime_wn %~ (+ 1)+  gpsTime & gpsTimeNano_tow .~ tow &+    if tow >= gpsTime ^. gpsTimeNano_tow then id else+      gpsTimeNano_wn %~ (+ 1)  -- | Produce GPS Time from Observation header, handling WN rollover. ---toGpsTime :: MonadStore e m => GpsObservationHeader -> m GpsTime+toGpsTime :: MonadStore e m => GpsObservationHeader -> m GpsTimeNano toGpsTime hdr = do   let tow      = hdr ^. gpsObservationHeader_tow       station  = hdr ^. gpsObservationHeader_station@@ -296,30 +295,37 @@ toCn0_L2 :: GpsL2ExtObservation -> Word8 toCn0_L2 = (^. gpsL2ExtObservation_cnr) -newLock :: IO Lock-newLock = do-  counter <- randomIO-  return Lock-    { _lockTime    = 0-    , _lockCounter = counter-    }---- | If incoming time is less than stored time, increment lock counter.----updateLock :: Word8 -> Lock -> Lock-updateLock time lock =-  lock & lockTime .~ time &-    if time >= lock ^. lockTime then id else-      lockCounter %~ (+1)---- | Produce Lock Counter from Lock Time, handling cycle slips.+-- | Convert between DF013 and DF019 lock time to DF402 lock time ---toLock :: MonadStore e m => Word16 -> GnssSignal -> Word8 -> m Word16-toLock station sid time = do-  lockMap <- view storeLockMap-  lock    <- liftIO $ newLock-  liftIO $ fmap (^. lockCounter) $-    modifyMap lockMap lock (station, sid) $ updateLock time+toLock :: Word8 -> Word8+toLock t =+  if t' < 32 then 0 else+    if t' < 64 then 1 else+      if t' < 128 then 2 else+        if t' < 256 then 3 else+          if t' < 512 then 4 else+            if t' < 1024 then 5 else+              if t' < 2048 then 6 else+                if t' < 4096 then 7 else+                  if t' < 8192 then 8 else+                    if t' < 16384 then 9 else+                      if t' < 32768 then 10 else+                        if t' < 65536 then 11 else+                          if t' < 131072 then 12 else+                            if t' < 262144 then 13 else+                              if t' < 524288 then 14 else+                                15+  where+    t' :: Word32+    t' =+      1000 *+        if t <= 23 then fromIntegral t else+          if t <= 47 then fromIntegral t * 2 - 24 else+            if t <= 71 then fromIntegral t * 4 - 120 else+              if t <= 95 then fromIntegral t * 8 - 408 else+                if t <= 119 then fromIntegral t * 16 - 1176 else+                  if t <= 126 then fromIntegral t * 32 - 3096 else+                    937  -- | Construct sequenced SBP observation header --@@ -327,81 +333,85 @@                          => Word8                -- ^ Total messages                          -> Word8                -- ^ Message in sequence                          -> GpsObservationHeader -- ^ RTCM observation header-                         -> m ObservationHeaderDep+                         -> m ObservationHeader fromGpsObservationHeader totalMsgs n hdr = do   t <- toGpsTime hdr-  return ObservationHeaderDep-    { _observationHeaderDep_t     = t+  return ObservationHeader+    { _observationHeader_t     = t     -- First nibble is the size of the sequence (n), second nibble is the     -- zero-indexed counter (ith packet of n). See observation header packing     -- https://github.com/swift-nav/libsbp/blob/master/spec/yaml/swiftnav/sbp/observation.yaml#L63-    , _observationHeaderDep_n_obs = totalMsgs `shiftL` 4 .|. n+    , _observationHeader_n_obs = totalMsgs `shiftL` 4 .|. n     }  -- | Construct an L1 GnssSignal ---toL1GnssSignal :: Word8 -> GpsL1Observation -> GnssSignal+toL1GnssSignal :: Word8 -> GpsL1Observation -> GnssSignal16 toL1GnssSignal sat l1 =-  GnssSignal-    { _gnssSignal_sat      = fromIntegral $ sat - 1-    , _gnssSignal_code     = if l1 ^. gpsL1Observation_code then l1PSidCode else l1CSidCode-    , _gnssSignal_reserved = 0+  GnssSignal16+    { _gnssSignal16_sat  = sat+    , _gnssSignal16_code = if l1 ^. gpsL1Observation_code then l1PSidCode else l1CSidCode     }  -- | Construct an L1 SBP PackedObsContent an RTCM satellite vehicle observation -- fromL1SatelliteObservation :: MonadStore e m-                           => Word16              -- ^ Station ID-                           -> Word8               -- ^ Satellite PRN+                           => Word8               -- ^ Satellite PRN                            -> GpsL1Observation                            -> GpsL1ExtObservation-                           -> m PackedObsContentDepC-fromL1SatelliteObservation station sat l1 l1e = do+                           -> m PackedObsContent+fromL1SatelliteObservation sat l1 l1e = do   -- Checks GPS L1 code indicator for RTCM message 1002.   -- See DF010, pg. 3-17 of the RTCM3 spec.   let sid = toL1GnssSignal sat l1-  lock    <- toLock station sid $ l1 ^. gpsL1Observation_lockTime-  return PackedObsContentDepC-    { _packedObsContentDepC_P    = toP_L1 l1 l1e-    , _packedObsContentDepC_L    = toL_L1 l1 l1e-    , _packedObsContentDepC_cn0  = toCn0_L1 l1e-    , _packedObsContentDepC_lock = lock-    , _packedObsContentDepC_sid  = sid+  return PackedObsContent+    { _packedObsContent_P     = toP_L1 l1 l1e+    , _packedObsContent_L     = toL_L1 l1 l1e+    , _packedObsContent_D     = Doppler 0 0+    , _packedObsContent_cn0   = toCn0_L1 l1e+    , _packedObsContent_lock  = toLock $ l1 ^. gpsL1Observation_lockTime+    , _packedObsContent_sid   = sid+    , _packedObsContent_flags = 0x7 -- Doppler Invalid+                                    -- 1/2 cycle phase ambiguity resolved+                                    -- carrier valid+                                    -- pseudorange valid     }  -- | Construct an L2 GnssSignal ---toL2GnssSignal :: Word8 -> GpsL2Observation -> Maybe GnssSignal+toL2GnssSignal :: Word8 -> GpsL2Observation -> Maybe GnssSignal16 toL2GnssSignal sat l2 = do   code <- M.lookup (l2 ^. gpsL2Observation_code) l2codeToSBPSignalCode-  return GnssSignal-    { _gnssSignal_sat      = fromIntegral $ sat - 1-    , _gnssSignal_code     = code-    , _gnssSignal_reserved = 0+  return GnssSignal16+    { _gnssSignal16_sat  = fromIntegral sat+    , _gnssSignal16_code = code     }  -- | Construct an L2 SBP PackedObsContent an RTCM satellite vehicle observation -- fromL2SatelliteObservation :: MonadStore e m-                           => Word16                 -- ^ Station ID-                           -> Word8                  -- ^ Satellite PRN+                           => Word8                  -- ^ Satellite PRN                            -> GpsL1Observation                            -> GpsL1ExtObservation                            -> GpsL2Observation                            -> GpsL2ExtObservation-                           -> m (Maybe PackedObsContentDepC)-fromL2SatelliteObservation station sat l1 l1e l2 l2e =+                           -> m (Maybe PackedObsContent)+fromL2SatelliteObservation sat l1 l1e l2 l2e =   -- Checks GPS L2 code indicator.   -- See DF016, pg. 3-17 of the RTCM3 spec.   maybe' (toL2GnssSignal sat l2) (return Nothing) $ \sid -> do-    lock <- toLock station sid $ l2 ^. gpsL2Observation_lockTime-    return $ Just PackedObsContentDepC-      { _packedObsContentDepC_P    = toP_L2 l1 l1e l2-      , _packedObsContentDepC_L    = toL_L2 l1 l1e l2 l2e-      , _packedObsContentDepC_cn0  = toCn0_L2 l2e-      , _packedObsContentDepC_lock = lock-      , _packedObsContentDepC_sid  = sid-      }+    return $ Just PackedObsContent+      { _packedObsContent_P     = toP_L2 l1 l1e l2+      , _packedObsContent_L     = toL_L2 l1 l1e l2 l2e+      , _packedObsContent_D     = Doppler 0 0+      , _packedObsContent_cn0   = toCn0_L2 l2e+      , _packedObsContent_lock  = toLock $ l2 ^. gpsL2Observation_lockTime+      , _packedObsContent_sid   = sid+      , _packedObsContent_flags = 0x7 -- Doppler Invalid+                                      -- 1/2 cycle phase ambiguity resolved+                                      -- carrier valid+                                      -- pseudorange valid+    }  -- | Construct SBP GPS observation message (possibly chunked). --@@ -409,13 +419,13 @@               => GpsObservationHeader -- ^ RTCM observation header               -> Word8                -- ^ Total messages               -> Word8                -- ^ Message in sequence-              -> [PackedObsContentDepC]-              -> m MsgObsDepC+              -> [PackedObsContent]+              -> m MsgObs chunkToMsgObs hdr totalMsgs n packed = do   header <- fromGpsObservationHeader totalMsgs n hdr-  return MsgObsDepC-    { _msgObsDepC_header = header-    , _msgObsDepC_obs    = packed+  return MsgObs+    { _msgObs_header = header+    , _msgObs_obs    = packed     }  -- | 16 bit SBP Sender Id is 12 bit RTCM Station Id with high nibble or'd in@@ -431,9 +441,9 @@  -- | Construct an L1 SBP PackedObsContent from an RTCM Msg 1002. ---fromObservation1002 :: MonadStore e m => Word16 -> Observation1002 -> m [PackedObsContentDepC]-fromObservation1002 station obs = do-  obs1 <- fromL1SatelliteObservation station sat l1 l1e+fromObservation1002 :: MonadStore e m => Observation1002 -> m [PackedObsContent]+fromObservation1002 obs = do+  obs1 <- fromL1SatelliteObservation sat l1 l1e   return $     -- Only lower set of PRN numbers (1-32) are supported     if sat > maxSats then mempty else@@ -449,21 +459,20 @@ -- -- This chunking takes places because the number of observations in a given 1002 -- may very well exceed the maximum SBP supported payload size of 255 bytes.-fromMsg1002 :: MonadStore e m => Msg1002 -> m [MsgObsDepC]+fromMsg1002 :: MonadStore e m => Msg1002 -> m [MsgObs] fromMsg1002 m = do-  let hdr     = m ^. msg1002_header-      station = hdr ^. gpsObservationHeader_station-  obs <- concatMapM (fromObservation1002 station) $ m ^. msg1002_observations+  let hdr = m ^. msg1002_header+  obs <- concatMapM fromObservation1002 $ m ^. msg1002_observations   let chunks    = zip [0..] $ chunksOf maxObsPerMessage obs       totalMsgs = fromIntegral $ length chunks   forM chunks $ uncurry $ chunkToMsgObs hdr totalMsgs  -- | Construct an L1/L2 SBP PackedObsContent from an RTCM Msg 1004. ---fromObservation1004 :: MonadStore e m => Word16 -> Observation1004 -> m [PackedObsContentDepC]-fromObservation1004 station obs = do-  obs1 <- fromL1SatelliteObservation station sat l1 l1e-  obs2 <- fromL2SatelliteObservation station sat l1 l1e l2 l2e+fromObservation1004 :: MonadStore e m => Observation1004 -> m [PackedObsContent]+fromObservation1004 obs = do+  obs1 <- fromL1SatelliteObservation sat l1 l1e+  obs2 <- fromL2SatelliteObservation sat l1 l1e l2 l2e   return $     -- Only lower set of PRN numbers (1-32) are supported     if sat > maxSats then mempty else@@ -483,11 +492,10 @@ -- -- This chunking takes places because the number of observations in a given 1004 -- may very well exceed the maximum SBP supported payload size of 255 bytes.-fromMsg1004 :: MonadStore e m => Msg1004 -> m [MsgObsDepC]+fromMsg1004 :: MonadStore e m => Msg1004 -> m [MsgObs] fromMsg1004 m = do-  let hdr     = m ^. msg1004_header-      station = hdr ^. gpsObservationHeader_station-  obs <- concatMapM (fromObservation1004 station) $ m ^. msg1004_observations+  let hdr = m ^. msg1004_header+  obs <- concatMapM fromObservation1004 $ m ^. msg1004_observations   let chunks    = zip [0..] $ chunksOf maxObsPerMessage obs       totalMsgs = fromIntegral $ length chunks   forM chunks $ uncurry $ chunkToMsgObs hdr totalMsgs@@ -519,11 +527,11 @@   (RTCM3Msg1002 m _rtcm3) -> do     let sender = m ^. msg1002_header ^. gpsObservationHeader_station     m' <- fromMsg1002 m-    return $ flip fmap m' $ \x -> SBPMsgObsDepC x $ toSBP x $ toSender sender+    return $ flip fmap m' $ \x -> SBPMsgObs x $ toSBP x $ toSender sender   (RTCM3Msg1004 m _rtcm3) -> do     let sender = m ^. msg1004_header ^. gpsObservationHeader_station     m' <- fromMsg1004 m-    return $ flip fmap m' $ \x -> SBPMsgObsDepC x $ toSBP x $ toSender sender+    return $ flip fmap m' $ \x -> SBPMsgObs x $ toSBP x $ toSender sender   (RTCM3Msg1005 m _rtcm3) -> do     let sender =  m ^. msg1005_reference ^. antennaReference_station     m' <- fromMsg1005 m@@ -542,4 +550,4 @@ newStore = do   day <- utctDay <$> getCurrentTime   let wn = fromIntegral $ div (diffDays day (fromGregorian 1980 1 6)) 7-  Store <$> newIORef wn <*> newIORef mempty <*> newIORef mempty+  Store <$> newIORef wn <*> newIORef mempty
src/Data/RTCM3/SBP/Types.hs view
@@ -30,15 +30,7 @@ import Data.Word import SwiftNav.SBP -data Lock = Lock-  { _lockTime    :: Word8-  , _lockCounter :: Word16-  } deriving ( Eq )--$(makeLenses ''Lock)--type GpsTimeMap = HashMap Word16 GpsTime-type LockMap    = HashMap (Word16, GnssSignal) Lock+type GpsTimeMap = HashMap Word16 GpsTimeNano  instance Hashable GnssSignal where   hashWithSalt s g = hashWithSalt s (g ^. gnssSignal_sat, g ^. gnssSignal_code, g ^. gnssSignal_reserved)@@ -74,7 +66,6 @@  data Store = Store   { _storeWn         :: IORef Word16-  , _storeLockMap    :: IORef LockMap   , _storeGpsTimeMap :: IORef GpsTimeMap   } deriving ( Eq ) 
test/Test/Data/RTCM3/SBP.hs view
@@ -23,7 +23,6 @@ import           Data.IORef import           Data.RTCM3.SBP import           Data.RTCM3.SBP.Types-import           Data.Word import           SwiftNav.SBP import           Test.HUnit.Approx import           Test.Tasty@@ -31,7 +30,7 @@  decodeRTCMFile :: FilePath -> IO [SBPMsg] decodeRTCMFile filename = do-  s <- Store <$> newIORef 1906 <*> newIORef mempty <*> newIORef mempty+  s <- Store <$> newIORef 1906 <*> newIORef mempty   runResourceT $ runConvertT s $ runConduit  $     sourceFile filename   =$=     conduitDecode         =$=@@ -44,27 +43,27 @@                     , msg' ^. msgBasePosEcef_z                     ) -isL1 :: PackedObsContentDepC -> Bool-isL1 obs = (obs ^. packedObsContentDepC_sid ^. gnssSignal_code) == l1CSidCode+isL1 :: PackedObsContent -> Bool+isL1 obs = (obs ^. packedObsContent_sid ^. gnssSignal16_code) == l1CSidCode -isL2 :: PackedObsContentDepC -> Bool+isL2 :: PackedObsContent -> Bool isL2 obs = code == l2CMSidCode || code == l2PSidCode   where-    code = obs ^. packedObsContentDepC_sid ^. gnssSignal_code+    code = obs ^. packedObsContent_sid ^. gnssSignal16_code -sat :: PackedObsContentDepC -> Word16-sat obs = obs ^. packedObsContentDepC_sid ^. gnssSignal_sat+sat :: PackedObsContent -> Word8+sat obs = obs ^. packedObsContent_sid ^. gnssSignal16_sat -cn0 :: PackedObsContentDepC -> Double-cn0 obs = 0.25 * fromIntegral (obs ^. packedObsContentDepC_cn0)+cn0 :: PackedObsContent -> Double+cn0 obs = 0.25 * fromIntegral (obs ^. packedObsContent_cn0) -pseudorange :: PackedObsContentDepC -> Double-pseudorange obs = fromIntegral (obs ^. packedObsContentDepC_P) / sbpCMinM+pseudorange :: PackedObsContent -> Double+pseudorange obs = fromIntegral (obs ^. packedObsContent_P) / sbpCMinM -carrierPhase :: PackedObsContentDepC -> Double+carrierPhase :: PackedObsContent -> Double carrierPhase obs = whole + fraction   where-    phase    = obs ^. packedObsContentDepC_L+    phase    = obs ^. packedObsContent_L     whole    = fromIntegral (phase ^. carrierPhase_i)     fraction = fromIntegral (phase ^. carrierPhase_f) / q32Width @@ -73,22 +72,22 @@   assertEqual "Base station position error!" pos $ basePosition posEcef assertExpectedBase _                             _   = assertFailure "Invalid message type!" -assertObsHeader :: SBPMsg -> ObservationHeaderDep -> Assertion-assertObsHeader (SBPMsgObsDepC obs _) header =-  assertEqual "Observation header is not equal" header $ obs ^. msgObsDepC_header+assertObsHeader :: SBPMsg -> ObservationHeader -> Assertion+assertObsHeader (SBPMsgObs obs _) header =+  assertEqual "Observation header is not equal" header $ obs ^. msgObs_header assertObsHeader _                 _      = assertFailure "Invalid message type!" -assertObs :: HashMap Word16 (Double, Double, Double)-          -> PackedObsContentDepC+assertObs :: HashMap Word8 (Double, Double, Double)+          -> PackedObsContent           -> Double        -- ^ Pseudorange tolerance (meters)           -> Double        -- ^ Carrier phase tolerance (cycles)           -> Word8         -- ^ Satellite band code           -> Assertion assertObs truth obs ptol ctol sig = do-  let prn         = 1 + sat obs+  let prn         = sat obs       def         = (0, 0, 0)       (p, c, snr) = lookupDefault def prn truth-      code        = obs ^. packedObsContentDepC_sid ^. gnssSignal_code+      code        = obs ^. packedObsContent_sid ^. gnssSignal16_code       msg'        = textToString $ "PRN=" ++ show prn ++ " CODE=" ++ show code   -- Pseudorange representation error   assertApproxEqual ("Incorrect pseudorange" ++ msg')   ptol p $ pseudorange obs@@ -99,11 +98,11 @@   assertEqual ("Incorrect code" ++ msg') sig   code  assertMsgObs :: SBPMsg -> Assertion-assertMsgObs (SBPMsgObsDepC obs' _) = do+assertMsgObs (SBPMsgObs obs' _) = do   let ptol    = 0.020 -- 2cm representation errorn       ctol_L1 = 0.005 -- carrier phase / L1 wavelength = 0.001m/0.190m cycles       ctol_L2 = 0.004 -- carrier phase / L2 wavelength = 0.001m/0.250m cycles-  forM_ (obs' ^. msgObsDepC_obs) $ \packed ->+  forM_ (obs' ^. msgObs_obs) $ \packed ->     case () of       _ |  isL1 packed -> assertObs testObs_L1 packed ptol ctol_L1 l1CSidCode         |  isL2 packed -> assertObs testObs_L2 packed ptol ctol_L2 l2PSidCode@@ -111,13 +110,13 @@ assertMsgObs _                  =  assertFailure "Invalid message type!"  assertMsgObsLength :: SBPMsg -> Int -> Assertion-assertMsgObsLength (SBPMsgObsDepC obs' _) len = length (obs' ^. msgObsDepC_obs) @?= len+assertMsgObsLength (SBPMsgObs obs' _) len = length (obs' ^. msgObs_obs) @?= len assertMsgObsLength _                  _   = assertFailure "Invalid message type!"  -- | L1 observations: PRN => Pseudorange, Carrier Phase, SNR -- -- From fixtures/rinex/ucsf_bard_four_seconds.obs RINEX-testObs_L1 :: HashMap Word16 (Double, Double, Double)+testObs_L1 :: HashMap Word8 (Double, Double, Double) testObs_L1 = fromList [ (5,  (20982568.242,  110264218.428, 50.500))                       , (31, (24390286.418,  128171817.525, 43.000))                       , (25, (20560669.144,  108047101.226, 51.750))@@ -131,7 +130,7 @@ -- | L2 observations: PRN => Pseudorange, Carrier Phase, SNR -- -- From fixtures/rinex/ucsf_bard_four_seconds.obs RINEX-testObs_L2 :: HashMap Word16 (Double, Double, Double)+testObs_L2 :: HashMap Word8 (Double, Double, Double) testObs_L2 = fromList [ (5,  (20982564.742, 85920194.806, 40.000))                       , (31, (24390281.338, 99874145.710, 22.750))                       , (25, (20560668.244, 84192554.659, 42.750))@@ -155,41 +154,41 @@         assertExpectedBase (msgs !! 0) expectedBasePos         assertExpectedBase (msgs !! 5) expectedBasePos         -- Message 1-        assertObsHeader (msgs !! 1) ObservationHeaderDep {-            _observationHeaderDep_t     = GpsTime 86354000 1906-          , _observationHeaderDep_n_obs = 32+        assertObsHeader (msgs !! 1) ObservationHeader {+            _observationHeader_t     = GpsTimeNano 86354000 0 1906+          , _observationHeader_n_obs = 32           }         assertMsgObsLength (msgs !! 1) 15         assertMsgObs (msgs !! 1)         -- Message 2-        assertObsHeader (msgs !! 2) ObservationHeaderDep {-            _observationHeaderDep_t     = GpsTime 86354000 1906-          , _observationHeaderDep_n_obs = 33+        assertObsHeader (msgs !! 2) ObservationHeader {+            _observationHeader_t     = GpsTimeNano 86354000 0 1906+          , _observationHeader_n_obs = 33           }         assertMsgObs (msgs !! 2)         assertMsgObsLength (msgs !! 2) 1         -- Message 3-        assertObsHeader (msgs !! 3) ObservationHeaderDep {-            _observationHeaderDep_t     = GpsTime 86355000 1906-          , _observationHeaderDep_n_obs = 32+        assertObsHeader (msgs !! 3) ObservationHeader {+            _observationHeader_t     = GpsTimeNano 86355000 0 1906+          , _observationHeader_n_obs = 32           }         assertMsgObsLength (msgs !! 3) 15         -- Message 4-        assertObsHeader (msgs !! 4) ObservationHeaderDep {-            _observationHeaderDep_t     = GpsTime 86355000 1906-          , _observationHeaderDep_n_obs = 33+        assertObsHeader (msgs !! 4) ObservationHeader {+            _observationHeader_t     = GpsTimeNano 86355000 0 1906+          , _observationHeader_n_obs = 33           }         assertMsgObsLength (msgs !! 4) 1         -- Message 6-        assertObsHeader (msgs !! 6) ObservationHeaderDep {-            _observationHeaderDep_t     = GpsTime 86356000 1906-          , _observationHeaderDep_n_obs = 32+        assertObsHeader (msgs !! 6) ObservationHeader {+            _observationHeader_t     = GpsTimeNano 86356000 0 1906+          , _observationHeader_n_obs = 32           }         assertMsgObsLength (msgs !! 6) 15         -- Message 7-        assertObsHeader (msgs !! 7) ObservationHeaderDep {-            _observationHeaderDep_t     = GpsTime 86356000 1906-          , _observationHeaderDep_n_obs = 33+        assertObsHeader (msgs !! 7) ObservationHeader {+            _observationHeader_t     = GpsTimeNano 86356000 0 1906+          , _observationHeader_n_obs = 33           }         assertMsgObsLength (msgs !! 7) 1      ]@@ -215,34 +214,17 @@ testUpdateGpsTime =   testGroup "Update GPS Time"     [ testCase "old TOW < new TOW" $ do-        let old = GpsTime 1 10+        let old = GpsTimeNano 1 0 10             new = updateGpsTime 2 old-        new ^. gpsTime_wn @?= 10+        new ^. gpsTimeNano_wn @?= 10     , testCase "old TOW = new TOW" $ do-        let old = GpsTime 1 10+        let old = GpsTimeNano 1 0 10             new = updateGpsTime 1 old-        new ^. gpsTime_wn @?= 10+        new ^. gpsTimeNano_wn @?= 10     , testCase "old TOW > new TOW" $ do-        let old = GpsTime 1 10+        let old = GpsTimeNano 1 0 10             new = updateGpsTime 0 old-        new ^. gpsTime_wn @?= 11-    ]--testUpdateLock :: TestTree-testUpdateLock =-  testGroup "Update Lock"-    [ testCase "old time < new time" $ do-        let old = Lock 1 10-            new = updateLock 2 old-        new ^. lockCounter @?= 10-    , testCase "old time = new time" $ do-        let old = Lock 1 10-            new = updateLock 1 old-        new ^. lockCounter @?= 10-    , testCase "old time > new time" $ do-        let old = Lock 1 10-            new = updateLock 0 old-        new ^. lockCounter @?= 11+        new ^. gpsTimeNano_wn @?= 11     ]  tests :: TestTree@@ -251,5 +233,4 @@     [ testMsg1004     , testToWn     , testUpdateGpsTime-    , testUpdateLock     ]