4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2009 Develer S.r.l. (http://www.develer.com/)
33 * \brief NMEA parser implementation.
35 * NMEA 0183 is acronym of National Marine Electronics Association that
36 * combined electrical and data specification for communication between marine
37 * electronic devices such as echo sounder, sonars, anemometer (wind speed and direction),
38 * gyrocompass, autopilot, GPS receivers and many other types of instruments.
39 * It has been defined by, and is controlled by, the U.S.-based National Marine
40 * Electronics Association.
41 * The NMEA 0183 standard uses a simple ASCII, serial communications protocol
42 * that defines how data is transmitted in a "sentence" from one "talker"
43 * to multiple "listeners" at a time.
44 * At the application layer, the standard also defines the contents of each sentence
45 * (message) type so that all listeners can parse messages accurately.
48 * \author Daniele Basile <asterix@develer.com>
55 #include "cfg/cfg_nmea.h"
57 #include <cfg/debug.h>
59 #define LOG_LEVEL NMEA_LOG_LEVEL
60 #define LOG_FORMAT NMEA_LOG_FORMAT
63 #include <net/nmeap/inc/nmeap.h>
71 * Make conversion from one string to int.
73 * You can specify the precision if the string is a float
74 * number. The result is an int multiplied to 10^precision.
76 static uint32_t tokenToInt(const char *s, int precision)
79 bool sep_found = false;
85 for(i = 0; i < NMEAP_MAX_SENTENCE_LENGTH; i++)
87 unsigned char c = *s++;
95 if (c == '\0' || !isdigit(c) || (precision == 0 && sep_found))
112 * Convert a string to micro degree.
114 static udegree_t convertToDegree(const char *str)
123 dec = tokenToInt(str, 4);
125 min = dec - deg * 1000000;
126 dec = deg * 1000000 + ((min * 5) + 1) / 3;
132 * Retun latitude in micro degree from a string.
134 static udegree_t nmea_latitude(const char *plat, const char *phem)
141 /* north lat is +, south lat is - */
142 ns = (*phem == 'N') ? 1 : -1;
145 return ns * convertToDegree(plat);
149 * Retun longitude in micro degree from a string.
151 static udegree_t nmea_longitude(const char *plot, const char *phem)
158 /* west long is negative, east long is positive */
159 ew = (*phem == 'E') ? 1 : -1;
161 return ew * convertToDegree(plot);
165 * Return altitude in meter from a string.
168 static int32_t nmea_altitude(const char *palt, const char *punits)
179 /* convert to feet */
180 /* alt = alt * 3.2808399 */
181 alt = alt * 3 + /* 3.0 */
182 (alt >> 2) + /* 0.25 */
183 (alt >> 6) + /* 0.015625 */
184 (alt >> 7) + /* 0.0078125 */
185 (alt >> 8); /* 0,00390625 */
193 * Convert time and date stamp string to unix time.
195 static time_t timestampToSec(uint32_t time_stamp, uint32_t date_stamp)
202 memset(&t, 0, sizeof(t));
203 memset(&tmr, 0, sizeof(tmr));
204 memset(&date, 0, sizeof(date));
206 LOG_INFO("time_s[%lu],date[%lu]\n", (long)time_stamp, (long)date_stamp);
207 uint32_t res = time_stamp / 1000;
208 uint32_t all = time_stamp;
209 msec = all - res * 1000;
211 for (int i = 0; i < 3; i++)
215 tmr[i] = all - res * 100;
216 LOG_INFO("t[%d]%d\n", tmr[i],i);
219 t.tm_sec = tmr[0] + (ROUND_UP(msec, 1000) / 1000);
222 //If we do not have refence data, we set 1/1/1970 as default
229 res = all = date_stamp;
230 for (int i = 0; i < 3; i++)
234 date[i] = all - res * 100;
235 LOG_INFO("d[%d]%d\n", date[i],i);
238 t.tm_mon = date[1] - 1; // time struct count month from 0 to 11;
239 // we should specify the number of years from 1900, but the year field
240 // is only two digits, so we add 100 (2000 - 1900)..
241 t.tm_year = date[0] + 100;
243 LOG_INFO("times=%d,%d,%d,%d,%d,%d\n",t.tm_sec, t.tm_min, t.tm_hour, t.tm_year, t.tm_mon, t.tm_mday);
249 * Callout example for GGA data
251 void gpgga_callout(nmeap_context_t *context, void *data, void *user_data)
257 NmeaGga *gga = (NmeaGga *)data;
258 LOG_INFO("Found GPGGA message %ld %ld %d %lu %d %d %d %d\n",
260 (long)gga->longitude,
271 * Callout example for RMC
273 void gprmc_callout(nmeap_context_t *context, void *data, void *user_data)
279 NmeaRmc *rmc = (NmeaRmc *)data;
281 LOG_INFO("Found GPRMC message %lu %c %ld %ld %d %d %d\n",
285 (long)rmc->longitude,
293 * Callout example for GSV data
295 void gpgsv_callout(nmeap_context_t *context, void *data, void *user_data)
301 NmeaGsv *gsv = (NmeaGsv *)data;
303 LOG_INFO("Found GPGSV message %d %d %d\n", gsv->tot_message, gsv->message_num, gsv->tot_svv);
305 for (int i = 0; i < 4; i++)
306 LOG_INFO("%d %d %d %d\n", gsv->info[i].sv_prn, gsv->info[i].elevation, gsv->info[i].azimut, gsv->info[i].snr);
311 * Callout example for VTG data
313 void gpvtg_callout(nmeap_context_t *context, void *data, void *user_data)
319 NmeaVtg *vtg = (NmeaVtg *)data;
320 LOG_INFO("Found GPVTG message %d %d %d\n", vtg->track_good, vtg->knot_speed, vtg->km_speed);
327 * standard GPGGA sentence parser
329 int nmea_gpgga(nmeap_context_t *context, nmeap_sentence_t *sentence)
332 * get pointer to sentence data
334 NmeaGga *gga = (NmeaGga *)sentence->data;
337 ASSERT(context->tokens >= 12);
339 gga->latitude = nmea_latitude(context->token[2],context->token[3]);
340 gga->longitude = nmea_longitude(context->token[4],context->token[5]);
341 gga->altitude = nmea_altitude(context->token[9],context->token[10]);
342 gga->time = timestampToSec(tokenToInt(context->token[1], 3), 0);
343 gga->satellites = atoi(context->token[7]);
344 gga->quality = atoi(context->token[6]);
345 gga->hdop = tokenToInt(context->token[8], 1);
346 gga->geoid = nmea_altitude(context->token[11],context->token[12]);
349 * if the sentence has a callout, call it
352 if (sentence->callout != 0)
353 (*sentence->callout)(context, gga, context->user_data);
359 * standard GPRMCntence parser
361 int nmea_gprmc(nmeap_context_t *context, nmeap_sentence_t *sentence)
365 * get pointer to sentence data
367 NmeaRmc *rmc = (NmeaRmc *)sentence->data;
370 ASSERT(context->tokens >= 10);
373 * extract data from the tokens
375 rmc->time = timestampToSec(tokenToInt(context->token[1], 3), tokenToInt(context->token[9], 0));
376 rmc->warn = *context->token[2];
377 rmc->latitude = nmea_latitude(context->token[3],context->token[4]);
378 rmc->longitude = nmea_longitude(context->token[5],context->token[6]);
379 rmc->speed = atoi(context->token[7]);
380 rmc->course = atoi(context->token[8]);
381 rmc->mag_var = atoi(context->token[10]);
383 if (sentence->callout != 0)
384 (*sentence->callout)(context, rmc, context->user_data);
391 * standard GPVTG sentence parser
393 int nmea_gpvtg(nmeap_context_t *context, nmeap_sentence_t *sentence)
397 * get pointer to sentence data
399 NmeaVtg *vtg = (NmeaVtg *)sentence->data;
402 ASSERT(context->tokens >= 7);
405 * extract data from the tokens
407 vtg->track_good = atoi(context->token[1]);
408 vtg->knot_speed = atoi(context->token[5]);
409 vtg->km_speed = atoi(context->token[7]);
412 * if the sentence has a callout, call it
414 if (sentence->callout != 0)
415 (*sentence->callout)(context, vtg, context->user_data);
421 * standard GPGDSV sentence parser
423 int nmea_gpgsv(nmeap_context_t *context, nmeap_sentence_t *sentence)
426 * get pointer to sentence data
428 NmeaGsv *gsv = (NmeaGsv *)sentence->data;
432 * extract data from the tokens
434 gsv->tot_message = atoi(context->token[1]);
435 gsv->message_num = atoi(context->token[2]);
436 gsv->tot_svv = atoi(context->token[3]);
438 // Fill remaning member until we have token
440 for (int i = 4; i < context->tokens - 3; i += 4, j++)
443 gsv->info[j].sv_prn = atoi(context->token[i]);
444 gsv->info[j].elevation = atoi(context->token[i + 1]);
445 gsv->info[j].azimut = atoi(context->token[i + 2]);
446 gsv->info[j].snr = atoi(context->token[i + 3]);
450 * if the sentence has a callout, call it
452 if (sentence->callout != 0)
453 (*sentence->callout)(context, gsv, context->user_data);
460 * Parse NMEA sentence from a channel.
462 void nmea_poll(nmeap_context_t *context, KFile *channel)
465 while ((c = kfile_getc(channel)) != EOF)
466 nmeap_parse(context, c);
468 if ((e = kfile_error(channel)))
470 LOG_ERR("ch error [%0X]\n", e);
471 kfile_clearerr(channel);