Skip to content

Commit 75941f0

Browse files
authored
Merge pull request #34 from LeeLeahy2/hash-unicore
Add the Unicore hash (#) parser
2 parents 01f3152 + 100eaaa commit 75941f0

File tree

4 files changed

+607
-14
lines changed

4 files changed

+607
-14
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
SparkFun Unicore Hash (#) test example sketch
3+
4+
License: MIT. Please see LICENSE.md for more details
5+
*/
6+
7+
#include <SparkFun_Extensible_Message_Parser.h> //http://librarymanager/All#SparkFun_Extensible_Message_Parser
8+
9+
//----------------------------------------
10+
// Constants
11+
//----------------------------------------
12+
13+
// Build the table listing all of the parsers
14+
SEMP_PARSE_ROUTINE const parserTable[] =
15+
{
16+
sempUnicoreHashPreamble
17+
};
18+
const int parserCount = sizeof(parserTable) / sizeof(parserTable[0]);
19+
20+
const char * const parserNames[] =
21+
{
22+
"Unicore hash parser"
23+
};
24+
const int parserNameCount = sizeof(parserNames) / sizeof(parserNames[0]);
25+
26+
27+
// Provide some valid and invalid Unicore hash (#) sentences
28+
const uint8_t rawDataStream[] =
29+
{
30+
// Valid Unicore hash (#) sentences, with or without CR or LF
31+
"#MODE,0,GPS,UNKNOWN,0,0,0,0,0,653;MODE ROVER SURVEY,*66" // 0
32+
"#MODE,0,GPS,UNKNOWN,0,0,0,0,0,653;MODE ROVER SURVEY,*66\r" // 55
33+
"#MODE,0,GPS,UNKNOWN,0,0,0,0,0,653;MODE ROVER SURVEY,*66\n" // 111
34+
35+
// Invalid data, must skip over
36+
"abcdefg" // 167
37+
38+
// Valid Unicore hash (#) sentences
39+
// 1 2 3 4 5 6 7 8
40+
//2345678901234567890123456789012345678901234567890123456789012345678901234567890
41+
"#MODE,0,GPS,UNKNOWN,0,0,0,0,0,653;MODE ROVER SURVEY,*66\r\n" // 174
42+
43+
// Valid Unicore hash (#) sentence with an 8 byte checksum
44+
"#VERSION,40,GPS,UNKNOWN,1,1000,0,0,18,15;UM980,R4.10Build7923,HRPT00-S10C-P,2310415000001-MD22B1225023842,ff3b1e9611b3b07b,2022/09/28*b164c965\r\n"
45+
46+
// Long valid name
47+
//123456789012345
48+
"#ABCDEFGHIJKLMNO,210230,A,3855.4487,N,09446.0071,W,0.0,076.2,1,00*15\r\n" // 231
49+
50+
// Name too long
51+
//1234567890123456
52+
"#ABCDEFGHIJKLMNOP,210230,A,3855.4487,N,09446.0071,W,0.0,076.2,130*6A\r\n" // 301
53+
54+
// Invalid character in the sentence name
55+
"#M@DE,0,GPS,UNKNOWN,0,0,0,0,0,653;MODE ROVER SURVEY,*66\r\n" //
56+
57+
// Bad checksum
58+
"#MODE,0,GPS,UNKNOWN,0,0,0,0,0,653;MODE ROVER SURVEY,*67\r\n" //
59+
60+
// Sentence too long by a single byte
61+
"#VERSION,40,GPS,UNKNOWN,1,1000,0,0,18,15;UM980,R4.10Build7923,HRPT00-S10C-P,2310415000001-MD22B1225023842,ff3b1e9611b3b07b,2022/09/28.*e1bffcd1\r\n"
62+
63+
// Sentence too long by a two bytes
64+
"#VERSION,40,GPS,UNKNOWN,1,1000,0,0,18,15;UM980,R4.10Build7923,HRPT00-S10C-P,2310415000001-MD22B1225023842,ff3b1e9611b3b07b,2022/09/28..*2de35071\r\n"
65+
66+
// Sentence too long by a three bytes
67+
"#VERSION,40,GPS,UNKNOWN,1,1000,0,0,18,15;UM980,R4.10Build7923,HRPT00-S10C-P,2310415000001-MD22B1225023842,ff3b1e9611b3b07b,2022/09/28...*fbf9af35\r\n"
68+
};
69+
70+
// Number of bytes in the rawDataStream
71+
#define RAW_DATA_BYTES sizeof(rawDataStream)
72+
73+
// Account for the largest Unicore hash (#) sentence + zero termination
74+
#define BUFFER_LENGTH 144 + 1
75+
76+
//----------------------------------------
77+
// Locals
78+
//----------------------------------------
79+
80+
uint32_t dataOffset;
81+
SEMP_PARSE_STATE *parse;
82+
83+
//----------------------------------------
84+
// Test routine
85+
//----------------------------------------
86+
87+
// Initialize the system
88+
void setup()
89+
{
90+
delay(1000);
91+
92+
Serial.begin(115200);
93+
Serial.println();
94+
Serial.println("Unicore hash (#)_Test_example sketch");
95+
Serial.println();
96+
97+
// Initialize the parser
98+
parse = sempBeginParser(parserTable, parserCount,
99+
parserNames, parserNameCount,
100+
0, BUFFER_LENGTH, processMessage, "Unicore_Hash_Test",
101+
&Serial, nullptr, badUnicoreHashChecksum);
102+
if (!parse)
103+
reportFatalError("Failed to initialize the parser");
104+
105+
// Obtain a raw data stream from somewhere
106+
Serial.printf("Raw data stream: %d bytes\r\n", RAW_DATA_BYTES);
107+
108+
// The raw data stream is passed to the parser one byte at a time
109+
sempEnableDebugOutput(parse);
110+
for (dataOffset = 0; dataOffset < RAW_DATA_BYTES; dataOffset++)
111+
// Update the parser state based on the incoming byte
112+
sempParseNextByte(parse, rawDataStream[dataOffset]);
113+
114+
// Done parsing the data
115+
sempStopParser(&parse);
116+
}
117+
118+
// Main loop processing after system is initialized
119+
void loop()
120+
{
121+
}
122+
123+
// Handle the bad checksum calculation
124+
// See Unicore NebulasIV, High Precision Products, HIGH PRECISION
125+
// COMMANDS AND LOGS Manual
126+
// Section 7.3, page 112
127+
bool badUnicoreHashChecksum(SEMP_PARSE_STATE *parse)
128+
{
129+
int alternateChecksum;
130+
bool badChecksum;
131+
int checksum;
132+
133+
// Older UM980 firmware during setup is improperly adding the first
134+
// character into the checksum calculation. Convert the received
135+
// checksum characters into binary.
136+
checksum = sempAsciiToNibble(parse->buffer[parse->length - 1]);
137+
checksum |= sempAsciiToNibble(parse->buffer[parse->length - 2]) << 4;
138+
139+
// Determine if the checksum also includes the first character
140+
alternateChecksum = parse->crc ^ parse->buffer[0];
141+
badChecksum = (alternateChecksum != checksum);
142+
143+
// Display bad checksums
144+
if (!badChecksum)
145+
{
146+
Serial.printf("UM980: Message improperly includes %c in checksum\r\n", parse->buffer[0]);
147+
dumpBuffer(parse->buffer, parse->length);
148+
}
149+
return badChecksum;
150+
}
151+
152+
// Call back from within parser, for end of message
153+
// Process a complete message incoming from parser
154+
void processMessage(SEMP_PARSE_STATE *parse, uint16_t type)
155+
{
156+
SEMP_SCRATCH_PAD *scratchPad = (SEMP_SCRATCH_PAD *)parse->scratchPad;
157+
uint32_t offset;
158+
159+
// Determine the raw data stream offset
160+
offset = dataOffset + 1 + 2 - parse->length;
161+
while (rawDataStream[offset] != '#')
162+
offset -= 1;
163+
164+
// Display the raw message
165+
Serial.println();
166+
Serial.printf("Valid Unicore Hash (#) Sentence: %s, %d bytes at 0x%08x (%d)\r\n",
167+
scratchPad->unicoreHash.sentenceName, parse->length, offset, offset);
168+
dumpBuffer(parse->buffer, parse->length);
169+
}
170+
171+
// Display the contents of a buffer
172+
void dumpBuffer(const uint8_t *buffer, uint16_t length)
173+
{
174+
int bytes;
175+
const uint8_t *end;
176+
int index;
177+
uint16_t offset;
178+
179+
end = &buffer[length];
180+
offset = 0;
181+
while (buffer < end)
182+
{
183+
// Determine the number of bytes to display on the line
184+
bytes = end - buffer;
185+
if (bytes > (16 - (offset & 0xf)))
186+
bytes = 16 - (offset & 0xf);
187+
188+
// Display the offset
189+
Serial.printf("0x%08lx: ", offset);
190+
191+
// Skip leading bytes
192+
for (index = 0; index < (offset & 0xf); index++)
193+
Serial.printf(" ");
194+
195+
// Display the data bytes
196+
for (index = 0; index < bytes; index++)
197+
Serial.printf("%02x ", buffer[index]);
198+
199+
// Separate the data bytes from the ASCII
200+
for (; index < (16 - (offset & 0xf)); index++)
201+
Serial.printf(" ");
202+
Serial.printf(" ");
203+
204+
// Skip leading bytes
205+
for (index = 0; index < (offset & 0xf); index++)
206+
Serial.printf(" ");
207+
208+
// Display the ASCII values
209+
for (index = 0; index < bytes; index++)
210+
Serial.printf("%c", ((buffer[index] < ' ') || (buffer[index] >= 0x7f)) ? '.' : buffer[index]);
211+
Serial.printf("\r\n");
212+
213+
// Set the next line of data
214+
buffer += bytes;
215+
offset += bytes;
216+
}
217+
}
218+
219+
// Print the error message every 15 seconds
220+
void reportFatalError(const char *errorMsg)
221+
{
222+
while (1)
223+
{
224+
Serial.print("HALTED: ");
225+
Serial.print(errorMsg);
226+
Serial.println();
227+
sleep(15);
228+
}
229+
}

src/Parse_NMEA.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -266,17 +266,6 @@ bool sempNmeaPreamble(SEMP_PARSE_STATE *parse, uint8_t data)
266266
return true;
267267
}
268268

269-
// Alternate NMEA parser with hash (#) as the preamble
270-
bool sempNmeaHashPreamble(SEMP_PARSE_STATE *parse, uint8_t data)
271-
{
272-
SEMP_SCRATCH_PAD *scratchPad = (SEMP_SCRATCH_PAD *)parse->scratchPad;
273-
if (data != '#')
274-
return false;
275-
scratchPad->nmea.sentenceNameLength = 0;
276-
parse->state = sempNmeaFindFirstComma;
277-
return true;
278-
}
279-
280269
// Translates state value into an string, returns nullptr if not found
281270
const char * sempNmeaGetStateName(const SEMP_PARSE_STATE *parse)
282271
{
@@ -296,7 +285,5 @@ const char * sempNmeaGetStateName(const SEMP_PARSE_STATE *parse)
296285
return "sempNmeaCarriageReturn";
297286
if (parse->state == sempNmeaLineFeed)
298287
return "sempNmeaLineFeed";
299-
if (parse->state == sempNmeaHashPreamble)
300-
return "sempNmeaHashPreamble";
301288
return nullptr;
302289
}

0 commit comments

Comments
 (0)