nmea.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. #ifdef PC_BUILD
  2. #include "../gps-test-tool/main.h"
  3. #else
  4. #include "main.h"
  5. #endif
  6. /*----------------------------------------------------*/
  7. /* Get a line received from GPS module */
  8. /*----------------------------------------------------*/
  9. uint16_t get_line ( /* 0:line incomplete or timed out, >0: Number of bytes received. */
  10. char *buff,
  11. uint16_t sz_buf
  12. )
  13. {
  14. char c;
  15. static uint16_t i = 0;
  16. uint16_t ret_len;
  17. set_timer(recv_timeout, 1000);
  18. for (;;) {
  19. wdt_reset();
  20. if (FLAGS & (F_LVD | F_POWEROFF))
  21. return 0; /* A brownout is detected */
  22. if (timer_expired(recv_timeout))
  23. return 0; /* timeout; continue the main loop */
  24. if (System.keypress) /* process user keypress */
  25. return 0;
  26. if (!uart0_test()) {
  27. sleep();
  28. continue;
  29. }
  30. c = (char)uart0_get();
  31. if (i == 0 && c != '$' )
  32. continue; /* Find start of line */
  33. if (c == '\n' || c == '\r') {
  34. buff[i++] = '\r';
  35. buff[i++] = '\n';
  36. buff[i++] = '\0'; /* add null termination for string */
  37. break; /* EOL */
  38. }
  39. buff[i++] = c;
  40. if (System.conf.uart_log_mode != UART_LOG_MODE_SYSTEM)
  41. uart1_put(c);
  42. if (i >= sz_buf - 3) /* keep 3 bytes for terminating character */
  43. i = 0; /* Buffer overflow (abort this line) */
  44. }
  45. ret_len = i;
  46. i = 0;
  47. if (ret_len > 0 && System.conf.uart_log_mode != UART_LOG_MODE_SYSTEM) {
  48. uart1_put('\r');
  49. uart1_put('\n');
  50. }
  51. return ret_len;
  52. }
  53. /* Compare sentence header string */
  54. BYTE gp_comp (const char *str1, __flash const char *str2)
  55. {
  56. char c;
  57. do {
  58. c = pgm_read_byte(str2++);
  59. } while (c && c == *str1++);
  60. return c;
  61. }
  62. /* Per-epoch GGA/GSA/logged tracking for nmea_epoch_complete() - partial parse
  63. * state, not global "fully cooked" data, so kept local to this file rather
  64. * than on System. */
  65. static struct {
  66. unsigned char gga_ok : 1;
  67. unsigned char gsa_ok : 1;
  68. unsigned char logged : 1;
  69. } epoch;
  70. #define FIELD_BUF_LEN 32
  71. /* Get a column item */
  72. static
  73. const char* gp_col ( /* Returns pointer to the item (returns a NULL when not found) */
  74. const char* buf, /* Pointer to the sentence */
  75. BYTE col /* Column number (0 is the 1st item) */
  76. ) {
  77. BYTE c;
  78. static char field_buf[FIELD_BUF_LEN];
  79. unsigned char length = 0;
  80. while (col) {
  81. do {
  82. c = *buf++;
  83. if (c <= ' ') return NULL;
  84. } while (c != ',');
  85. col--;
  86. }
  87. while (*buf && *buf != ',' && length < FIELD_BUF_LEN-1) {
  88. field_buf[length++] = *buf++;
  89. }
  90. field_buf[length] = '\0';
  91. return field_buf;
  92. }
  93. unsigned int gp_val(const char *db, unsigned char count) {
  94. unsigned int out = 0;
  95. while (count--) {
  96. unsigned char n;
  97. n = *db - '0';
  98. if (n >= 10)
  99. return 0;
  100. out *= 10;
  101. out += n;
  102. db++;
  103. }
  104. return out;
  105. }
  106. #define SPEED_SAMPLE_CAP_MULT 3 /* cap any single sample at this multiple of the running average... */
  107. #define SPEED_SAMPLE_CAP_MIN_SAMPLES 60 /* ...but only once the average actually means something, ... */
  108. #define SPEED_SAMPLE_CAP_FLOOR_X100 2000 /* ...and never cap below this (20 km/h): the cap is a backstop against
  109. * a single wild outlier, not a speed limit - persistence (below) is what
  110. * lets a genuinely sustained fast segment (car/train) through. */
  111. #define SPEED_SAMPLE_HIGH_HISTORY_LEN 7 /* consider this many of the most recent samples... */
  112. #define SPEED_SAMPLE_HIGH_HISTORY_MASK ((1U << SPEED_SAMPLE_HIGH_HISTORY_LEN) - 1)
  113. #define SPEED_SAMPLE_HIGH_MIN_COUNT 5 /* ...and once this many of them were above the cap, stop clamping:
  114. * a single spike (e.g. the 200 km/h one-second glitch seen on a real
  115. * hike) never reaches this, but a sustained fast segment does within a
  116. * few seconds. */
  117. /* Bit history of "was this raw sample above the cap" for the last
  118. * SPEED_SAMPLE_HIGH_HISTORY_LEN samples (bit 0: most recent). Static file
  119. * scope, not part of System - it's working state for the cap logic only,
  120. * nothing else needs it. */
  121. static unsigned char speed_sample_high_history;
  122. static void speed_sample_process(double speed_kmh) {
  123. unsigned char paused = tracking_is_paused();
  124. unsigned char fix_trustworthy = gps_fix_trustworthy();
  125. System.speed = speed_kmh+0.5;
  126. /* Average speed: fixed-point accumulation of instantaneous samples,
  127. * excluding time spent paused, instead of dividing distance by time. */
  128. if (!paused && fix_trustworthy) {
  129. unsigned long int sample_x100 = (unsigned long int)(speed_kmh*100.0 + 0.5);
  130. if (System.speed_sample_count >= SPEED_SAMPLE_CAP_MIN_SAMPLES) {
  131. unsigned long int avg_x100 = System.speed_accum_x100 / System.speed_sample_count;
  132. unsigned long int cap_x100 = avg_x100 * SPEED_SAMPLE_CAP_MULT;
  133. unsigned char is_high;
  134. if (cap_x100 < SPEED_SAMPLE_CAP_FLOOR_X100)
  135. cap_x100 = SPEED_SAMPLE_CAP_FLOOR_X100;
  136. is_high = sample_x100 > cap_x100;
  137. speed_sample_high_history = (speed_sample_high_history << 1) | is_high;
  138. if (is_high && __builtin_popcount(speed_sample_high_history & SPEED_SAMPLE_HIGH_HISTORY_MASK) < SPEED_SAMPLE_HIGH_MIN_COUNT)
  139. sample_x100 = cap_x100; /* not (yet) persistent: clamp as a backstop against a lone spike */
  140. }
  141. System.speed_accum_x100 += sample_x100;
  142. System.speed_sample_count++;
  143. }
  144. }
  145. static time_t gp_rmc_parse(const char *str) {
  146. const char *p;
  147. struct tm tmc;
  148. double speed_knots;
  149. epoch.gga_ok = 0;
  150. epoch.gsa_ok = 0;
  151. epoch.logged = 0;
  152. p = gp_col(str, 1); /* Get h:m:s */
  153. if (!p)
  154. return 0;
  155. tmc.tm_hour = gp_val(p, 2);
  156. tmc.tm_min = gp_val(p+2, 2);
  157. tmc.tm_sec = gp_val(p+4, 2);
  158. p = gp_col(str, 9); /* Get y:m:d */
  159. if (!p)
  160. return 0;
  161. tmc.tm_mday = gp_val(p, 2);
  162. tmc.tm_mon = gp_val(p+2, 2) - 1;
  163. tmc.tm_year = gp_val(p+4, 2) + 100;
  164. utc = mktime(&tmc); /* Check time validity */
  165. if (utc == -1)
  166. return 0;
  167. p = gp_col(str, 2); /* Get status */
  168. if (!p || *p != 'A') {
  169. System.location_valid = LOC_INVALID;
  170. FLAGS &= ~F_GPSOK;
  171. return 0; /* Return 0 even is time is valid (comes from module's internal RTC) */
  172. }
  173. FLAGS |= F_GPSOK;
  174. /* Speed over ground, from this same sentence (and the same epoch whose
  175. * status field was just checked above), in knots - converted to km/h.
  176. * Not VTG: some receivers omit VTG or send a shorter form of it, and RMC
  177. * is the one sentence essentially every NMEA GNSS module emits. */
  178. p = gp_col(str, 7);
  179. if (p && *p) {
  180. xatof(&p, &speed_knots);
  181. speed_sample_process(speed_knots * 1.852);
  182. }
  183. return utc;
  184. }
  185. static void gp_gga_parse(const char *str) {
  186. const char *p;
  187. double tmp;
  188. p = gp_col(str, 7); /* satellites used */
  189. if (!p)
  190. return;
  191. System.satellites_used = atoi(p);
  192. epoch.gga_ok = 1;
  193. if (System.satellites_used >= System.conf.min_sats) {
  194. System.sat_count_low = 0;
  195. } else {
  196. System.sat_count_low = 1;
  197. }
  198. /* check validity */
  199. p = gp_col(str, 6);
  200. if (!p)
  201. return;
  202. if (*p == '0') {
  203. System.location_valid = LOC_INVALID;
  204. return;
  205. }
  206. if (!System.sat_count_low)
  207. System.location_valid = LOC_VALID_NEW; /* don't accept the coordinates otherwise, even if reported valid */
  208. /* parse location */
  209. p = gp_col(str, 2); /* latitude */
  210. if (!p)
  211. return;
  212. location.lat = gp_val(p, 2); /* degrees */
  213. p += 2;
  214. xatof(&p, &tmp); /* minutes */
  215. tmp /= 60; /* convert minutes to degrees */
  216. location.lat += tmp;
  217. p = gp_col(str, 3); /* N/S */
  218. if (!p)
  219. return;
  220. if (*p != 'N')
  221. location.lat = -location.lat;
  222. p = gp_col(str, 4); /* longitude */
  223. if (!p)
  224. return;
  225. location.lon = gp_val(p, 3); /* degrees */
  226. p += 3;
  227. xatof(&p, &tmp); /* minutes */
  228. tmp /= 60; /* convert minutes to degrees */
  229. location.lon += tmp;
  230. p = gp_col(str, 5); /* E/W */
  231. if (!p)
  232. return;
  233. if (*p != 'E')
  234. location.lon = -location.lon;
  235. p = gp_col(str, 6); /* fix type */
  236. if (!p)
  237. return;
  238. if (*p == '2')
  239. System.sbas = 1;
  240. else
  241. System.sbas = 0;
  242. p = gp_col(str, 9); /* MSL altitude */
  243. if (!p)
  244. return;
  245. xatof(&p, &tmp);
  246. location.alt = tmp;
  247. location.time = utc; /* parsed from RMC */
  248. }
  249. /* A fix is only trusted (for the speed average, and for the auto-pause
  250. * speed-based unpause check below) when it is valid, of a known type, and
  251. * reasonably accurate: a single sample from a poor fix (e.g. HDOP 16 with
  252. * only a few satellites) would otherwise distort the whole-session average,
  253. * or - worse - talk the auto-pause logic into resuming a stationary logger. */
  254. #define FIX_QUALITY_HDOP_MAX_X100 500 /* a fix is untrustworthy once HDOP > 5.00 */
  255. unsigned char gps_fix_trustworthy(void) {
  256. return (FLAGS & F_GPSOK) && System.fix_type != FIX_TYPE_UNKNOWN
  257. && System.hdop_x100 <= FIX_QUALITY_HDOP_MAX_X100;
  258. }
  259. static void gp_gsa_parse(const char *str) {
  260. const char *p;
  261. double hdop;
  262. p = gp_col(str, 2); /* fix type: 1 no fix, 2 2D, 3 3D */
  263. if (p && *p == '2')
  264. System.fix_type = FIX_TYPE_2D;
  265. else if (p && *p == '3')
  266. System.fix_type = FIX_TYPE_3D;
  267. else
  268. System.fix_type = FIX_TYPE_UNKNOWN;
  269. p = gp_col(str, 16); /* HDOP */
  270. if (p && *p) {
  271. xatof(&p, &hdop);
  272. System.hdop_x100 = (unsigned int)(hdop*100.0 + 0.5);
  273. }
  274. epoch.gsa_ok = 1;
  275. }
  276. /* Returns 1 exactly once per epoch (the moment both GGA and GSA have been
  277. * parsed for it, whichever arrives second), so callers that need this
  278. * epoch's satellite count and HDOP together (e.g. logging a fix event) never
  279. * see a stale value left over from a previous epoch or an earlier line -
  280. * regardless of which order the receiver sends GGA/GSA in. */
  281. unsigned char nmea_epoch_complete(void) {
  282. if (epoch.gga_ok && epoch.gsa_ok && !epoch.logged) {
  283. epoch.logged = 1;
  284. return 1;
  285. }
  286. return 0;
  287. }
  288. /*$PMTK355*31<CR><LF>
  289. Return $PMTK001,355,3,1,0,0*2E “$PMTK001,355,3,GLON_Enable,BEIDOU_Enable,GALILEO_Enable”
  290. The GLONASS search mode is enabled. */
  291. static void pmtk001_parse(const char *str) {
  292. const char *p;
  293. /* check validity */
  294. p = gp_col(str, 1);
  295. if (strcmp_P(p, PSTR("355"))) /* not the PMTK355 reply */
  296. return;
  297. p = gp_col(str, 2);
  298. if (*p == '0' || *p == '1') { /* invalid / unsupported */
  299. System.gps_only = 1;
  300. xputs_P(PSTR("GPS only\r\n"));
  301. } else {
  302. System.gps_only = 0;
  303. xputs_P(PSTR("Multi GNSS\r\n"));
  304. }
  305. gps_initialize();
  306. }
  307. unsigned char nmea_checksum(const char *str) {
  308. unsigned char cs = 0;
  309. while (*str && *str != '*') {
  310. cs ^= *str++; /* NMEA checksum is quite primitive, but still should catch simple bitstream errors or truncated lines */
  311. }
  312. return cs;
  313. }
  314. time_t gps_parse(const char *str) { /* Get all required data from NMEA sentences */
  315. signed int len = strlen(str)-2; /* remove final \r\n */
  316. const char *checksum;
  317. unsigned char calc_checksum, inc_checksum;
  318. signed int i;
  319. char c;
  320. if (len < 4)
  321. return 0; /* each message must contain $.*xx where . is one or more actual data characters and xx is the checksum */
  322. for (i = len-1; i && i >= (len-2); i--) { /* find checksum */
  323. if (str[i] == '*')
  324. break;
  325. }
  326. checksum = str+i+1; /* skip * character */
  327. inc_checksum = 0; /* parse hex string */
  328. while (*checksum && isalnum(*checksum)) {
  329. inc_checksum *= 16;
  330. c = *checksum++;
  331. if (c >= '0' && c <= '9') {
  332. inc_checksum += c - '0';
  333. } else if (c >= 'a' && c <= 'f') {
  334. inc_checksum += c - 'a' + 10;
  335. } else if (c >= 'A' && c <= 'F') {
  336. inc_checksum += c - 'A' + 10;
  337. } else {
  338. xputs_P(PSTR("Invalid NMEA: malformed checksum\r\n"));
  339. return 0; /* invalid checksum character */
  340. }
  341. }
  342. str++; /* drop initial $ */
  343. calc_checksum = nmea_checksum(str);
  344. if (inc_checksum != calc_checksum) {
  345. xputs_P(PSTR("Invalid NMEA checksum received\r\n"));
  346. return 0;
  347. }
  348. if (!gp_comp(str, PSTR("GPRMC")) || !gp_comp(str, PSTR("GNRMC")) || !gp_comp(str, PSTR("BDRMC")) || !gp_comp(str, PSTR("GARMC"))) {
  349. return gp_rmc_parse(str);
  350. }
  351. if (!gp_comp(str, PSTR("GPGGA")) || !gp_comp(str, PSTR("GNGGA")) || !gp_comp(str, PSTR("BDGGA")) || !gp_comp(str, PSTR("GAGGA"))) {
  352. gp_gga_parse(str);
  353. return 0;
  354. }
  355. if (!gp_comp(str, PSTR("GPGSA")) || !gp_comp(str, PSTR("GNGSA")) || !gp_comp(str, PSTR("BDGSA")) || !gp_comp(str, PSTR("GAGSA"))) {
  356. gp_gsa_parse(str);
  357. return 0;
  358. }
  359. if (!System.gps_initialized && !gp_comp(str, PSTR("PMTK011"))) {
  360. gps_initialize();
  361. return 0;
  362. }
  363. if (!gp_comp(str, PSTR("PMTK001"))) {
  364. pmtk001_parse(str);
  365. return 0;
  366. }
  367. if (!gp_comp(str, PSTR("PMTK"))) {
  368. /* Any other PMTK reply (e.g. PMTK705, the firmware release query
  369. * response) - log it verbatim, no need for a dedicated parser. */
  370. log_prefix();
  371. xputs_P(PSTR("RX "));
  372. xputs(str);
  373. return 0;
  374. }
  375. return 0;
  376. }
  377. void uart0_put_wrap(int c) {
  378. uart0_put((char)c);
  379. }
  380. /* Sends the SBAS-enable/disable and GNSS-mode commands for the currently
  381. * configured settings. Used both as the second step of gps_initialize()'s
  382. * boot sequence, and directly as the settings menu's .changed callback for
  383. * the SBAS/GNSS-mode entries - the latter must apply the just-changed
  384. * setting immediately, regardless of System.gps_initialized, rather than
  385. * going through gps_initialize()'s boot state machine (which would instead
  386. * re-send the GNSS-mode query if called before the module's PMTK011 boot
  387. * line has been seen). */
  388. void gps_settings_changed(void) {
  389. /*
  390. * PMTK353: set gnss search mode (GPS/Galileo/Glonass/Beidou)
  391. * PMTK313: enable SBAS
  392. */
  393. if (get_flag(CONFFLAG_ENABLE_SBAS)) {
  394. xfprintf(uart0_put_wrap, PSTR("$PMTK313,1*2E\r\n"));
  395. xputs_P(PSTR("SBAS enable sent\r\n"));
  396. } else {
  397. xfprintf(uart0_put_wrap, PSTR("$PMTK313,0*2F\r\n"));
  398. xputs_P(PSTR("SBAS disable sent\r\n"));
  399. }
  400. if (!System.gps_only) {
  401. switch (System.conf.gnss_mode) {
  402. default:
  403. case GNSS_MODE_GPS_GLONASS_GALILEO:
  404. xfprintf(uart0_put_wrap, PSTR("$PMTK353,1,1,1,0,0*2A\r\n"));
  405. break;
  406. case GNSS_MODE_GPS:
  407. xfprintf(uart0_put_wrap, PSTR("$PMTK353,1,0,0,0,0*2A\r\n"));
  408. break;
  409. case GNSS_MODE_GPS_GALILEO:
  410. xfprintf(uart0_put_wrap, PSTR("$PMTK353,1,0,1,0,0*2B\r\n"));
  411. break;
  412. case GNSS_MODE_GALILEO:
  413. xfprintf(uart0_put_wrap, PSTR("$PMTK353,0,0,1,0,0*2A\r\n"));
  414. break;
  415. case GNSS_MODE_GPS_BEIDOU:
  416. xfprintf(uart0_put_wrap, PSTR("$PMTK353,1,0,0,0,1*2B\r\n"));
  417. break;
  418. case GNSS_MODE_BEIDOU:
  419. xfprintf(uart0_put_wrap, PSTR("$PMTK353,0,0,0,0,1*2A\r\n"));
  420. break;
  421. }
  422. xputs_P(PSTR("GNSS mode setting sent\r\n"));
  423. } else {
  424. System.conf.gnss_mode = GNSS_MODE_GPS;
  425. }
  426. }
  427. void gps_initialize(void) {
  428. /*
  429. * PMTK355: query gnss search mode (will fail if only GPS is supported)
  430. * PMTK605: query firmware release; the reply (PMTK705) is logged
  431. * verbatim by the generic PMTK passthrough in gps_parse()
  432. */
  433. switch (System.gps_initialized) {
  434. case GPS_INIT_NOT_INITIALIZED:
  435. xfprintf(uart0_put_wrap, PSTR("$PMTK355*31\r\n"));
  436. xputs_P(PSTR("GNSS mode query sent\r\n"));
  437. xfprintf(uart0_put_wrap, PSTR("$PMTK605*31\r\n"));
  438. System.gps_initialized = GPS_INIT_QUERY_SENT;
  439. break;
  440. case GPS_INIT_QUERY_SENT:
  441. gps_settings_changed();
  442. break;
  443. default:
  444. break;
  445. }
  446. }
  447. void check_min_sat_limit(void) {
  448. if (System.conf.min_sats > 6 && (System.conf.gnss_mode == GNSS_MODE_GPS || System.conf.gnss_mode == GNSS_MODE_GALILEO || System.conf.gnss_mode == GNSS_MODE_BEIDOU)) {
  449. /* by geometry, max visible number is 6..12 */
  450. System.conf.min_sats = 6;
  451. }
  452. }