@@ -9015,6 +9015,365 @@ bool DevUBLOXGNSS::setDynamicSPARTNKeys(uint8_t keyLengthBytes1, uint16_t validF
90159015  return (sendCommand(&packetCfg, 0) == SFE_UBLOX_STATUS_SUCCESS); // UBX-RXM-SPARTNKEY is silent. It does not ACK (or NACK)
90169016}
90179017
9018+ // Support for SPARTN parsing
9019+ // Mostly stolen from https://github.com/u-blox/ubxlib/blob/master/common/spartn/src/u_spartn_crc.c
9020+ 
9021+ uint8_t DevUBLOXGNSS::uSpartnCrc4(const uint8_t *pU8Msg, size_t size)
9022+ {
9023+     // Initialize local variables
9024+     uint8_t u8TableRemainder;
9025+     uint8_t u8Remainder = 0; // Initial remainder
9026+ 
9027+     // Compute the CRC value
9028+     // Divide each byte of the message by the corresponding polynomial
9029+     for (size_t x = 0; x < size; x++) {
9030+         u8TableRemainder = pU8Msg[x] ^ u8Remainder;
9031+         u8Remainder = sfe_ublox_u8Crc4Table[u8TableRemainder];
9032+     }
9033+ 
9034+     return u8Remainder;
9035+ }
9036+ 
9037+ uint8_t DevUBLOXGNSS::uSpartnCrc8(const uint8_t *pU8Msg, size_t size)
9038+ {
9039+     // Initialize local variables
9040+     uint8_t u8TableRemainder;
9041+     uint8_t u8Remainder = 0; // Initial remainder
9042+ 
9043+     // Compute the CRC value
9044+     // Divide each byte of the message by the corresponding polynomial
9045+     for (size_t x = 0; x < size; x++) {
9046+         u8TableRemainder = pU8Msg[x] ^ u8Remainder;
9047+         u8Remainder = sfe_ublox_u8Crc8Table[u8TableRemainder];
9048+     }
9049+ 
9050+     return u8Remainder;
9051+ }
9052+ 
9053+ uint16_t DevUBLOXGNSS::uSpartnCrc16(const uint8_t *pU8Msg, size_t size)
9054+ {
9055+     // Initialize local variables
9056+     uint16_t u16TableRemainder;
9057+     uint16_t u16Remainder = 0; // Initial remainder
9058+     uint8_t  u8NumBitsInCrc = (8 * sizeof(uint16_t));
9059+ 
9060+     // Compute the CRC value
9061+     // Divide each byte of the message by the corresponding polynomial
9062+     for (size_t x = 0; x < size; x++) {
9063+         u16TableRemainder = pU8Msg[x] ^ (u16Remainder >> (u8NumBitsInCrc - 8));
9064+         u16Remainder = sfe_ublox_u16Crc16Table[u16TableRemainder] ^ (u16Remainder << 8);
9065+     }
9066+ 
9067+     return u16Remainder;
9068+ }
9069+ 
9070+ uint32_t DevUBLOXGNSS::uSpartnCrc24(const uint8_t *pU8Msg, size_t size)
9071+ {
9072+     // Initialize local variables
9073+     uint32_t u32TableRemainder;
9074+     uint32_t u32Remainder = 0; // Initial remainder
9075+     uint8_t u8NumBitsInCrc = (8 * sizeof(uint8_t) * 3);
9076+ 
9077+     // Compute the CRC value
9078+     // Divide each byte of the message by the corresponding polynomial
9079+     for (size_t x = 0; x < size; x++) {
9080+         u32TableRemainder = pU8Msg[x] ^ (u32Remainder >> (u8NumBitsInCrc - 8));
9081+         u32Remainder = sfe_ublox_u32Crc24Table[u32TableRemainder] ^ (u32Remainder << 8);
9082+         u32Remainder = u32Remainder & 0x00FFFFFF; // Only interested in 24 bits
9083+     }
9084+ 
9085+     return u32Remainder;
9086+ }
9087+ 
9088+ uint32_t DevUBLOXGNSS::uSpartnCrc32(const uint8_t *pU8Msg, size_t size)
9089+ {
9090+     // Initialize local variables
9091+     uint32_t u32TableRemainder;
9092+     uint32_t u32Remainder = 0xFFFFFFFFU; // Initial remainder
9093+     uint8_t u8NumBitsInCrc = (8 * sizeof(uint32_t));
9094+     uint32_t u32FinalXORValue = 0xFFFFFFFFU;
9095+ 
9096+     // Compute the CRC value
9097+     // Divide each byte of the message by the corresponding polynomial
9098+     for (size_t x = 0; x < size; x++) {
9099+         u32TableRemainder = pU8Msg[x] ^ (u32Remainder >> (u8NumBitsInCrc - 8));
9100+         u32Remainder = sfe_ublox_u32Crc32Table[u32TableRemainder] ^ (u32Remainder << 8);
9101+     }
9102+ 
9103+     u32Remainder = u32Remainder ^ u32FinalXORValue;
9104+ 
9105+     return u32Remainder;
9106+ }
9107+ 
9108+ // Parse SPARTN data
9109+ uint8_t * DevUBLOXGNSS::parseSPARTN(uint8_t incoming, bool &valid, uint16_t &len)
9110+ {
9111+   typedef enum {
9112+     waitingFor73,
9113+     TF002_TF006,
9114+     TF007,
9115+     TF009,
9116+     TF016,
9117+     TF017,
9118+     TF018
9119+   } parseStates;
9120+   static parseStates parseState = waitingFor73;
9121+ 
9122+   static uint8_t spartn[1100];
9123+ 
9124+   static uint16_t frameCount;
9125+   static uint8_t messageType;
9126+   static uint16_t payloadLength;
9127+   static uint16_t EAF;
9128+   static uint8_t crcType;
9129+   static uint16_t crcBytes;
9130+   static uint8_t frameCRC;
9131+   static uint8_t messageSubtype;
9132+   static uint16_t timeTagType;
9133+   static uint16_t authenticationIndicator;
9134+   static uint16_t embeddedApplicationLengthBytes;
9135+   static uint16_t TF007toTF016;
9136+ 
9137+   valid = false;
9138+ 
9139+   switch(parseState)
9140+   {
9141+     case waitingFor73:
9142+       if (incoming == 0x73)
9143+       {
9144+         parseState = TF002_TF006;
9145+         frameCount = 0;
9146+         spartn[0] = incoming;
9147+       }
9148+       break;
9149+     case TF002_TF006:
9150+       spartn[1 + frameCount] = incoming;
9151+       if (frameCount == 0)
9152+       {
9153+         messageType = incoming >> 1;
9154+         payloadLength = incoming & 0x01;
9155+       }
9156+       if (frameCount == 1)
9157+       {
9158+         payloadLength <<= 8;
9159+         payloadLength |= incoming;
9160+       }
9161+       if (frameCount == 2)
9162+       {
9163+         payloadLength <<= 1;
9164+         payloadLength |= incoming >> 7;
9165+         EAF = (incoming >> 6) & 0x01;
9166+         crcType = (incoming >> 4) & 0x03;
9167+         switch (crcType)
9168+         {
9169+           case 0:
9170+             crcBytes = 1;
9171+             break;
9172+           case 1:
9173+             crcBytes = 2;
9174+             break;
9175+           case 2:
9176+             crcBytes = 3;
9177+             break;
9178+           default:
9179+             crcBytes = 4;
9180+             break;
9181+         }
9182+         frameCRC = incoming & 0x0F;
9183+         spartn[3] = spartn[3] & 0xF0; // Zero the 4 LSBs before calculating the CRC
9184+         if (uSpartnCrc4(&spartn[1], 3) == frameCRC)
9185+         {
9186+           spartn[3] = incoming; // Restore TF005 and TF006 now we know the data is valid
9187+           parseState = TF007;
9188+           //Serial.println("Header CRC is valid");
9189+           //Serial.printf("payloadLength %d EAF %d crcType %d\n", payloadLength, EAF, crcType);
9190+         }
9191+         else
9192+         {
9193+           parseState = waitingFor73;
9194+           //Serial.println("Header CRC is INVALID");
9195+         }
9196+       }
9197+       frameCount++;
9198+       break;
9199+     case TF007:
9200+       spartn[4] = incoming;
9201+       messageSubtype = incoming >> 4;
9202+       timeTagType = (incoming >> 3) & 0x01;
9203+       //Serial.printf("timeTagType %d\n", timeTagType);
9204+       if (timeTagType == 0)
9205+         TF007toTF016 = 4;
9206+       else
9207+         TF007toTF016 = 6;
9208+       if (EAF > 0)
9209+         TF007toTF016 += 2;
9210+       parseState = TF009;
9211+       frameCount = 1;          
9212+       break;
9213+     case TF009:
9214+       spartn[4 + frameCount] = incoming;
9215+       frameCount++;
9216+       if (frameCount == TF007toTF016)
9217+       {
9218+         if (EAF == 0)
9219+         {
9220+           authenticationIndicator = 0;
9221+           embeddedApplicationLengthBytes = 0;
9222+         }
9223+         else
9224+         {
9225+           authenticationIndicator = (incoming >> 3) & 0x07;
9226+           //Serial.printf("authenticationIndicator %d\n", authenticationIndicator);
9227+           if (authenticationIndicator <= 1)
9228+             embeddedApplicationLengthBytes = 0;
9229+           else
9230+           {
9231+             switch(incoming & 0x07)
9232+             {
9233+               case 0:
9234+                 embeddedApplicationLengthBytes = 8; // 64 bits
9235+                 break;
9236+               case 1:
9237+                 embeddedApplicationLengthBytes = 12; // 96 bits
9238+                 break;
9239+               case 2:
9240+                 embeddedApplicationLengthBytes = 16; // 128 bits
9241+                 break;
9242+               case 3:
9243+                 embeddedApplicationLengthBytes = 32; // 256 bits
9244+                 break;
9245+               default:
9246+                 embeddedApplicationLengthBytes = 64; // 512 / TBD bits
9247+                 break;
9248+             }
9249+           }
9250+           //Serial.printf("embeddedApplicationLengthBytes %d\n", embeddedApplicationLengthBytes);
9251+         }
9252+         parseState = TF016;
9253+         frameCount = 0;                  
9254+       }
9255+       break;
9256+     case TF016:
9257+       spartn[4 + TF007toTF016 + frameCount] = incoming;
9258+       frameCount++;
9259+       if (frameCount == payloadLength)
9260+       {
9261+         if (embeddedApplicationLengthBytes > 0)
9262+         {
9263+           parseState = TF017;
9264+           frameCount = 0;
9265+         }
9266+         else               
9267+         {
9268+           parseState = TF018;
9269+           frameCount = 0;
9270+         }
9271+       }
9272+       break;
9273+     case TF017:
9274+       spartn[4 + TF007toTF016 + payloadLength + frameCount] = incoming;
9275+       frameCount++;
9276+       if (frameCount == embeddedApplicationLengthBytes)
9277+       {
9278+         parseState = TF018;
9279+         frameCount = 0;        
9280+       }
9281+       break;
9282+     case TF018:
9283+       spartn[4 + TF007toTF016 + payloadLength + embeddedApplicationLengthBytes + frameCount] = incoming;
9284+       frameCount++;
9285+       if (frameCount == crcBytes)
9286+       {
9287+           parseState = waitingFor73;
9288+           uint16_t numBytes = 4 + TF007toTF016 + payloadLength + embeddedApplicationLengthBytes;
9289+           //Serial.printf("numBytes %d\n", numBytes);
9290+           uint8_t *ptr = &spartn[numBytes];
9291+           switch (crcType)
9292+           {
9293+             case 0:
9294+             {
9295+               uint8_t expected = *ptr;
9296+               if (uSpartnCrc8(&spartn[1], numBytes - 1) == expected) // Don't include the preamble in the CRC
9297+               {
9298+                 valid = true;
9299+                 len = numBytes + 1;
9300+                 //Serial.println("SPARTN CRC-8 is valid");
9301+               }
9302+               else
9303+               {
9304+                 //Serial.println("SPARTN CRC-8 is INVALID");
9305+               }
9306+             }
9307+             break;
9308+             case 1:
9309+             {
9310+               uint16_t expected = *ptr++;
9311+               expected <<= 8;
9312+               expected |= *ptr;
9313+               if (uSpartnCrc16(&spartn[1], numBytes - 1) == expected) // Don't include the preamble in the CRC
9314+               {
9315+                 valid = true;
9316+                 len = numBytes + 2;
9317+                 //Serial.println("SPARTN CRC-16 is valid");
9318+               }
9319+               else
9320+               {
9321+                 //Serial.println("SPARTN CRC-16 is INVALID");
9322+               }
9323+             }
9324+             break;
9325+             case 2:
9326+             {
9327+               uint32_t expected = *ptr++;
9328+               expected <<= 8;
9329+               expected |= *ptr++;
9330+               expected <<= 8;
9331+               expected |= *ptr;
9332+               uint32_t crc = uSpartnCrc24(&spartn[1], numBytes - 1); // Don't include the preamble in the CRC
9333+               if (crc == expected)
9334+               {
9335+                 valid = true;
9336+                 len = numBytes + 3;
9337+                 //Serial.println("SPARTN CRC-24 is valid");
9338+               }
9339+               else
9340+               {
9341+                 //Serial.printf("SPARTN CRC-24 is INVALID: 0x%06X vs 0x%06X\n", expected, crc);
9342+               }
9343+             }
9344+             break;
9345+             default:
9346+             {
9347+               uint32_t expected = *ptr++;
9348+               expected <<= 8;
9349+               expected |= *ptr++;
9350+               expected <<= 8;
9351+               expected |= *ptr++;
9352+               expected <<= 8;
9353+               expected |= *ptr;
9354+               if (uSpartnCrc32(&spartn[1], numBytes - 1) == expected)
9355+               {
9356+                 valid = true;
9357+                 len = numBytes + 4;
9358+                 //Serial.println("SPARTN CRC-32 is valid");
9359+               }
9360+               else
9361+               {
9362+                 //Serial.println("SPARTN CRC-32 is INVALID");
9363+               }
9364+             }
9365+             break;
9366+           }
9367+       }
9368+       break;
9369+   }
9370+ 
9371+   (void)messageType; // Avoid pesky compiler warnings-as-errors
9372+   (void)messageSubtype;
9373+ 
9374+   return &spartn[0];
9375+ }
9376+ 
90189377// Get the unique chip ID using UBX-SEC-UNIQID
90199378// The ID is five bytes on the F9 and M9 (version 1) but six bytes on the M10 (version 2)
90209379bool DevUBLOXGNSS::getUniqueChipId(UBX_SEC_UNIQID_data_t *data, uint16_t maxWait)
0 commit comments