/elekIOServ/NMEAParser.c

http://github.com/Yniold/liftsrc · C · 486 lines · 295 code · 67 blank · 124 comment · 66 complexity · da11841665dc4989801eb2a6f57caef6 MD5 · raw file

  1. // ============================================
  2. // NMEA Parser
  3. // Implementation
  4. // ============================================
  5. // $RCSfile: NMEAParser.c,v $ last changed on $Date: 2006/09/04 13:05:50 $ by $Author: rudolf $
  6. // History:
  7. //
  8. // $Log: NMEAParser.c,v $
  9. // Revision 1.5 2006/09/04 13:05:50 rudolf
  10. // fix for GCC 4.03
  11. //
  12. // Revision 1.4 2005/09/21 21:02:48 rudolf
  13. // fixed Latitude and Longitude parsing, changed tty to S1
  14. //
  15. // Revision 1.3 2005/01/31 11:36:31 rudolf
  16. // Added parsing of NMEA GPVTG datafield (groundspeed and heading)
  17. //
  18. // Revision 1.2 2005/01/27 18:16:12 rudolf
  19. // changed NMEA parser to work with elekIO serv
  20. //
  21. // Revision 1.1 2005/01/27 14:59:36 rudolf
  22. // added files for GPS receiver connection
  23. //
  24. //
  25. #include <stddef.h>
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include "NMEAParser.h"
  29. #define MAXFIELD 25 // maximum field length
  30. extern volatile unsigned char ucDataReadyFlag;
  31. // ==================
  32. // Init NMEA Parser
  33. // ==================
  34. void NMEAParserInit()
  35. {
  36. nState = NP_STATE_SOM; // Reset Statemachine
  37. dwCommandCount = 0;
  38. Reset();
  39. }
  40. // ===============================
  41. // Process serial input buffer
  42. // ===============================
  43. unsigned char ParseBuffer(unsigned char *pBuff, int iLen)
  44. {
  45. int iIndex = 0;
  46. for(iIndex = 0; iIndex < iLen; iIndex++)
  47. {
  48. ProcessNMEA(pBuff[iIndex]);
  49. }
  50. return TRUE;
  51. }
  52. // ==================
  53. // Process NMEA data
  54. // ==================
  55. void ProcessNMEA(unsigned char ucData)
  56. {
  57. switch(nState)
  58. {
  59. // Search for start of message '$'
  60. case NP_STATE_SOM :
  61. if(ucData == '$')
  62. {
  63. ucChecksum = 0; // reset checksum
  64. wIndex = 0; // reset index
  65. nState = NP_STATE_CMD;
  66. }
  67. break;
  68. // Retrieve command (NMEA Address)
  69. case NP_STATE_CMD :
  70. if(ucData != ',' && ucData != '*')
  71. {
  72. pCommand[wIndex++] = ucData;
  73. ucChecksum ^= ucData;
  74. // Check for command overflow
  75. if(wIndex >= NP_MAX_CMD_LEN)
  76. {
  77. nState = NP_STATE_SOM;
  78. }
  79. }
  80. else
  81. {
  82. pCommand[wIndex] = '\0'; // terminate command
  83. ucChecksum ^= ucData;
  84. wIndex = 0;
  85. nState = NP_STATE_DATA; // goto get data state
  86. }
  87. break;
  88. // Store data and check for end of sentence or checksum flag
  89. case NP_STATE_DATA :
  90. if(ucData == '*') // checksum flag?
  91. {
  92. pData[wIndex] = '\0';
  93. nState = NP_STATE_CHECKSU1;
  94. }
  95. else // no checksum flag, store data
  96. {
  97. //
  98. // Check for end of sentence with no checksum
  99. //
  100. if(ucData == '\r')
  101. {
  102. pData[wIndex] = '\0';
  103. ProcessCommand(pCommand, pData);
  104. nState = NP_STATE_SOM;
  105. return;
  106. }
  107. //
  108. // Store data and calculate checksum
  109. //
  110. ucChecksum ^= ucData;
  111. pData[wIndex] = ucData;
  112. if(++wIndex >= NP_MAX_DATA_LEN) // Check for buffer overflow
  113. {
  114. nState = NP_STATE_SOM;
  115. }
  116. }
  117. break;
  118. // first byte of checksum
  119. case NP_STATE_CHECKSU1 :
  120. if( (ucData - '0') <= 9)
  121. {
  122. ucReceivedChecksum = (ucData - '0') << 4;
  123. }
  124. else
  125. {
  126. ucReceivedChecksum = (ucData - 'A' + 10) << 4;
  127. }
  128. nState = NP_STATE_CHECKSU2;
  129. break;
  130. // second byte of checksum
  131. case NP_STATE_CHECKSU2 :
  132. if( (ucData - '0') <= 9)
  133. {
  134. ucReceivedChecksum |= (ucData - '0');
  135. }
  136. else
  137. {
  138. ucReceivedChecksum |= (ucData - 'A' + 10);
  139. }
  140. if(ucChecksum == ucReceivedChecksum)
  141. {
  142. ProcessCommand(pCommand, pData);
  143. }
  144. nState = NP_STATE_SOM;
  145. break;
  146. default : nState = NP_STATE_SOM;
  147. }
  148. }
  149. //=======================================================================
  150. // Process NMEA sentence - Use the NMEA address (*pCommand) and call the
  151. // appropriate sentense data processor.
  152. //=======================================================================
  153. unsigned char ProcessCommand(unsigned char *pCommand, unsigned char *pData)
  154. {
  155. //
  156. // GPGGA
  157. //
  158. if( strcmp((char *)pCommand, "GPGGA") == 0)
  159. {
  160. ProcessGPGGA(pData);
  161. }
  162. //
  163. // GPVTG
  164. //
  165. if( strcmp((char *)pCommand, "GPVTG") == 0)
  166. {
  167. ProcessGPVTG(pData);
  168. }
  169. dwCommandCount++;
  170. return TRUE;
  171. }
  172. //===========================================================================
  173. // Name: GetField
  174. //
  175. // Description: This function will get the specified field in a NMEA string.
  176. //
  177. // Entry: unsigned char *pData - Pointer to NMEA string
  178. // unsigned char *pField - pointer to returned field
  179. // int nfieldNum - Field offset to get
  180. // int nMaxFieldLen - Maximum of unsigned chars pFiled can handle
  181. //===========================================================================
  182. unsigned char GetField(unsigned char *pData, unsigned char *pField, int nFieldNum, int nMaxFieldLen)
  183. {
  184. //
  185. // Validate params
  186. //
  187. if(pData == NULL || pField == NULL || nMaxFieldLen <= 0)
  188. {
  189. return FALSE;
  190. }
  191. //
  192. // Go to the beginning of the selected field
  193. //
  194. int i = 0;
  195. int nField = 0;
  196. while(nField != nFieldNum && pData[i])
  197. {
  198. if(pData[i] == ',')
  199. {
  200. nField++;
  201. }
  202. i++;
  203. if(pData[i] == 0)
  204. {
  205. pField[0] = '\0';
  206. return FALSE;
  207. }
  208. }
  209. if(pData[i] == ',' || pData[i] == '*')
  210. {
  211. pField[0] = '\0';
  212. return FALSE;
  213. }
  214. //
  215. // copy field from pData to Field
  216. //
  217. int i2 = 0;
  218. while(pData[i] != ',' && pData[i] != '*' && pData[i])
  219. {
  220. pField[i2] = pData[i];
  221. i2++; i++;
  222. //
  223. // check if field is too big to fit on passed parameter. If it is,
  224. // crop returned field to its max length.
  225. //
  226. if(i2 >= nMaxFieldLen)
  227. {
  228. i2 = nMaxFieldLen-1;
  229. break;
  230. }
  231. }
  232. pField[i2] = '\0';
  233. return TRUE;
  234. }
  235. //===========================================================================
  236. // Reset: Reset all NMEA data to start-up default values.
  237. //===========================================================================
  238. void Reset()
  239. {
  240. int i;
  241. //
  242. // GPGGA Data
  243. //
  244. ucGGAHour = 0; //
  245. ucGGAMinute = 0; //
  246. ucGGASecond = 0; //
  247. dGGALatitude = 0.0; // < 0 = South, > 0 = North
  248. dGGALongitude = 0.0; // < 0 = West, > 0 = East
  249. ucGGAGPSQuality = 0; // 0 = fix not available,
  250. // 1 = GPS sps mode,
  251. // 2 = Differential GPS, SPS mode, fix valid,
  252. // 3 = GPS PPS mode, fix valid
  253. ucGGANumOfSatsInUse = 0; //
  254. dGGAHDOP = 0.0; //
  255. dGGAAltitude = 0.0; // Altitude: mean-sea-level (geoid) meters
  256. dwGGACount = 0; //
  257. nGGAOldVSpeedSeconds = 0; //
  258. dGGAOldVSpeedAlt = 0.0; //
  259. dGGAVertSpeed = 0.0; //
  260. }
  261. //===========================================================================
  262. // ProcessGPGGA: extract time, date, postition and quality from GPGGA data
  263. //===========================================================================
  264. void ProcessGPGGA(unsigned char *pData)
  265. {
  266. unsigned char pField[MAXFIELD];
  267. unsigned char pBuff[10];
  268. //
  269. // Time
  270. //
  271. if(GetField(pData, pField, 0, MAXFIELD))
  272. {
  273. // Hour
  274. pBuff[0] = pField[0];
  275. pBuff[1] = pField[1];
  276. pBuff[2] = '\0';
  277. ucGGAHour = atoi((char*)pBuff);
  278. // minute
  279. pBuff[0] = pField[2];
  280. pBuff[1] = pField[3];
  281. pBuff[2] = '\0';
  282. ucGGAMinute = atoi((char*)pBuff);
  283. // Second
  284. pBuff[0] = pField[4];
  285. pBuff[1] = pField[5];
  286. pBuff[2] = '\0';
  287. ucGGASecond = atoi((char*)pBuff);
  288. }
  289. //
  290. // Latitude
  291. //
  292. if(GetField(pData, pField, 1, MAXFIELD))
  293. {
  294. dGGALatitude = atof((char*)pField);
  295. }
  296. if(GetField(pData, pField, 2, MAXFIELD))
  297. {
  298. if(pField[0] == 'S')
  299. {
  300. dGGALatitude = -dGGALatitude;
  301. }
  302. }
  303. //
  304. // Longitude
  305. //
  306. if(GetField(pData, pField, 3, MAXFIELD))
  307. {
  308. dGGALongitude = atof((char *)pField);
  309. }
  310. if(GetField(pData, pField, 4, MAXFIELD))
  311. {
  312. if(pField[0] == 'W')
  313. {
  314. dGGALongitude = -dGGALongitude;
  315. }
  316. }
  317. //
  318. // GPS quality
  319. //
  320. if(GetField(pData, pField, 5, MAXFIELD))
  321. {
  322. ucGGAGPSQuality = pField[0] - '0';
  323. }
  324. //
  325. // Satellites in use
  326. //
  327. if(GetField(pData, pField, 6, MAXFIELD))
  328. {
  329. pBuff[0] = pField[0];
  330. pBuff[1] = pField[1];
  331. pBuff[2] = '\0';
  332. ucGGANumOfSatsInUse = atoi((char*)pBuff);
  333. }
  334. //
  335. // HDOP
  336. //
  337. if(GetField(pData, pField, 7, MAXFIELD))
  338. {
  339. dGGAHDOP = atof((char *)pField);
  340. }
  341. //
  342. // Altitude
  343. //
  344. if(GetField(pData, pField, 8, MAXFIELD))
  345. {
  346. dGGAAltitude = atof((char *)pField);
  347. }
  348. //
  349. // Durive vertical speed (bonus)
  350. //
  351. int nSeconds = (int)ucGGAMinute * 60 + (int)ucGGASecond;
  352. if(nSeconds > nGGAOldVSpeedSeconds)
  353. {
  354. double dDiff = (double)(nGGAOldVSpeedSeconds-nSeconds);
  355. double dVal = dDiff/60.0;
  356. if(dVal != 0.0)
  357. {
  358. dGGAVertSpeed = (dGGAOldVSpeedAlt - dGGAAltitude) / dVal;
  359. }
  360. }
  361. dGGAOldVSpeedAlt = dGGAAltitude;
  362. nGGAOldVSpeedSeconds = nSeconds;
  363. if(ucGGAGPSQuality > 0) // position fixes are valid
  364. ucDataReadyFlag = TRUE;
  365. dwGGACount++;
  366. }
  367. //===========================================================================
  368. // ProcessGPVTG: extract speed and heading
  369. //===========================================================================
  370. void ProcessGPVTG(unsigned char *pData)
  371. {
  372. unsigned char pField[MAXFIELD];
  373. unsigned char pBuff[10];
  374. if(GetField(pData, pField, 0, MAXFIELD))
  375. {
  376. // True Heading
  377. pBuff[0] = pField[0];
  378. pBuff[1] = pField[1];
  379. pBuff[2] = pField[2];
  380. pBuff[3] = pField[3];
  381. pBuff[4] = pField[4];
  382. pBuff[5] = '\0';
  383. dVTGTrueHeading = atof((char*)pBuff);
  384. }
  385. if(GetField(pData, pField, 2, MAXFIELD))
  386. {
  387. // Magnetic Heading
  388. pBuff[0] = pField[0];
  389. pBuff[1] = pField[1];
  390. pBuff[2] = pField[2];
  391. pBuff[3] = pField[3];
  392. pBuff[4] = pField[4];
  393. pBuff[5] = '\0';
  394. dVTGMagneticHeading = atof((char*)pBuff);
  395. }
  396. if(GetField(pData, pField, 4, MAXFIELD))
  397. {
  398. // Groundspeed in knots
  399. pBuff[0] = pField[0];
  400. pBuff[1] = pField[1];
  401. pBuff[2] = pField[2];
  402. pBuff[3] = pField[3];
  403. pBuff[4] = pField[4];
  404. pBuff[5] = '\0';
  405. dVTGSpeedInknots = atof((char*)pBuff);
  406. }
  407. if(GetField(pData, pField, 6, MAXFIELD))
  408. {
  409. // Groundspeed in kmh
  410. pBuff[0] = pField[0];
  411. pBuff[1] = pField[1];
  412. pBuff[2] = pField[2];
  413. pBuff[3] = pField[3];
  414. pBuff[4] = pField[4];
  415. pBuff[5] = '\0';
  416. dVTGSpeedInKmh = atof((char*)pBuff);
  417. }
  418. dwVTGCount++;
  419. }