nmea.c

Go to the documentation of this file.
00001 
00053 #include "nmea.h"
00054 
00055 #include "cfg/cfg_nmea.h"
00056 
00057 #include <cfg/debug.h>
00058 
00059 #define LOG_LEVEL  NMEA_LOG_LEVEL
00060 #define LOG_FORMAT NMEA_LOG_FORMAT
00061 #include <cfg/log.h>
00062 
00063 #include <net/nmeap/inc/nmeap.h>
00064 
00065 #include <ctype.h>
00066 #include <time.h>
00067 #include <string.h>
00068 #include <stdlib.h>
00069 
00070 /*
00071  * Make conversion from one string to int.
00072  *
00073  * You can specify the precision if the string is a float
00074  * number. The result is an int multiplied to 10^precision.
00075  */
00076 static uint32_t tokenToInt(const char *s, int precision)
00077 {
00078     uint32_t num = 0;
00079     bool sep_found = false;
00080     int i;
00081 
00082     if (!s)
00083         return 0;
00084 
00085     for(i = 0; i < NMEAP_MAX_SENTENCE_LENGTH; i++)
00086     {
00087         char c = *s++;
00088 
00089         if (c == '.')
00090         {
00091             sep_found = true;
00092             continue;
00093         }
00094 
00095         if (c == '\0' || !isdigit(c) || (precision == 0 && sep_found))
00096             break;
00097 
00098         if (sep_found)
00099             precision--;
00100 
00101         num *= 10;
00102         num += c - '0';
00103     }
00104 
00105     while (precision--)
00106         num *= 10;
00107 
00108     return num;
00109 }
00110 
00111 /*
00112  * Convert a string to micro degree.
00113  */
00114 static udegree_t convertToDegree(const char *str)
00115 {
00116     uint32_t dec;
00117     uint32_t deg;
00118     uint32_t min;
00119 
00120     if (*str == 0)
00121     {
00122         return 0;
00123     }
00124 
00125     dec = tokenToInt(str, 4);
00126     deg = dec / 1000000;
00127     min = dec - deg * 1000000;
00128     dec = deg * 1000000 + ((min * 5) + 1) / 3;
00129 
00130     return dec;
00131 }
00132 
00133 /*
00134  * Retun latitude in micro degree from a string.
00135  */
00136 static udegree_t nmea_latitude(const char *plat, const char *phem)
00137 {
00138     int ns;
00139 
00140     if (*phem == 0)
00141         return 0;
00142 
00143     /* north lat is +, south lat is - */
00144     ns = (*phem == 'N') ? 1 : -1;
00145 
00146 
00147     return ns * convertToDegree(plat);
00148 }
00149 
00150 /*
00151  * Retun longitude in micro degree from a string.
00152  */
00153 static udegree_t nmea_longitude(const char *plot, const char *phem)
00154 {
00155     int ew;
00156 
00157     if (*phem == 0)
00158         return 0;
00159 
00160     /* west long is negative, east long is positive */
00161    ew = (*phem == 'E') ? 1 : -1;
00162 
00163     return ew * convertToDegree(plot);
00164 }
00165 
00166 /*
00167  * Return altitude in meter from a string.
00168  *
00169  */
00170 static uint16_t nmea_altitude(const char *palt, const char *punits)
00171 {
00172     uint32_t alt;
00173 
00174     if (*palt == 0)
00175         return 0;
00176 
00177     alt = atoi(palt);
00178 
00179     if (*punits == 'F')
00180     {
00181         /* convert to feet */
00182         /* alt = alt * 3.2808399 */
00183         alt = alt * 3 +  /* 3.0 */
00184               (alt >> 2) + /* 0.25 */
00185               (alt >> 6) + /* 0.015625 */
00186               (alt >> 7) + /* 0.0078125 */
00187               (alt >> 8); /* 0,00390625 */
00188 
00189     }
00190 
00191     return alt;
00192 }
00193 
00194 /*
00195  * Convert time and date stamp string to unix time.
00196  */
00197 static time_t timestampToSec(uint32_t time_stamp, uint32_t date_stamp)
00198 {
00199     struct tm t;
00200     uint16_t msec;
00201     uint16_t tmr[3];
00202     uint16_t date[3];
00203 
00204     memset(&t, 0, sizeof(t));
00205     memset(&tmr, 0, sizeof(tmr));
00206     memset(&date, 0, sizeof(date));
00207 
00208     LOG_INFO("time_s[%lu],date[%lu]\n", (long)time_stamp, (long)date_stamp);
00209     uint32_t res = time_stamp / 1000;
00210     uint32_t all = time_stamp;
00211     msec = all - res * 1000;
00212 
00213     for (int i = 0; i < 3; i++)
00214     {
00215         all = res;
00216         res = all / 100;
00217         tmr[i]  = all - res * 100;
00218         LOG_INFO("t[%d]%d\n", tmr[i],i);
00219     }
00220 
00221     t.tm_sec = tmr[0] + (ROUND_UP(msec, 1000) / 1000);
00222     t.tm_min = tmr[1];
00223     t.tm_hour = tmr[2];
00224     //If we not have refence data, we set as default 1/1/1970.
00225     t.tm_mday = 1;
00226     t.tm_mon = 0;
00227     t.tm_year = 70;
00228 
00229     if (date_stamp)
00230     {
00231         res = all = date_stamp;
00232         for (int i = 0; i < 3; i++)
00233         {
00234             all = res;
00235             res = all / 100;
00236             date[i]  = all - res * 100;
00237             LOG_INFO("d[%d]%d\n", date[i],i);
00238         }
00239         t.tm_mday = date[2];
00240         t.tm_mon = date[1] - 1; // time struct count month from 0 to 11;
00241         // we should specific number of years from 1900, but the year field
00242         // is only two cipher, so we sum 100 (2000 - 1900)..
00243         t.tm_year = date[0] + 100;
00244     }
00245     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);
00246 
00247     return  mktime(&t);
00248 }
00249 
00253 void gpgga_callout(nmeap_context_t *context, void *data, void *user_data)
00254 {
00255     (void)context;
00256     (void)user_data;
00257     (void)data;
00258     LOG_INFOB(
00259         NmeaGga *gga = (NmeaGga *)data;
00260         LOG_INFO("Found GPGGA message %ld %ld %d %lu %d %d %d %d\n",
00261                 (long)gga->latitude,
00262                 (long)gga->longitude,
00263                 gga->altitude,
00264                 gga->time,
00265                 gga->satellites,
00266                 gga->quality,
00267                 gga->hdop,
00268                 gga->geoid);
00269     );
00270 }
00271 
00275 void gprmc_callout(nmeap_context_t *context, void *data, void *user_data)
00276 {
00277     (void)context;
00278     (void)user_data;
00279     (void)data;
00280     LOG_INFOB(
00281         NmeaRmc *rmc = (NmeaRmc *)data;
00282 
00283         LOG_INFO("Found GPRMC message %lu %c %ld %ld %d %d %d\n",
00284                 rmc->time,
00285                 rmc->warn,
00286                 (long)rmc->latitude,
00287                 (long)rmc->longitude,
00288                 rmc->speed,
00289                 rmc->course,
00290                 rmc->mag_var);
00291     );
00292 }
00293 
00297 void gpgsv_callout(nmeap_context_t *context, void *data, void *user_data)
00298 {
00299     (void)context;
00300     (void)user_data;
00301     (void)data;
00302     LOG_INFOB(
00303         NmeaGsv *gsv = (NmeaGsv *)data;
00304 
00305         LOG_INFO("Found GPGSV message %d %d %d\n", gsv->tot_message, gsv->message_num, gsv->tot_svv);
00306 
00307         for (int i = 0; i < 4; i++)
00308             LOG_INFO("%d %d %d %d\n", gsv->info[i].sv_prn, gsv->info[i].elevation, gsv->info[i].azimut, gsv->info[i].snr);
00309     );
00310 }
00311 
00315 void gpvtg_callout(nmeap_context_t *context, void *data, void *user_data)
00316 {
00317     (void)context;
00318     (void)user_data;
00319     (void)data;
00320     LOG_INFOB(
00321         NmeaVtg *vtg = (NmeaVtg *)data;
00322         LOG_INFO("Found GPVTG message %d %d %d\n", vtg->track_good, vtg->knot_speed, vtg->km_speed);
00323     );
00324 }
00325 
00326 
00327 
00331 int nmea_gpgga(nmeap_context_t *context, nmeap_sentence_t *sentence)
00332 {
00333     /*
00334      * get pointer to sentence data
00335      */
00336     NmeaGga *gga = (NmeaGga *)sentence->data;
00337 
00338     ASSERT(gga);
00339     ASSERT(context->tokens >= 12);
00340 
00341     gga->latitude   = nmea_latitude(context->token[2],context->token[3]);
00342     gga->longitude  = nmea_longitude(context->token[4],context->token[5]);
00343     gga->altitude   = nmea_altitude(context->token[9],context->token[10]);
00344     gga->time       = timestampToSec(tokenToInt(context->token[1], 3), 0);
00345     gga->satellites = atoi(context->token[7]);
00346     gga->quality    = atoi(context->token[6]);
00347     gga->hdop       = tokenToInt(context->token[8], 1);
00348     gga->geoid      = nmea_altitude(context->token[11],context->token[12]);
00349 
00350     /*
00351      * if the sentence has a callout, call it
00352      */
00353 
00354     if (sentence->callout != 0)
00355         (*sentence->callout)(context, gga, context->user_data);
00356 
00357     return NMEA_GPGGA;
00358 }
00359 
00363 int nmea_gprmc(nmeap_context_t *context, nmeap_sentence_t *sentence)
00364 {
00365 
00366     /*
00367      * get pointer to sentence data
00368      */
00369     NmeaRmc *rmc = (NmeaRmc *)sentence->data;
00370 
00371     ASSERT(rmc);
00372     ASSERT(context->tokens >= 10);
00373 
00374     /*
00375      * extract data from the tokens
00376      */
00377     rmc->time       = timestampToSec(tokenToInt(context->token[1], 3), tokenToInt(context->token[9], 0));
00378     rmc->warn       = *context->token[2];
00379     rmc->latitude   = nmea_latitude(context->token[3],context->token[4]);
00380     rmc->longitude  = nmea_longitude(context->token[5],context->token[6]);
00381     rmc->speed      = atoi(context->token[7]);
00382     rmc->course     = atoi(context->token[8]);
00383     rmc->mag_var    = atoi(context->token[10]);
00384 
00385     if (sentence->callout != 0)
00386         (*sentence->callout)(context, rmc, context->user_data);
00387 
00388     return NMEA_GPRMC;
00389 }
00390 
00391 
00395 int nmea_gpvtg(nmeap_context_t *context, nmeap_sentence_t *sentence)
00396 {
00397 
00398     /*
00399      * get pointer to sentence data
00400      */
00401     NmeaVtg *vtg = (NmeaVtg *)sentence->data;
00402 
00403     ASSERT(vtg);
00404     ASSERT(context->tokens >= 7);
00405 
00406     /*
00407      * extract data from the tokens
00408      */
00409     vtg->track_good  = atoi(context->token[1]);
00410     vtg->knot_speed  = atoi(context->token[5]);
00411     vtg->km_speed    = atoi(context->token[7]);
00412 
00413     /*
00414      * if the sentence has a callout, call it
00415      */
00416     if (sentence->callout != 0)
00417         (*sentence->callout)(context, vtg, context->user_data);
00418 
00419     return NMEA_GPVTG;
00420 }
00421 
00425 int nmea_gpgsv(nmeap_context_t *context, nmeap_sentence_t *sentence)
00426 {
00427 
00428     /*
00429      * get pointer to sentence data
00430      */
00431     NmeaGsv *gsv = (NmeaGsv *)sentence->data;
00432 
00433 
00434     /*
00435      * extract data from the tokens
00436      */
00437     gsv->tot_message     = atoi(context->token[1]);
00438     gsv->message_num     = atoi(context->token[2]);
00439     gsv->tot_svv         = atoi(context->token[3]);
00440 
00441     // Fill remaning member until we have token
00442     int  j = 0;
00443     for (int i = 4; i < context->tokens - 3; i += 4, j++)
00444     {
00445 
00446         gsv->info[j].sv_prn     = atoi(context->token[i]);
00447         gsv->info[j].elevation  = atoi(context->token[i + 1]);
00448         gsv->info[j].azimut     = atoi(context->token[i + 2]);
00449         gsv->info[j].snr        = atoi(context->token[i + 3]);
00450     }
00451 
00452     /*
00453      * if the sentence has a callout, call it
00454      */
00455     if (sentence->callout != 0)
00456         (*sentence->callout)(context, gsv, context->user_data);
00457 
00458     return NMEA_GPGSV;
00459 }
00460 
00461 
00462 /*
00463  * Parse NMEA sentence from a channel.
00464  */
00465 void nmea_poll(nmeap_context_t *context, KFile *channel)
00466 {
00467     int c;
00468     while ((c = kfile_getc(channel)) != EOF)
00469     {
00470         nmeap_parse(context, c);
00471     }
00472 }
00473