semuconsulting / pyspartn

Python library for parsing SPARTN protocol messages.
BSD 3-Clause "New" or "Revised" License
7 stars 3 forks source link

SPARTN-1X-HPAC-GPS satellite mask parsing (probbaly) off #29

Closed jonathanmuller closed 3 months ago

jonathanmuller commented 3 months ago

Hi, I have been playing with pyspartn a bit more and am now close to result I hope, thus my ability to now find errors deep in the parsing.

When using version 1.0.2 (latest at the time of writing), the parsing of SPARTN-1X-HPAC-GPS satellite mask looks wrong. Too many bits are read. Field of interest is SF011. Definition is :

Leftmost 2 bits indicate bitmask size to follow
0 : 32 bits
1 : 44 bits
2 : 56 bits
3 : 64 bits

So typically, with first 2 bits as "01", 44 bits should be read. However 46 bits are read. 46 is the total field size, however 2 bits are already read.

To Reproduce

payload = bytes.fromhex("7302dfec38db7b1fa05b14c8abf0841e136d0e55dcda5cf7440c399537f66ee9add1b9fb0ebc1c10e09d790ff561d3763b3e1287ac1384dbcdec28b968ab77aac1da6cae785cbaa21d9a50a8ac8385e7b605bbcacb0e954f1c1d53815ab69553770f25cb912a7dfabfe590374371fc07def2da832f82697af5815bdc5a21dbe0f655fe85e8b0fc319f94d4366c0f8b7da564d952b5b0654af600ab39cb0bab35411c2c85046464a999e6510c23a3f3617ad49a868627e9eae5d3ca6400b9f2ce2067d6bdeaa9924fe8a0b4823b0ad1ea447238f8659292ea928feb3733f51a795dd3858ef4e340eb8e40dd454b011756ee506e1e9c74a59f1c50546baba4da9e739e91ae8582f01edb2bdfdf98c7163a664205223f8c9b70c77c4e63184260cd4f9486e3fd8fbd7b954766e89d5dc3d6b8b59ddc6804103e86912aff1524b1202a52d7c0e8406ba5ecefdf0c2c093c8e984e67dc62f3508bd07ac44bdf30ae8df66de478019405aa373d422b2e069eeb4eff4c14edcb7881a939f8a8cdfbf8752e28fc375c3bfc4ab70ec1802c1cab04feecbd5cb4d97966cdfb5d1a76baf8432402da5d6ba3808e42d0cdc3f952c3993a9f71716abdcc7e53c30e8a5542f5f5d1a1f95ef0a22564fecedd6335407302d1eb38db7b1fa05b1508fe82ed5f6427ac94ee8c1869b413cf5212489c83c8247e503c8291d261fdaa401f80b95d5f6a45b2b32730be3a3b8ab5a82ef1fb8df71fb663e5d494933c053ad2ab6bb43889375c3f67c382121e44d46b0d6d1fe516a35032af3a30e914250f49238c6e3367ca6a1acb60c476d4e6662f31f914b8e9e65fa9c5c9350c7951a6cefdb66acb3fe6dfb82fce760a7707179e59b85a8adb51841019c542921fc58a532de2529fd4a548fea7bf4c3ebf14e7616ae5b138beeb217bb251f3aebfd2d6b63253912174c604879b9b7fa0a1ec430c3528122b690f2fb800d9c3c2848f97eaa5740968abb79e865bb61899c99e2c50483af7d281f339f089f61bd35c031ddde5bf2b129372e8d840f0a6f5dfd9d96da54ebdfd5109176c10570bd996f464e26cb49475127f67ce870dc8c8e0513c3c9a359cc6ad3377de6600c1d6e32bebbeb5ca55bebab885d716c3b2b14b8f0112180cd918e0bb87f07d4a5a6149bb752ead5a4f00938323b59b1fe40d71b2286917383e6dc0330b89f23b70f6e6eedf18c01e8161e3af45595889c07eec100ee59fe5a8b859a6f6ab94595180a5937740ed24cfa0af7302ceed38db7b1fa05b154834bc1fd26efa4642733718086ba2724db1aeca8921e51513ce28e1c2b63e8aaeb07ecc79b0124b72c32ce7abdafceb9a845e7041824d96b80aa285a4b9d1f157acf31a958258770083e69ccd79ad6db549371357715881f9003aeac8af2ed085a95c402c9004dad4659bd207d007e2477ff308714bc4b06274402939d6a6aeffa7ac7636807db23feb6985b87b9eec42294d1d14c30ca37c50f7f8ccb590bd61c916bc7acc55dbccfcac6ad3678dd3ee98c3091ad9d9f4ee8b6180f4994f0cad0cbc2dddde8eeffa6e71bf47487a6ba54e0291d284230603c01b73b73f545ab3c8a0a1a3d2c93b3615d48ff7ac4e07bc6395214cfd5eb5a20d70f98b00e685b312a7a2046c37f5670787fe23303496ff807c6f7cb94a6b453cdcde901db537cc315a794c2189ec376db92a09721e9f7c20878cb959b9e7864e4d1c4af2434db467459e46223c300abc55a56b355f6474f3ba0e2c2b4db4db0a1856e5958be1e2ea4ec7fea6577300ed9383293503b286d4ea49b2f9f3aeed828b9aad3b920b38ed024497244e906356072eb4710602863b8fdd5f8146eed2120a5b8d02e84f217302c06338db7b1fa05b1588567a097223f7f09dc90c3c1c0d85ad8299c8ad4d0aa969d01a6f482dafb86e676c90dbde180f51313ee8a38745d3e6f33fecaf50f438f651a0b42ee93549f02d8910899b4e4bf5fb0514560b8fce55f7f95756158ec80ba47413613201ba66da52d8fab96ec9c25becb6b170269830f76483e2d9fdcc01dca9f7f28336197149f55dc9cc5d5a4d82681792fc3a0aaaf700759470a4035b55090423951a0c8bea009e7bdd1daa21ca528aba7e621417d416115735c6002a4a92dd520c1c3c416b4efbd1175423cd9a77b35197f39556dbca2d8e41529b541b63d450e3bf94565bf6255e9e71eeaf3e69c495604a34e5c0490766c2f57aa8d33631b468af6c2e56a9b325bbe6988f277aa20f332880331da79e810c56463e5f8cf7aeb0292a0ea17c6f748113070d04bf8fc4a735ce5f577180f5be82381a0985f8769c76b086cd192c799a4cee4f0e3bbb59112e81e12169b1523d019236d5e7c9f413531f6093c44e460866b4534ff4033385aee4561eaf7ecc85b29285129c332fc9ab457a0478b8f273022d6538db7b1fa05b15c87006e3601505d12cb65f9b43a400a3d13bd4aef0647d4691533ac61450bdc06c811202376ec73454437e7ead5e103947caed003125cc47bbd2cace0c0465274d4f1458164bf98db035280c08884769654c9a0ffa16dac70cf8b67b84b7")
key = "930d847b779b126863c8b3b2766ae7cc"
date_str = "2024-08-02 08:55:17.221124"
base_date = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S.%f")
spr = SPARTNReader(BytesIO(payload), decode=True, key=key, basedate=base_date)

With debug enabled, this produces (look offset 85) :

Offset:0     read_length:9       key:SF005                val:281
Offset:9     read_length:4       key:SF068                val:1
Offset:13    read_length:1       key:SF069                val:0
Offset:14    read_length:5       key:SF030                val:9
Offset:19    read_length:8       key:SF031_01             val:0
Offset:27    read_length:7       key:SF039_01             val:0
Offset:34    read_length:2       key:SF040T_01            val:1
Offset:36    read_length:2       key:SF040I_01            val:1
Offset:38    read_length:3       key:SF041_01             val:1
Offset:41    read_length:3       key:SF042_01             val:1
Offset:44    read_length:8       key:SF043_01             val:0.0
Offset:52    read_length:1       key:SF044_01             val:1
Offset:53    read_length:9       key:SF048_01             val:-0.06000000000000005
Offset:62    read_length:9       key:SF049a_01            val:0.013000000000000012
Offset:71    read_length:9       key:SF049b_01            val:-0.0020000000000000018
Offset:80    read_length:3       key:SF054_01             val:1
Offset:83    read_length:2       key:SatBitmaskLen_01     val:1
Offset:85    read_length:46      key:SF094_01             val:34350104  <--- Here the problem start
Offset:131   read_length:0       key:PRN_01_01            val:21 <--- now we can see the problem
Offset:131   read_length:4       key:SF055_01_01          val:2 <-- everything is off by 2 bits now
...

And here is how I log the parsing :

@@ -385,6 +385,7 @@ class SPARTNMessage:

         except SPARTNMessageError as err:
             raise err
+        print(f"Offset:{offset} \t read_length:{str(attlen).ljust(4, ' ')} \t key:{anami.ljust(20, ' ')} val:{val}")

         setattr(self, anami, val)

Expected Behaviour

Manual parsing gives :

...
011110000 -> Tropo coeff T00    (I agree with 1.0.2)
100001100 -> Tropo coeff T0     (I agree with 1.0.2)
011111101 -> Tropo coeff T10    (I agree with 1.0.2)
001       -> Iono equation type (I agree with 1.0.2)
01        -> 44 bits to follow  (I agree with 1.0.2)
00000000000000000000100000110000100100000110 -> GPS sat mask (error) : I read 44 bits but 1.0.2 reads 46 bits
0000 -> Iono quality (error) : Now we read 2 bits too much and everything is off. Iono quality should be 0, but is read as 2 (see the next 2 bits of the stream)
1011000110... -> ... next bits of the stream

Additional context

SATBITMASKLEN = {
    "SF011": [32, 44, 56, 64],
    "SF012": [24, 36, 48, 63],
    "SF093": [36, 45, 54, 64],
    "SF094": [37, 46, 55, 64],
    "SF095": [10, 40, 48, 64],
}

Looks correct, so it's probably in the logic of the SATBITMASKLEN. I'll take a look and see if I can find the responsible line, and also if OCB messages are affected, as they are using the same parsing logic.

Have a good day

jonathanmuller commented 3 months ago

I obviously confused BDS with GPS, so the field parsing is correct =) There is still a bug later in the chain (maybe on my side ?), but the parsing is absolutely right on this field