nmeaparser.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /*
  2. * =====================================================================================
  3. *
  4. * Filename: nmeaparser.c
  5. *
  6. * Description: Nmea parser source file
  7. *
  8. * Version: 1.0
  9. * Created: 2018/8/15 17:43:35
  10. * Revision: none
  11. * Compiler: gcc
  12. *
  13. * Author: Jarod Lee(),
  14. * Organization:
  15. *
  16. * =====================================================================================
  17. */
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <stdio.h>
  21. #include <stdbool.h>
  22. #include <math.h>
  23. #include "nmeaparser.h"
  24. /*
  25. * Convert nmea string to integer
  26. * @p: nmea string start ptr
  27. * @end: nmea string end ptr
  28. * @return: the integer value
  29. */
  30. static int str2int(const char *p, const char *end) {
  31. int result = 0;
  32. while(p < end)
  33. {
  34. int c = *p - '0';
  35. if ((unsigned)c >= 10) {
  36. return -1;
  37. }
  38. result = result * 10 + c;
  39. p++;
  40. }
  41. return result;
  42. }
  43. /*
  44. * Convert nmea string to float
  45. * @p: nmea string start ptr
  46. * @end: nmea string end ptr
  47. * @return: the float value
  48. */
  49. static double str2float(const char *p, const char *end) {
  50. int len = end - p;
  51. char temp[16];
  52. if (len >= (int)sizeof(temp))
  53. return 0.;
  54. memcpy( temp, p, len );
  55. temp[len] = 0;
  56. return strtod( temp, NULL );
  57. }
  58. /*
  59. * Convert nmea's latitude or longitude token to degrees
  60. * @tok: latitude or longitude token
  61. * @return: latitude or longitude in degrees
  62. */
  63. static double convert_from_hhmm(struct token tok) {
  64. double val = str2float(tok.p, tok.end);
  65. int degrees = (int)(floor(val) / 100);
  66. double minutes = val - degrees*100.;
  67. double dcoord = degrees + minutes / 60.0;
  68. return dcoord;
  69. }
  70. /*
  71. * Convert nmea's latitude to degrees
  72. * @lat_tok: nmea latitude token
  73. * @lat_hemi: 'N' means positive while 'S' means negative
  74. */
  75. static double convert_latitude(struct token lat_tok, char lat_hemi)
  76. {
  77. double lat = convert_from_hhmm(lat_tok);
  78. return (lat_hemi == 'N' ? 1 : -1) * lat;
  79. }
  80. /*
  81. * Convert nmea's longitude to degrees
  82. * @lon_tok: nmea longitude token
  83. * @lon_hemi: 'E' means positive while 'W' means negative
  84. */
  85. static double convert_longitude(struct token lon_tok, char lon_hemi)
  86. {
  87. double lon = convert_from_hhmm(lon_tok);
  88. return (lon_hemi == 'E' ? 1 : -1) * lon;
  89. }
  90. /*
  91. * Convert nmea time token to gnss time struct
  92. * @tok: nmea's time token
  93. * @return: gnss_time structure
  94. */
  95. static struct gnss_time convert_gnss_time(struct token tok)
  96. {
  97. // HHMMSS.MS: 101943.235-> 10:19:43.235 -> hour = 10, minute = 19, second = 43, ms = 235
  98. double val = str2float(tok.p, tok.end);
  99. struct gnss_time time;
  100. memset(&time, 0, sizeof(struct gnss_time));
  101. time.hour = (int)(val / 10000);
  102. time.minute = (int)(val / 100) % 100;
  103. time.second = ((int)val) % 100;
  104. time.ms = (int)(0.5 + 1000 * (val - floor(val)));
  105. return time;
  106. }
  107. /*
  108. * Convert nmea date token into gnss_date struct
  109. * @tok: nmea's date token in RMC
  110. * @return: gnss_date structure
  111. */
  112. static struct gnss_date convert_gnss_date(struct token tok)
  113. {
  114. int val = str2int(tok.p, tok.end);
  115. struct gnss_date date;
  116. memset(&date, 0, sizeof(struct gnss_date));
  117. date.year = 2000 + (val % 100);
  118. date.month = (val / 100) % 100;
  119. date.day = (int)(val / 10000);
  120. return date;
  121. }
  122. /*
  123. * Get constellation type by nmea sentence token
  124. * @p: ptr to nmea's sentence start
  125. * @return: constellation type
  126. */
  127. static int get_sv_constell(char *p)
  128. {
  129. if (0 == memcmp(p, "GP", 2))
  130. return CONSTELL_TYPE_GPS;
  131. else if (0 == memcmp(p, "GL", 2))
  132. return CONSTELL_TYPE_GLN;
  133. else if (0 == memcmp(p, "BD", 2))
  134. return CONSTELL_TYPE_BDS;
  135. return CONSTELL_TYPE_UNKNOWN;
  136. }
  137. /*
  138. * Is the quality means fixed.
  139. * @quality: fix flag in GGA
  140. * @return: true for fixed, false for not fixed
  141. */
  142. static bool is_quality_fixed(char quality) {
  143. switch(quality) {
  144. case '1':
  145. case '2':
  146. case '3':
  147. case '4':
  148. case '5':
  149. case '6':
  150. return true;
  151. default:
  152. return false;
  153. }
  154. }
  155. /*
  156. * Is the mode means fixed
  157. * @mode: fix flag in RMC
  158. * @return: true for fixed, false for not fixed
  159. */
  160. static bool is_mode_fixed(char mode)
  161. {
  162. switch(mode) {
  163. case 'A':
  164. case 'D':
  165. case 'E':
  166. return true;
  167. default:
  168. return false;
  169. }
  170. }
  171. /*
  172. * Parse GGA, store the parsed data in navigation data
  173. * @tzer: current tokenizer
  174. * @navdata: navigation data
  175. */
  176. void parse_gga(struct nmea_tokenizer *tzer, struct nav_data *navdata)
  177. {
  178. struct token tok_time = nmea_tokenizer_get(tzer, 1);
  179. struct token tok_latitude = nmea_tokenizer_get(tzer, 2);
  180. struct token tok_latitudeHemi = nmea_tokenizer_get(tzer, 3);
  181. struct token tok_longitude = nmea_tokenizer_get(tzer, 4);
  182. struct token tok_longitudeHemi = nmea_tokenizer_get(tzer, 5);
  183. struct token tok_fixQuality = nmea_tokenizer_get(tzer, 6);
  184. struct token tok_svNumInUse = nmea_tokenizer_get(tzer, 7);
  185. struct token tok_hdop = nmea_tokenizer_get(tzer, 8);
  186. struct token tok_altitude = nmea_tokenizer_get(tzer, 9);
  187. navdata->is_fixed = is_quality_fixed(tok_fixQuality.p[0]);
  188. if (navdata->is_fixed) {
  189. navdata->time = convert_gnss_time(tok_time);
  190. navdata->lat = convert_latitude(tok_latitude, tok_latitudeHemi.p[0]);
  191. navdata->lon = convert_longitude(tok_longitude, tok_longitudeHemi.p[0]);
  192. navdata->sv_inuse = str2int(tok_svNumInUse.p, tok_svNumInUse.end);
  193. navdata->hdop = str2float(tok_hdop.p, tok_hdop.end);
  194. navdata->alt = str2float(tok_altitude.p, tok_altitude.end);
  195. }
  196. }
  197. /*
  198. * Parse RMC, store the parsed data in navigation data
  199. * @tzer: current tokenizer
  200. * @navdata: navigation data
  201. */
  202. void parse_rmc(struct nmea_tokenizer *tzer, struct nav_data *navdata) {
  203. struct token tok_time = nmea_tokenizer_get(tzer,1);
  204. struct token tok_dataValid = nmea_tokenizer_get(tzer,2);
  205. struct token tok_latitude = nmea_tokenizer_get(tzer,3);
  206. struct token tok_latitudeHemi = nmea_tokenizer_get(tzer,4);
  207. struct token tok_longitude = nmea_tokenizer_get(tzer,5);
  208. struct token tok_longitudeHemi = nmea_tokenizer_get(tzer,6);
  209. struct token tok_speed = nmea_tokenizer_get(tzer,7);
  210. struct token tok_heading = nmea_tokenizer_get(tzer,8);
  211. struct token tok_date = nmea_tokenizer_get(tzer,9);
  212. struct token tok_fixMode = nmea_tokenizer_get(tzer,12);
  213. navdata->is_fixed = is_mode_fixed(tok_fixMode.p[0]);
  214. if (navdata->is_fixed) {
  215. navdata->time = convert_gnss_time(tok_time);
  216. navdata->lat = convert_latitude(tok_latitude, tok_latitudeHemi.p[0]);
  217. navdata->lon = convert_longitude(tok_longitude, tok_longitudeHemi.p[0]);
  218. navdata->speed = str2float(tok_speed.p, tok_speed.end);
  219. navdata->heading = str2float(tok_heading.p, tok_heading.end);
  220. }
  221. if (tok_dataValid.p[0] == 'A')
  222. navdata->date = convert_gnss_date(tok_date);
  223. }
  224. /*
  225. * Parse GSA, store the parsed data in navigation data
  226. * @tzer: current tokenizer
  227. * @navdata: navigation data
  228. */
  229. void parse_gsa(struct nmea_tokenizer *tzer, struct nav_data *navdata) {
  230. struct token tok_id = nmea_tokenizer_get(tzer, 0);
  231. struct token tok_pdop = nmea_tokenizer_get(tzer, 15);
  232. struct token tok_hdop = nmea_tokenizer_get(tzer, 16);
  233. struct token tok_vdop = nmea_tokenizer_get(tzer, 17);
  234. struct token tok_svs = nmea_tokenizer_get(tzer, 18);
  235. int constell = get_sv_constell(tok_id.p);
  236. switch(tok_svs.p[0]) {
  237. case '1':
  238. constell = CONSTELL_TYPE_GPS;
  239. break;
  240. case '2':
  241. constell = CONSTELL_TYPE_GLN;
  242. break;
  243. case '4':
  244. constell = CONSTELL_TYPE_BDS;
  245. break;
  246. default:
  247. break;
  248. }
  249. int i;
  250. for (i = 3; i <= 14; i++) {
  251. struct token tok_prn = nmea_tokenizer_get(tzer, i);
  252. int svid = prn2svid(str2int(tok_prn.p, tok_prn.end), constell);
  253. if (svid > 0 && svid < MAX_SVID) {
  254. navdata->sates[svid].in_use = true;
  255. }
  256. }
  257. navdata->pdop = str2float(tok_pdop.p, tok_pdop.end);
  258. navdata->hdop = str2float(tok_hdop.p, tok_hdop.end);
  259. navdata->vdop = str2float(tok_vdop.p, tok_vdop.end);
  260. }
  261. /*
  262. * Parse GSV, store the parsed data in navigation data
  263. * @tzer: current tokenizer
  264. * @navdata: navigation data
  265. */
  266. void parse_gsv(struct nmea_tokenizer *tzer, struct nav_data *d) {
  267. struct token tok_id = nmea_tokenizer_get(tzer, 0);
  268. struct token tok_noSentences = nmea_tokenizer_get(tzer, 1);
  269. struct token tok_sentence = nmea_tokenizer_get(tzer, 2);
  270. struct token tok_noSatellites = nmea_tokenizer_get(tzer, 3);
  271. int constell = get_sv_constell(tok_id.p);
  272. int noSatellites = str2int(tok_noSatellites.p, tok_noSatellites.end);
  273. int sentence = str2int(tok_sentence.p, tok_sentence.end);
  274. int totalSentences = str2int(tok_noSentences.p, tok_noSentences.end);
  275. if (sentence == 1)
  276. d->sv_count = 0;
  277. if (noSatellites > 0) {
  278. int i = 0;
  279. while (i < 4 && d->sv_count < noSatellites) {
  280. struct token tok_prn = nmea_tokenizer_get(tzer, i * 4 + 4);
  281. struct token tok_elevation = nmea_tokenizer_get(tzer, i * 4 + 5);
  282. struct token tok_azimuth = nmea_tokenizer_get(tzer, i * 4 + 6);
  283. struct token tok_snr = nmea_tokenizer_get(tzer, i * 4 + 7);
  284. int prn = str2int(tok_prn.p, tok_prn.end);
  285. int svid = prn2svid(prn, constell);
  286. if (svid > 0 && svid < MAX_SVID) {
  287. d->sates[svid].prn = prn;
  288. d->sates[svid].elev = str2float(tok_elevation.p, tok_elevation.end);
  289. d->sates[svid].azim = str2float(tok_azimuth.p, tok_azimuth.end);
  290. d->sates[svid].cn0 = str2float(tok_snr.p, tok_snr.end);
  291. d->sates[svid].valid = 1;
  292. d->sates[svid].constell = tell_constell(svid);
  293. }
  294. d->sv_count++;
  295. d->sv_inview++;
  296. i += 1;
  297. }
  298. }
  299. if (sentence == totalSentences)
  300. d->sv_count = 0;
  301. }
  302. /*
  303. * Parse current nmea sentence
  304. */
  305. void parse(struct nmea_tokenizer *tzer, struct nav_data *navdata, void (*reporter)(struct nav_data *)) {
  306. struct token tok = nmea_tokenizer_get(tzer, 0);
  307. tok.p += 2;
  308. // display and reset nav_data if encounter GGA sentence
  309. if (memcmp(tok.p, "GGA", 3) == 0) {
  310. // navdata_display(navdata);
  311. if (reporter)
  312. reporter(navdata);
  313. navdata_init(navdata);
  314. }
  315. if (memcmp(tok.p, "GGA", 3) == 0) { // parse GGA
  316. parse_gga(tzer, navdata);
  317. }
  318. else if (memcmp(tok.p, "RMC", 3) == 0) { // parse RMC
  319. parse_rmc(tzer, navdata);
  320. }
  321. else if (memcmp(tok.p, "GSA", 3) == 0) { // parse GSA
  322. parse_gsa(tzer, navdata);
  323. }
  324. else if (memcmp(tok.p, "GSV", 3) == 0) { // parse GSV
  325. parse_gsv(tzer, navdata);
  326. }
  327. }
  328. /*
  329. * Init nmea parser
  330. */
  331. void nmea_parser_init(struct nmea_parser *p)
  332. {
  333. memset(p, 0, sizeof(*p));
  334. }
  335. /*
  336. * Put one char into nmea parser
  337. * @p: nmea_parser
  338. * @c: the character placed into nmea parser
  339. */
  340. void nmea_parser_putchar(struct nmea_parser *p, char c)
  341. {
  342. nmea_reader_add(p->reader, c);
  343. if (c == '\n') {
  344. if (nmea_reader_check(p->reader)) {
  345. nmea_tokenizer_init(p->tzer, p->reader->buf, p->reader->buf + p->reader->pos);
  346. parse(p->tzer, p->data, p->report_nav_status);
  347. }
  348. nmea_reader_init(p->reader);
  349. }
  350. }