packages feed

odid 0.1.0.0 → 1.0.0.0

raw patch · 6 files changed

+111/−86 lines, 6 filesdep −hedgehogdep −tasty-hedgehogPVP ok

version bump matches the API change (PVP)

Dependencies removed: hedgehog, tasty-hedgehog

API changes (from Hackage documentation)

+ Data.ODID: [authType] :: AuthMsg -> AuthType
+ Data.ODID: [pageN] :: AuthMsg -> Maybe (Word8, Word8, Word32)
+ Data.ODID: [pageNum] :: AuthMsg -> Word8
+ Data.ODID: [signature] :: AuthMsg -> ByteString

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for odid -## 0.1.0.0 -- YYYY-mm-dd+## 1.0.0.0 -- 9-29-2026++* Update Auth record fields, decoding, CLI parsers+* Improve CLI & docs++## 0.1.0.0 -- 9-24-2026  * First version. Released on an unsuspecting world.
README.md view
@@ -1,6 +1,6 @@ # Open Drone ID -ASTM F3411-22a+[ASTM F3411-22a](https://store.astm.org/f3411-22a.html)  Usage @@ -9,11 +9,18 @@ odid w basic foo | odid r ``` -Development+Install  ```+cabal install odid+```++[Development](https://www.haskell.org/ghcup/)++``` cabal build cabal test cabal haddock+cabal run odid -- w basic foo bar.bin cabal install ```
exe/Main.hs view
@@ -11,6 +11,7 @@ import Data.Int import Data.ODID import Data.Version+import Numeric import Options.Applicative import Paths_odid import Prettyprinter@@ -78,7 +79,7 @@ parseUAType :: Parser UAType parseUAType = asum $ map mkFlag [None ..]   where-    mkFlag None = flag None None $ long "none"+    mkFlag None = flag None None $ long "none" <> help "Default UA type"     mkFlag GroundObstacle = flag' GroundObstacle $ long "ground-obstacle"     mkFlag t = flag' t $ long $ map toLower $ show t @@ -86,15 +87,16 @@ parseUASID = pad 20 <$> strArgument (metavar "UASID")  selfIDParser :: Parser Msg-selfIDParser = fmap (Msg $ MsgHdr 2 SelfIDTy) $-  SelfIDBdy <$> option auto (short 't' <> value 0 <> showDefault <> help "Description type")-    <*> pad 23 `fmap` strArgument (metavar "DESC" <> help "Description")+selfIDParser = fmap (Msg $ MsgHdr 2 SelfIDTy) $ SelfIDBdy+  <$> option auto (short 't' <> value 0 <> showDefault <> metavar "WORD8" <> help "Description type")+  <*> pad 23 `fmap` strArgument (metavar "ASCII" <> help "Description")  opIDParser :: Parser Msg opIDParser = fmap (Msg $ MsgHdr 2 OperatorID) $   OpIDBdy <$> parseOpIDTy <*> parseOpID <*> parseRsvdBytes   where-    parseOpIDTy = option auto $ short 't' <> help "Operator ID type"+    parseOpIDTy = option auto $ short 't' <> value 0 <> showDefault <> metavar "WORD8"+      <> help "Operator ID type"     parseOpID = fmap (pad 20) $ strArgument $ metavar "ID" <> help "ASCII text"  pad :: Int64 -> String -> ByteString@@ -113,22 +115,26 @@   LocMsg <$> opStatusParser <*> switch (long "flag-rsvd" <> help "Reserved flag")     <*> heightTypeParser     <*> switch (long "180" <> help "E/W direction segment switch. >=180 active otherwise <180")-    <*> speedMultParser <*> option auto (long "track-dir" <> help "Track direction 0-359 deg.")-    <*> option auto (long "speed" <> help "Ground speed m/s")-    <*> option auto (long "vert-speed" <> help "Vertical speed m/s")+    <*> speedMultParser+    <*> option auto (long "track-dir" <> value 0 <> showDefault <> help "Track direction 0-359 deg.")+    <*> option auto (long "speed" <> value 0 <> showDefault <> help "Ground speed m/s")+    <*> option auto (long "vert-speed" <> value 0 <> showDefault <> help "Vertical speed m/s")     <*> parseLat <*> parseLon-    <*> option auto (long "pres-alt" <> help "Pressure altitude")-    <*> option auto (long "geo-alt" <> help "Geodetic altitude")-    <*> option auto (long "height") <*> vertAccParser "vert-acc"+    <*> option auto (long "pres-alt" <> value 0 <> showDefault <> help "Pressure altitude")+    <*> option auto (long "geo-alt" <> value 0 <> showDefault <> help "Geodetic altitude")+    <*> option auto (long "height" <> value 0 <> showDefault <> help "Height m")+    <*> vertAccParser "vert-acc"     <*> horizAccParser <*> vertAccParser "baro-acc" <*> speedAccParser-    <*> option auto (long "timestamp") <*> option auto (long "tstamp-acc-rsvd")-    <*> option auto (long "tstamp-acc") <*> option auto (long "loc-rsvd")+    <*> option auto (long "timestamp" <> value 0 <> showDefault <> help "seconds since last hour 0.1 step")+    <*> option auto (long "tstamp-acc-rsvd" <> value 0 <> showDefault <> help "Reserved")+    <*> option auto (long "tstamp-acc" <> value 0 <> showDefault <> help "timestamp accuracy 0-1.5 s 0.1 resolution")+    <*> option auto (long "loc-rsvd" <> value 0 <> showDefault <> help "Reserved")  parseLat :: Parser Double-parseLat = option auto $ long "lat" <> help "Latitude"+parseLat = option auto $ long "lat" <> value 0 <> showDefault <> help "Latitude"  parseLon :: Parser Double-parseLon = option auto $ long "lon" <> help "Longitude"+parseLon = option auto $ long "lon" <> value 0 <> showDefault <> help "Longitude"  opStatusParser :: Parser OpStatus opStatusParser = asum@@ -151,7 +157,7 @@  vertAccParser :: String -> Parser VertAcc vertAccParser s = asum-  [ fmap readAcc $ option auto $ long s <> help "Vertical accuracy m"+  [ fmap readAcc $ option auto $ long s <> value 0 <> showDefault <> help "Vertical accuracy m"   , fmap VertAccRsvd $ option auto $ long (s ++ "-rsvd") <> help "Reserved low nibble"   ]   where@@ -167,7 +173,7 @@  horizAccParser :: Parser HorizAcc horizAccParser = asum-  [ fmap readAcc $ option auto $ long "horiz-acc" <> help "Horizontal accuracy m"+  [ fmap readAcc $ option auto $ long "horiz-acc" <> value 0 <> showDefault <> help "Horizontal accuracy m"   , fmap HorizAccRsvd $ option auto $ long "horiz-acc-rsvd"       <> help "Horizontal accuracy reserved value"   ]@@ -190,7 +196,7 @@  speedAccParser :: Parser SpeedAcc speedAccParser = asum-  [ fmap readAcc $ option auto $ long "speed-acc" <> help "Speed accuracy m/s"+  [ fmap readAcc $ option auto $ long "speed-acc" <> value 0 <> showDefault <> help "Speed accuracy m/s"   , fmap SpeedAccRsvd $ option auto $ long "speed-acc-rsvd"       <> help "Reserved speed accuracy low nibble"   ]@@ -267,11 +273,25 @@   <> help "Reserved"  authParser :: Parser Msg-authParser = fmap (Msg (MsgHdr 2 Auth) . AuthBdy) $+authParser = fmap (Msg (MsgHdr 2 Auth) . AuthBdy . padSig) $   AuthMsg <$> parseAuthType <*> parsePageNum <*> optional parsePage0 <*> parseSignature   where-    parseSignature = fmap BS.pack $ some $ argument auto $ metavar "BYTE" <> help "Signature bytes"+    parseSignature = option (eitherReader bytes) $ short 's' <> long "sig" <> metavar "BYTES"+      <> help "Signature bytes base-16. Example: 0x0123cafe"+    padSig a | pageNum a == 0 = a{signature=pad 17 $ BSC.unpack $ signature a}+             | otherwise = a{signature=pad 23 $ BSC.unpack $ signature a} +bytes :: String -> Either String ByteString+bytes s = BS.pack <$> case s of+  '0':'x':rest -> go rest+  _ -> go s+  where+    go (h:l:rest) = case readHex [h,l] of+      [(byte,"")] -> (byte:) <$> go rest+      _ -> Left $ "cannot read byte 0x" ++ [h,l]+    go [c] = Left $ "cannot read byte 0x" ++ [c]+    go [] = Right []+ parseAuthType :: Parser AuthType parseAuthType = asum   [ flag AuthNone AuthNone $ long "none" <> help "No auth, default."@@ -285,7 +305,7 @@   ]  parsePageNum :: Parser Word8-parsePageNum = option auto $ long "page-num" <> help "Page number"+parsePageNum = option auto $ long "page-num" <> value 0 <> showDefault <> help "Page number"  parsePage0 :: Parser (Word8, Word8, Word32) parsePage0 = (,,)
lib/Data/ODID.hs view
@@ -151,7 +151,8 @@  data MsgBdy = BasicIDBdy IDType UAType UASID ByteString | LocBdy LocMsg | AuthBdy AuthMsg   | SelfIDBdy Word8 ByteString | SysBdy SysMsg | OpIDBdy Word8 ByteString ByteString-  | PackBdy Word8 Word8 [Msg] | RsvdBdy ByteString+  | -- | Message size 0x19, num msgs+    PackBdy Word8 Word8 [Msg] | RsvdBdy ByteString   deriving (Eq, Read, Show)  instance Pretty MsgBdy where@@ -164,7 +165,7 @@     SelfIDBdy ty desc -> vsep ["Type:" <+> pretty ty, "Desc:" <+> pretty (BSC.unpack desc)]     SysBdy s -> pretty s     OpIDBdy t i r -> vsep ["Type:" <+> pretty t, "ID:" <+> pretty (BSC.unpack i)-      , "Rsvd:" <+> pretty (BSC.unpack r)]+      , "Rsvd:" <+> prettyBytes r]     PackBdy sz nm ms -> vsep ["Size=" <> pretty sz <+> "Cnt=" <> pretty nm       , indent 2 $ vsep $ pretty <$> ms]     RsvdBdy bs -> "Reserved:" <+> prettyBytes bs@@ -185,7 +186,7 @@     IDTypeRsvd r -> "Reserved" <+> pretty r  data HorizAcc-  = GT10NM  -- ^ >=18.52 km (10 NM) or Unknown+  = GT10NM  -- ^ \>=18.52 km (10 NM) or Unknown   | LT10NM  -- ^ <18.52 km (10 NM)   | LT4NM   -- ^ <7.408 km (4 NM)   | LT2NM   -- ^ <3.704 km (2 NM)@@ -226,7 +227,7 @@   pretty c = viaShow c  data VertAcc-  = VertAccGTE150M -- ^ >=150 m or Unknown+  = VertAccGTE150M -- ^ \>=150 m or Unknown   | VertAccLT150M-- ^ <150 m   | VertAccLT45M -- ^ <45 m   | VertAccLT25M -- ^ <25 m@@ -523,10 +524,10 @@     , "Reserved:" <+> pretty (sysRsvd s)     ] -data AuthMsg = AuthMsg AuthType-  Word8 -- ^ Page number-  (Maybe (Word8, Word8, Word32)) -- ^ PageN last page index, length, timestamp-  ByteString -- ^ signature+data AuthMsg = AuthMsg{authType :: AuthType, pageNum :: Word8+  , pageN :: Maybe (Word8, Word8, Word32) -- ^ last page index, length, timestamp+  , signature :: ByteString+  }   deriving (Eq, Read, Show)  instance Binary AuthMsg where@@ -547,11 +548,10 @@         AuthMsg ty pge (Just z) <$> getLazyByteString 17       else AuthMsg ty pge Nothing <$> getLazyByteString 23 -  put (AuthMsg ty pge (Just (lpi, l, ts)) sig) = do-    putWord8 $ writeAuthType ty `shiftL` 4 .|. pge .&. 0xF-    putWord8 lpi <> putWord8 l <> putWord32le ts <> putLazyByteString sig-  put (AuthMsg ty pge Nothing sig) = do+  put (AuthMsg ty pge zM sig) = do     putWord8 $ writeAuthType ty `shiftL` 4 .|. pge .&. 0xF+    forM_ zM $ \(lpi, l, ts) ->+      putWord8 lpi <> putWord8 l <> putWord32le ts     putLazyByteString sig  instance Pretty AuthMsg where
odid.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               odid-version:            0.1.0.0+version:            1.0.0.0 synopsis:           Open Drone ID description:        ASTM F3411-22a homepage:           https://github.com/dopamane/odid@@ -14,6 +14,10 @@ extra-doc-files:    CHANGELOG.md                     README.md +source-repository head+  type: git+  location: https://github.com/dopamane/odid+ library   default-language: Haskell2010   ghc-options:      -Wall -O2@@ -50,8 +54,6 @@     base < 5,     binary,     bytestring,-    hedgehog,     odid,     tasty,-    tasty-hedgehog,     tasty-hunit
test/Main.hs view
@@ -5,60 +5,51 @@ import Data.Binary import qualified Data.ByteString.Lazy as BS import Data.ODID-import Hedgehog-import qualified Hedgehog.Gen   as Gen-import qualified Hedgehog.Range as Range import Test.Tasty-import Test.Tasty.Hedgehog import Test.Tasty.HUnit  main :: IO ()-main = defaultMain $ testGroup "Test.ODID" [testBasicID, testLocation]+main = defaultMain $ testGroup "Test.ODID" [loopback "BasicID" basicID+  , loopback "Location" loc, loopback "Auth0" auth0, loopback "AuthN" authN+  , loopback "SelfID" selfID, loopback "Sys" sys, loopback "OpID" opID+  , loopback "Pack" msgPack] -testBasicID :: TestTree-testBasicID = testCase "BasicID" $ do-  let input = Msg (MsgHdr 2 BasicIDTy) $-        BasicIDBdy CAARegID Heli "01234567890123456789" $ BS.replicate 3 0x00-  decode (encode input) @?= input+loopback :: (Binary a, Eq a, Show a) => String -> a -> TestTree+loopback n d = testCase n $ decode (encode d) @?= d -testLocation :: TestTree-testLocation = testCase "Location" $ do-  let input = Msg (MsgHdr 2 Location) $ LocBdy-        LocMsg{locOpStatus=Ground, locFlagsRsvd=False, locHeightType=AGL-          , locFlagsDir=False, locFlagsMult=False, locTrackDir=135-          , locSpeed=5, locVertSpeed=7.5, locLat=34.0522, locLon=118.2437-          , locPresAlt=10.5, locGeoAlt=8.5, locHeight=2-          , locVertAcc=VertAccLT3M, locHorzAcc=LT1M-          , locBaroAltAcc=VertAccLT3M, locSpeedAcc=LT1MS, locTimestamp=0-          , locTStampAccRsvd=0, locTStampAcc=0.1, locRsvd=0}-  decode (encode input) @?= input+basicID :: Msg+basicID = Msg (MsgHdr 2 BasicIDTy) $+  BasicIDBdy CAARegID Heli "01234567890123456789" $ BS.replicate 3 0x00 -{--testMsgBinaryTrip :: TestTree-testMsgBinaryTrip = testProperty "Msg" $ property $ binTrip =<< forAll genMsg+loc :: Msg+loc = Msg (MsgHdr 2 Location) $ LocBdy+  LocMsg{locOpStatus=Ground, locFlagsRsvd=False, locHeightType=AGL+        , locFlagsDir=False, locFlagsMult=False, locTrackDir=135+        , locSpeed=5, locVertSpeed=7.5, locLat=34.0522, locLon=118.2437+        , locPresAlt=10.5, locGeoAlt=8.5, locHeight=2+        , locVertAcc=VertAccLT3M, locHorzAcc=LT1M+        , locBaroAltAcc=VertAccLT3M, locSpeedAcc=LT1MS, locTimestamp=0+        , locTStampAccRsvd=0, locTStampAcc=0.1, locRsvd=0} -genMsg :: MonadGen m => m Msg-genMsg = do-  ver <- Gen.word8 $ Range.linear 0 15-  typ <- Gen.element msgTypes-  Msg (MsgHdr ver typ) <$> case typ of-    BasicIDTy -> BasicIDBdy <$> Gen.enumBounded <*> Gen.enumBounded-      <*> BS.fromStrict `fmap` Gen.bytes (Range.singleton 20)-      <*> BS.fromStrict `fmap` Gen.bytes (Range.singleton 3)-    Location -> LocBdy <$> Gen.discard-    Auth -> Gen.discard-    SelfIDTy -> SelfIDBdy <$> Gen.enumBounded-      <*> BS.fromStrict `fmap` Gen.bytes (Range.singleton 23)-    System -> Gen.discard-    OperatorID -> OpIDBdy <$> Gen.enumBounded-      <*> BS.fromStrict `fmap` Gen.bytes (Range.singleton 20)-      <*> BS.fromStrict `fmap` Gen.bytes (Range.singleton 3)-    Pack -> Gen.discard+selfID :: Msg+selfID = Msg (MsgHdr 2 SelfIDTy) $ SelfIDBdy 1 $ BS.pack [0..22] -genOpStatus :: MonadGen m => m OpStatus-genOpStatus = Gen.choice $ OpStatusRsvd `fmap` Gen.word8 (Range.linear 0 15) : map pure-  [Undeclared, Ground, Airborne, Emergency, RemoteIDSystemFailure]+opID :: Msg+opID = Msg (MsgHdr 2 OperatorID) $ OpIDBdy 0 (BS.pack [0..19]) $ BS.pack [1, 2, 3] -binTrip :: (MonadTest m, Show a, Eq a, Binary a) => a -> m ()-binTrip d = tripping d encode $ fmap (\(_, _, a) -> a) . decodeOrFail--}+sys :: Msg+sys = Msg (MsgHdr 2 System) $ SysBdy+  SysMsg{sysClassType=EuroUnion, sysOpSrcType=Takeoff, sysOpLat=34.0575762+        , sysOpLon=(-118.2405026), sysArCnt=1, sysArRad=10, sysArCeil=10.5+        , sysArFloor=10.5, sysClassCat=Open, sysClassClass=3, sysOpAlt=24.5+        , sysTimestamp=12345, sysRsvd=0xAB}++auth0 :: Msg+auth0 = Msg (MsgHdr 2 Auth) $ AuthBdy $ AuthMsg AuthNone 0 (Just (0, 255, 120)) $+  BS.pack [0..16]++authN :: Msg+authN = Msg (MsgHdr 2 Auth) $ AuthBdy $ AuthMsg UASIDSig 1 Nothing $ BS.pack [0..22]++msgPack :: Msg+msgPack = Msg (MsgHdr 2 Pack) $ PackBdy 0x19 7 [basicID, loc, selfID, opID, sys, auth0, authN]