nmea.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #include <avr/wdt.h>
  2. #include <stddef.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <avr/sleep.h>
  6. #include "nmea.h"
  7. #include "main.h"
  8. #include "uart0.h"
  9. #include "uart1.h"
  10. #include "xprintf.h"
  11. /*----------------------------------------------------*/
  12. /* Get a line received from GPS module */
  13. /*----------------------------------------------------*/
  14. UINT get_line ( /* 0:Brownout or timeout, >0: Number of bytes received. */
  15. char *buff,
  16. UINT sz_buf
  17. )
  18. {
  19. char c;
  20. UINT i = 0;
  21. set_timer(recv_timeout, 1000);
  22. for (;;) {
  23. wdt_reset();
  24. if (FLAGS & (F_LVD | F_POWEROFF))
  25. return 0; /* A brownout is detected */
  26. if (timer_expired(recv_timeout))
  27. return 0; /* timeout; continue the main loop */
  28. if (!uart0_test()) {
  29. sleep();
  30. continue;
  31. }
  32. c = (char)uart0_get();
  33. uart1_put(c);
  34. if (i == 0 && c != '$')
  35. continue; /* Find start of line */
  36. buff[i++] = c;
  37. if (c == '\n')
  38. break; /* EOL */
  39. if (i >= sz_buf)
  40. i = 0; /* Buffer overflow (abort this line) */
  41. }
  42. return i;
  43. }
  44. /* Compare sentence header string */
  45. BYTE gp_comp (const char *str1, __flash const char *str2)
  46. {
  47. char c;
  48. do {
  49. c = pgm_read_byte(str2++);
  50. } while (c && c == *str1++);
  51. return c;
  52. }
  53. #define FIELD_BUF_LEN 32
  54. /* Get a column item */
  55. static
  56. const char* gp_col ( /* Returns pointer to the item (returns a NULL when not found) */
  57. const char* buf, /* Pointer to the sentence */
  58. BYTE col /* Column number (0 is the 1st item) */
  59. ) {
  60. BYTE c;
  61. static char field_buf[FIELD_BUF_LEN];
  62. unsigned char length = 0;
  63. while (col) {
  64. do {
  65. c = *buf++;
  66. if (c <= ' ') return NULL;
  67. } while (c != ',');
  68. col--;
  69. }
  70. while (*buf && *buf != ',' && length < FIELD_BUF_LEN-1) {
  71. field_buf[length++] = *buf++;
  72. }
  73. field_buf[length] = '\0';
  74. return field_buf;
  75. }
  76. unsigned int gp_val(const char *db, unsigned char count) {
  77. unsigned int out = 0;
  78. while (count--) {
  79. unsigned char n;
  80. n = *db - '0';
  81. if (n >= 10)
  82. return 0;
  83. out *= 10;
  84. out += n;
  85. db++;
  86. }
  87. return out;
  88. }
  89. static time_t gp_rmc_parse(const char *str) {
  90. const char *p;
  91. struct tm tmc;
  92. p = gp_col(str, 1); /* Get h:m:s */
  93. if (!p)
  94. return 0;
  95. tmc.tm_hour = gp_val(p, 2);
  96. tmc.tm_min = gp_val(p+2, 2);
  97. tmc.tm_sec = gp_val(p+4, 2);
  98. p = gp_col(str, 9); /* Get y:m:d */
  99. if (!p)
  100. return 0;
  101. tmc.tm_mday = gp_val(p, 2);
  102. tmc.tm_mon = gp_val(p+2, 2) - 1;
  103. tmc.tm_year = gp_val(p+4, 2) + 100;
  104. utc = mktime(&tmc); /* Check time validity */
  105. if (utc == -1)
  106. return 0;
  107. p = gp_col(str, 2); /* Get status */
  108. if (!p || *p != 'A') {
  109. System.location_valid = LOC_INVALID;
  110. FLAGS &= ~F_GPSOK;
  111. return 0; /* Return 0 even is time is valid (comes from module's internal RTC) */
  112. }
  113. FLAGS |= F_GPSOK;
  114. return utc;
  115. }
  116. static void gp_gga_parse(const char *str) {
  117. const char *p;
  118. double tmp;
  119. p = gp_col(str, 7); /* satellites used */
  120. System.satellites_used = atoi(p);
  121. /* check validity */
  122. p = gp_col(str, 6);
  123. if (*p == '0') {
  124. System.location_valid = LOC_INVALID;
  125. return;
  126. }
  127. System.location_valid = LOC_VALID_NEW;
  128. /* parse location */
  129. p = gp_col(str, 2); /* latitude */
  130. location.lat = gp_val(p, 2); /* degrees */
  131. p += 2;
  132. xatof(&p, &tmp); /* minutes */
  133. tmp /= 60; /* convert minutes to degrees */
  134. location.lat += tmp;
  135. p = gp_col(str, 3); /* N/S */
  136. if (*p != 'N')
  137. location.lat = -location.lat;
  138. p = gp_col(str, 4); /* longitude */
  139. location.lon = gp_val(p, 3); /* degrees */
  140. p += 3;
  141. xatof(&p, &tmp); /* minutes */
  142. tmp /= 60; /* convert minutes to degrees */
  143. location.lon += tmp;
  144. p = gp_col(str, 5); /* E/W */
  145. if (*p != 'E')
  146. location.lon = -location.lon;
  147. p = gp_col(str, 6); /* fix type */
  148. if (*p == '2')
  149. System.sbas = 1;
  150. else
  151. System.sbas = 0;
  152. p = gp_col(str, 9); /* MSL altitude */
  153. xatof(&p, &tmp);
  154. location.alt = tmp;
  155. location.time = utc; /* parsed from RMC */
  156. }
  157. /*$PMTK355*31<CR><LF>
  158. Return $PMTK001,355,3,1,0,0*2E “$PMTK001,355,3,GLON_Enable,BEIDOU_Enable,GALILEO_Enable”
  159. The GLONASS search mode is enabled. */
  160. static void pmtk001_parse(const char *str) {
  161. const char *p;
  162. /* check validity */
  163. p = gp_col(str, 1);
  164. if (strcmp_P(p, PSTR("355"))) /* not the PMTK355 reply */
  165. return;
  166. p = gp_col(str, 2);
  167. if (*p == '0' || *p == '1') { /* invalid / unsupported */
  168. System.gps_only = 1;
  169. xputs_P(PSTR("GPS only\r\n"));
  170. } else {
  171. System.gps_only = 0;
  172. xputs_P(PSTR("Multi GNSS\r\n"));
  173. }
  174. gps_initialize();
  175. }
  176. time_t gps_parse(const char *str) { /* Get all required data from NMEA sentences */
  177. if (!gp_comp(str, PSTR("$GPRMC")) || !gp_comp(str, PSTR("$GNRMC")) || !gp_comp(str, PSTR("$BDRMC")) || !gp_comp(str, PSTR("$GARMC"))) {
  178. return gp_rmc_parse(str);
  179. }
  180. if (!gp_comp(str, PSTR("$GPGGA")) || !gp_comp(str, PSTR("$GNGGA")) || !gp_comp(str, PSTR("$BDGGA")) || !gp_comp(str, PSTR("$GAGGA"))) {
  181. gp_gga_parse(str);
  182. return 0;
  183. }
  184. if (!System.gps_initialized && !gp_comp(str, PSTR("$PMTK011"))) {
  185. gps_initialize();
  186. return 0;
  187. }
  188. if (!gp_comp(str, PSTR("$PMTK001"))) {
  189. pmtk001_parse(str);
  190. return 0;
  191. }
  192. return 0;
  193. }
  194. void uart0_put_wrap(int c) {
  195. uart0_put((char)c);
  196. }
  197. void gps_initialize(void) {
  198. /*
  199. * PMTK355: query gnss search mode (will fail if only GPS is supported)
  200. * PMTK353: set gnss search mode (GPS/Galileo/Glonass/Beidou)
  201. * PMTK313: enable SBAS
  202. */
  203. if (!System.gps_initialized)
  204. xfprintf(uart0_put_wrap, PSTR("$PMTK355*31\r\n"));
  205. if (get_flag(CONFFLAG_ENABLE_SBAS))
  206. xfprintf(uart0_put_wrap, PSTR("$PMTK313,1*2E\r\n"));
  207. else
  208. xfprintf(uart0_put_wrap, PSTR("$PMTK313,0*2F\r\n"));
  209. if (!System.gps_only) {
  210. switch (System.conf.gnss_mode) {
  211. default:
  212. case GNSS_MODE_GPS_GLONASS_GALILEO:
  213. xfprintf(uart0_put_wrap, PSTR("$PMTK353,1,1,1,0,0*2A\r\n"));
  214. break;
  215. case GNSS_MODE_GPS:
  216. xfprintf(uart0_put_wrap, PSTR("$PMTK353,1,0,0,0,0*2A\r\n"));
  217. break;
  218. case GNSS_MODE_GPS_GALILEO:
  219. xfprintf(uart0_put_wrap, PSTR("$PMTK353,1,0,1,0,0*2B\r\n"));
  220. break;
  221. case GNSS_MODE_GALILEO:
  222. xfprintf(uart0_put_wrap, PSTR("$PMTK353,0,0,1,0,0*2A\r\n"));
  223. break;
  224. case GNSS_MODE_GPS_BEIDOU:
  225. xfprintf(uart0_put_wrap, PSTR("$PMTK353,1,0,0,0,1*2B\r\n"));
  226. break;
  227. case GNSS_MODE_BEIDOU:
  228. xfprintf(uart0_put_wrap, PSTR("$PMTK353,0,0,0,0,1*2A\r\n"));
  229. break;
  230. }
  231. } else {
  232. System.conf.gnss_mode = GNSS_MODE_GPS;
  233. }
  234. xputs_P(PSTR("GPS init sent\r\n"));
  235. System.gps_initialized = 1;
  236. }