|
| 1 | +/* |
| 2 | + SparkFun SBF in SPARTN test example sketch |
| 3 | +
|
| 4 | + The mosaic-X5 can output raw L-Band (LBandBeam1) data, interspersed with SBF messages |
| 5 | +
|
| 6 | + This example demonstrates how to use two parsers to extract the SBF from the L-Band stream |
| 7 | +
|
| 8 | + License: MIT. Please see LICENSE.md for more details |
| 9 | +*/ |
| 10 | + |
| 11 | +#include <SparkFun_Extensible_Message_Parser.h> //http://librarymanager/All#SparkFun_Extensible_Message_Parser |
| 12 | + |
| 13 | +//---------------------------------------- |
| 14 | +// Constants |
| 15 | +//---------------------------------------- |
| 16 | + |
| 17 | +// Build the table listing all of the parsers |
| 18 | +SEMP_PARSE_ROUTINE const parserTable1[] = |
| 19 | +{ |
| 20 | + sempSbfPreamble |
| 21 | +}; |
| 22 | +const int parserCount1 = sizeof(parserTable1) / sizeof(parserTable1[0]); |
| 23 | + |
| 24 | +const char * const parser1Names[] = |
| 25 | +{ |
| 26 | + "SBF parser" |
| 27 | +}; |
| 28 | +const int parser1NameCount = sizeof(parser1Names) / sizeof(parser1Names[0]); |
| 29 | + |
| 30 | +SEMP_PARSE_ROUTINE const parserTable2[] = |
| 31 | +{ |
| 32 | + sempSpartnPreamble |
| 33 | +}; |
| 34 | +const int parserCount2 = sizeof(parserTable2) / sizeof(parserTable2[0]); |
| 35 | + |
| 36 | +const char * const parser2Names[] = |
| 37 | +{ |
| 38 | + "SPARTN parser" |
| 39 | +}; |
| 40 | +const int parser2NameCount = sizeof(parser2Names) / sizeof(parser2Names[0]); |
| 41 | + |
| 42 | +// Provide some valid L-Band SPARTN data, interspersed with valid SBF messages |
| 43 | +const uint8_t rawDataStream[] = |
| 44 | +{ |
| 45 | + // Invalid data - must skip over |
| 46 | + 0, 1, 2, 3, 4, 5, 6, 7, // 0 |
| 47 | + |
| 48 | + // SPARTN HPAC 0 - first ~half |
| 49 | + 0x73, 0x03, 0x12, 0x6C, 0x08, 0xBF, 0x33, 0xD0, 0xF0, 0x6C, 0x2E, 0x88, 0xFA, 0xE5, 0x9B, 0x61, |
| 50 | + 0x1B, 0x55, 0x79, 0x31, 0x7C, 0x12, 0x89, 0xE6, 0xE6, 0x91, 0x39, 0xA4, 0x65, 0x70, 0xC4, 0xB7, |
| 51 | + 0xDD, 0x01, 0xE0, 0x64, 0xFE, 0x15, 0xED, 0x9C, 0x0C, 0x3B, 0xC0, 0xBE, 0xA9, 0x1A, 0xF6, 0xB6, |
| 52 | + 0x72, 0xC5, 0x01, 0xDF, 0x17, 0xC2, 0xF6, 0x1B, 0xDD, 0x7B, 0x65, 0x8D, 0xD6, 0xB0, 0xCF, 0x03, |
| 53 | + 0x04, 0xB3, 0x14, 0x46, 0xC0, 0x0B, 0x71, 0x93, 0xDC, 0x22, 0xCF, 0x3D, 0x6B, 0x98, 0xB9, 0xD0, |
| 54 | + 0x26, 0x9C, 0xA4, 0xEC, 0xE7, 0xBD, 0x54, 0x47, 0x85, 0x46, 0x78, 0x0F, 0xDA, 0x73, 0x8B, 0xBB, |
| 55 | + 0xD5, 0xEA, 0x7F, 0xE4, 0x55, 0xEE, 0x4C, 0x71, 0xA7, 0x77, 0x93, 0x89, 0x31, 0xE1, 0x64, 0x44, |
| 56 | + 0xA6, 0xBB, 0xB3, 0xF9, 0x1E, 0x5A, 0xF0, 0xE9, 0xEC, 0xD5, 0x08, 0x6C, 0x59, 0x0D, 0x8C, 0xF6, |
| 57 | + 0x95, 0x8B, 0x1B, 0x12, 0x3D, 0x52, 0x88, 0xA8, 0xD0, 0x4E, 0x20, 0x5F, 0x88, 0x31, 0x64, 0xD2, |
| 58 | + 0xDE, 0xDE, 0x97, 0x15, 0xFD, 0x5A, 0x35, 0xF0, 0xC0, 0xBC, 0x28, 0x14, 0xE2, 0x90, 0x40, 0x27, |
| 59 | + 0x17, 0xFC, 0x3C, 0x5E, 0xFD, 0x52, 0xA8, 0xF2, 0xBB, 0x9E, 0x0B, 0x9E, 0x96, 0x63, 0xB9, 0x75, |
| 60 | + 0x47, 0xC7, 0xDC, 0x95, 0xF0, 0xEB, 0x5B, 0x91, 0x66, 0xAA, 0xCB, 0x67, 0xAF, 0x86, 0xCC, 0x29, |
| 61 | + 0xC4, 0x8A, 0xB3, 0xE2, 0x2F, 0xF9, 0xAA, 0xC8, 0x35, 0x21, 0xD3, 0x9E, 0x93, 0x5B, 0x6D, 0xB6, |
| 62 | + 0x41, 0xD9, 0xDD, 0x12, 0x38, 0x5C, 0xA7, 0x8B, 0xDE, 0x9A, 0xCC, 0x56, 0x0F, 0xBE, 0x7D, 0x5D, |
| 63 | + 0x22, 0xA1, 0x11, 0x52, 0x83, 0x1A, 0xD8, 0xFD, 0xEF, 0x92, 0xFB, 0x04, 0x2F, 0xF1, 0x28, 0x59, |
| 64 | + 0xE5, 0x66, 0x40, 0x51, 0xF1, 0x7F, 0xE6, 0x21, 0x38, 0x21, 0x2D, 0x51, 0x80, 0xF6, 0x53, 0x99, |
| 65 | + 0x46, 0x76, 0xAD, 0x69, 0xC1, 0xE1, 0xF7, 0x7B, 0xF0, 0x0F, 0xDD, 0xAC, 0xC4, 0x22, 0x15, 0x1D, |
| 66 | + |
| 67 | + |
| 68 | + // SBF Block 4007 (PVTGeodetic) |
| 69 | + 0x24, 0x40, 0xC4, 0x86, 0xA7, 0x4F, 0x60, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, |
| 70 | + 0x00, 0x00, 0x00, 0x20, 0x5F, 0xA0, 0x12, 0xC2, 0x00, 0x00, 0x00, 0x20, 0x5F, 0xA0, 0x12, 0xC2, |
| 71 | + 0x00, 0x00, 0x00, 0x20, 0x5F, 0xA0, 0x12, 0xC2, 0xF9, 0x02, 0x95, 0xD0, 0xF9, 0x02, 0x95, 0xD0, |
| 72 | + 0xF9, 0x02, 0x95, 0xD0, 0xF9, 0x02, 0x95, 0xD0, 0xF9, 0x02, 0x95, 0xD0, 0x00, 0x00, 0x00, 0x20, |
| 73 | + 0x5F, 0xA0, 0x12, 0xC2, 0xF9, 0x02, 0x95, 0xD0, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, |
| 74 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, |
| 75 | + |
| 76 | + |
| 77 | + // SPARTN HPAC 0 - second ~half |
| 78 | + 0x2A, 0x4D, 0x6C, 0xA3, 0x17, 0xE3, 0x7B, 0x99, 0xF1, 0xC8, 0xCB, 0xC0, 0x4B, 0x75, 0x1E, 0xE4, |
| 79 | + 0x5B, 0x14, 0x90, 0x13, 0xC7, 0x19, 0x11, 0xFA, 0xDD, 0x57, 0x1A, 0xCF, 0x63, 0x02, 0x13, 0x45, |
| 80 | + 0x83, 0x72, 0xA4, 0x56, 0x40, 0xD8, 0xD7, 0xAC, 0x7A, 0xA4, 0x83, 0xD9, 0xA9, 0x2B, 0x42, 0x77, |
| 81 | + 0x12, 0xCF, 0xFB, 0xFA, 0xBD, 0x3E, 0x4D, 0xFA, 0x3C, 0x79, 0xC1, 0xD5, 0xE8, 0x73, 0x09, 0x9C, |
| 82 | + 0xB4, 0x36, 0x7C, 0xCC, 0x46, 0x5B, 0x4D, 0x8D, 0xC5, 0xF1, 0xAA, 0xEB, 0x2B, 0x1E, 0xCF, 0xB1, |
| 83 | + 0xE5, 0x74, 0xE3, 0x75, 0xD4, 0x77, 0x8F, 0x6A, 0x1B, 0xE8, 0x5D, 0x56, 0xEB, 0xB6, 0xE9, 0x95, |
| 84 | + 0x88, 0x13, 0x97, 0xA2, 0x19, 0x7B, 0xE3, 0x2B, 0xEA, 0x17, 0x01, 0x7E, 0xCB, 0x81, 0x81, 0x21, |
| 85 | + 0x95, 0xD1, 0x0E, 0x55, 0x3D, 0xA2, 0xC1, 0x75, 0xAF, 0x03, 0x4B, 0x28, 0x10, 0x47, 0x20, 0x58, |
| 86 | + 0xA4, 0x9F, 0x95, 0x05, 0x5F, 0x51, 0x3A, 0x39, 0x94, 0xAA, 0xD7, 0xBF, 0x88, 0x61, 0xCF, 0x7B, |
| 87 | + 0x99, 0x38, 0x6A, 0xBD, 0xA8, 0xEA, 0xE0, 0x2A, 0xBC, 0x04, 0x84, 0xF2, 0xC1, 0xD2, 0xB8, 0x86, |
| 88 | + 0xC3, 0x07, 0x1C, 0x3B, 0x42, 0x49, 0xDC, 0xC3, 0x65, 0x29, 0x81, 0x8C, 0x17, 0x7A, 0xD0, 0x92, |
| 89 | + 0x47, 0x16, 0xB7, 0x53, 0xB9, 0x67, 0x98, 0x57, 0x9A, 0xCD, 0x15, 0x98, 0x27, 0x52, 0x0D, 0x4F, |
| 90 | + 0x4B, 0x49, 0xBA, 0x74, 0xE0, 0x4D, 0x37, 0x8B, 0x23, 0xB3, 0x69, 0x2B, 0xA4, 0x73, 0x68, 0x9B, |
| 91 | + 0xA9, 0xEC, 0x71, 0xCF, 0x13, 0x17, 0x63, 0xC9, 0x49, 0xD9, 0x5A, 0xAC, 0x22, 0xDC, 0x5F, 0xD2, |
| 92 | + 0x43, 0xC0, 0x0A, 0x4F, 0xF8, 0xD6, 0x18, 0x34, 0x4F, 0x3D, 0xF4, 0xC1, 0x84, 0x97, 0xE5, 0x68, |
| 93 | + 0xBB, 0x44, 0x00, 0xEF, 0xB0, 0x10, 0x75, 0xA0, 0xFF, 0xE6, 0x3E, 0x83, 0x53, 0x58, 0x56, 0x5E, |
| 94 | + 0x56, 0x60, 0xB0, 0xFE, 0x18, 0x94, 0x40, 0xB3, 0xC1, 0x6E, 0x5D, 0x5D, 0x90, 0xD7, 0x72, 0x46, |
| 95 | + 0x58, 0x95, 0x5C, 0x69, 0x1C, 0x64, 0x1A, 0xA6, 0x5C, 0xF3, 0xCD, 0x32, 0xFA, 0x00, 0xCE, 0xD7, |
| 96 | + 0x71, 0x5E, 0x8D, |
| 97 | + |
| 98 | +}; |
| 99 | + |
| 100 | +// Number of bytes in the rawDataStream |
| 101 | +#define RAW_DATA_BYTES (sizeof(rawDataStream) / sizeof(rawDataStream[0])) |
| 102 | + |
| 103 | +// Account for the largest SBF messages |
| 104 | +#define BUFFER_LENGTH 2048 |
| 105 | + |
| 106 | +//---------------------------------------- |
| 107 | +// Locals |
| 108 | +//---------------------------------------- |
| 109 | + |
| 110 | +uint32_t dataOffset1; |
| 111 | +SEMP_PARSE_STATE *parse1; |
| 112 | +uint32_t dataOffset2; |
| 113 | +SEMP_PARSE_STATE *parse2; |
| 114 | + |
| 115 | +//---------------------------------------- |
| 116 | +// Test routine |
| 117 | +//---------------------------------------- |
| 118 | + |
| 119 | +// Initialize the system |
| 120 | +void setup() |
| 121 | +{ |
| 122 | + delay(1000); |
| 123 | + |
| 124 | + Serial.begin(115200); |
| 125 | + Serial.println(); |
| 126 | + Serial.println("SBF_in_SPARTN_Test example sketch"); |
| 127 | + Serial.println(); |
| 128 | + |
| 129 | + // Initialize the parsers |
| 130 | + parse1 = sempBeginParser(parserTable1, parserCount1, |
| 131 | + parser1Names, parser1NameCount, |
| 132 | + 0, BUFFER_LENGTH, processSbfMessage, "SBF_Test"); |
| 133 | + if (!parse1) |
| 134 | + reportFatalError("Failed to initialize parser 1"); |
| 135 | + |
| 136 | + parse2 = sempBeginParser(parserTable2, parserCount2, |
| 137 | + parser2Names, parser2NameCount, |
| 138 | + 0, BUFFER_LENGTH, processSpartnMessage, "SPARTN_Test"); |
| 139 | + if (!parse2) |
| 140 | + reportFatalError("Failed to initialize parser 2"); |
| 141 | + |
| 142 | + // Obtain a raw data stream from somewhere |
| 143 | + Serial.printf("Raw data stream: %d bytes\r\n", RAW_DATA_BYTES); |
| 144 | + |
| 145 | + // The raw data stream is passed to the SBF parser one byte at a time |
| 146 | + sempEnableDebugOutput(parse1); |
| 147 | + |
| 148 | + // Any data which is not SBF is passed to the SPARTN parser |
| 149 | + sempEnableDebugOutput(parse2); |
| 150 | + |
| 151 | + for (dataOffset1 = 0; dataOffset1 < RAW_DATA_BYTES; dataOffset1++) |
| 152 | + { |
| 153 | + // Update the SBF parser state based on the incoming byte |
| 154 | + sempParseNextByte(parse1, rawDataStream[dataOffset1]); |
| 155 | + |
| 156 | + // If the data is not SBF, the state will return to sempFirstByte |
| 157 | + if (parse1->state == sempFirstByte) |
| 158 | + { |
| 159 | + // Data is not SBF, so pass it to the SPARTN parser |
| 160 | + for (dataOffset2 = 0; dataOffset2 < parse1->length; dataOffset2++) |
| 161 | + { |
| 162 | + // Update the SPARTN parser state based on the non-SBF byte |
| 163 | + sempParseNextByte(parse2, parse1->buffer[dataOffset2]); |
| 164 | + } |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + // Done parsing the data |
| 169 | + sempStopParser(&parse1); |
| 170 | + sempStopParser(&parse2); |
| 171 | +} |
| 172 | + |
| 173 | +// Main loop processing after system is initialized |
| 174 | +void loop() |
| 175 | +{ |
| 176 | + // Nothing to do here... |
| 177 | +} |
| 178 | + |
| 179 | +// Call back from within parser, for end of message |
| 180 | +// Process a complete message incoming from parser |
| 181 | +void processSbfMessage(SEMP_PARSE_STATE *parse, uint16_t type) |
| 182 | +{ |
| 183 | + SEMP_SCRATCH_PAD *scratchPad = (SEMP_SCRATCH_PAD *)parse->scratchPad; |
| 184 | + |
| 185 | + uint32_t offset; |
| 186 | + |
| 187 | + // Display the raw message |
| 188 | + Serial.println(); |
| 189 | + offset = dataOffset1 + 1 - parse->length; |
| 190 | + Serial.printf("Valid SBF message block %d : %d bytes at 0x%08lx (%ld)\r\n", |
| 191 | + scratchPad->sbf.sbfID, |
| 192 | + parse->length, offset, offset); |
| 193 | + dumpBuffer(parse->buffer, parse->length); |
| 194 | + |
| 195 | + // Display Block Number |
| 196 | + Serial.print("SBF Block Number: "); |
| 197 | + Serial.println(sempSbfGetBlockNumber(parse)); |
| 198 | + |
| 199 | + // Display the parser state |
| 200 | + static bool displayOnce = true; |
| 201 | + if (displayOnce) |
| 202 | + { |
| 203 | + displayOnce = false; |
| 204 | + Serial.println(); |
| 205 | + sempPrintParserConfiguration(parse, &Serial); |
| 206 | + } |
| 207 | +} |
| 208 | + |
| 209 | +// Call back from within parser, for end of message |
| 210 | +// Process a complete message incoming from parser |
| 211 | +void processSpartnMessage(SEMP_PARSE_STATE *parse, uint16_t type) |
| 212 | +{ |
| 213 | + SEMP_SCRATCH_PAD *scratchPad = (SEMP_SCRATCH_PAD *)parse->scratchPad; |
| 214 | + |
| 215 | + static bool displayOnce = true; |
| 216 | + uint32_t offset; |
| 217 | + |
| 218 | + // Display the raw message |
| 219 | + Serial.println(); |
| 220 | + offset = dataOffset2 + 1 - parse->length; |
| 221 | + Serial.printf("Valid SPARTN message, type %d, subtype %d : %d bytes at 0x%08lx (%ld)\r\n", |
| 222 | + scratchPad->spartn.messageType, |
| 223 | + scratchPad->spartn.messageSubtype, |
| 224 | + parse->length, offset, offset); |
| 225 | + dumpBuffer(parse->buffer, parse->length); |
| 226 | + |
| 227 | + // Display the parser state |
| 228 | + if (displayOnce) |
| 229 | + { |
| 230 | + displayOnce = false; |
| 231 | + Serial.println(); |
| 232 | + sempPrintParserConfiguration(parse, &Serial); |
| 233 | + } |
| 234 | +} |
| 235 | + |
| 236 | +// Display the contents of a buffer |
| 237 | +void dumpBuffer(const uint8_t *buffer, uint16_t length) |
| 238 | +{ |
| 239 | + int bytes; |
| 240 | + const uint8_t *end; |
| 241 | + int index; |
| 242 | + uint32_t offset; |
| 243 | + |
| 244 | + end = &buffer[length]; |
| 245 | + offset = 0; |
| 246 | + while (buffer < end) |
| 247 | + { |
| 248 | + // Determine the number of bytes to display on the line |
| 249 | + bytes = end - buffer; |
| 250 | + if (bytes > (16 - (offset & 0xf))) |
| 251 | + bytes = 16 - (offset & 0xf); |
| 252 | + |
| 253 | + // Display the offset |
| 254 | + Serial.printf("0x%08lx: ", offset); |
| 255 | + |
| 256 | + // Skip leading bytes |
| 257 | + for (index = 0; index < (offset & 0xf); index++) |
| 258 | + Serial.printf(" "); |
| 259 | + |
| 260 | + // Display the data bytes |
| 261 | + for (index = 0; index < bytes; index++) |
| 262 | + Serial.printf("%02x ", buffer[index]); |
| 263 | + |
| 264 | + // Separate the data bytes from the ASCII |
| 265 | + for (; index < (16 - (offset & 0xf)); index++) |
| 266 | + Serial.printf(" "); |
| 267 | + Serial.printf(" "); |
| 268 | + |
| 269 | + // Skip leading bytes |
| 270 | + for (index = 0; index < (offset & 0xf); index++) |
| 271 | + Serial.printf(" "); |
| 272 | + |
| 273 | + // Display the ASCII values |
| 274 | + for (index = 0; index < bytes; index++) |
| 275 | + Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]); |
| 276 | + Serial.printf("\r\n"); |
| 277 | + |
| 278 | + // Set the next line of data |
| 279 | + buffer += bytes; |
| 280 | + offset += bytes; |
| 281 | + } |
| 282 | +} |
| 283 | + |
| 284 | +// Print the error message every 15 seconds |
| 285 | +void reportFatalError(const char *errorMsg) |
| 286 | +{ |
| 287 | + while (1) |
| 288 | + { |
| 289 | + Serial.print("HALTED: "); |
| 290 | + Serial.print(errorMsg); |
| 291 | + Serial.println(); |
| 292 | + sleep(15); |
| 293 | + } |
| 294 | +} |
0 commit comments