/*
* Copyright Tim (xtimor@gmail.com)
*
* NMEA library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*/
/*
*
* NMEA library
* URL: http://nmea.sourceforge.net
* Author: Tim (xtimor@gmail.com)
* Licence: http://www.gnu.org/licenses/lgpl.html
* $Id: parse.c 17 2008-03-11 11:56:11Z xtimor $
*
*/
/**
* \file parse.h
* \brief Functions of a low level for analysis of
* packages of NMEA stream.
*
* \code
* ...
* ptype = nmea_pack_type(
* (const char *)parser->buffer + nparsed + 1,
* parser->buff_use - nparsed - 1);
*
* if(0 == (node = malloc(sizeof(nmeaParserNODE))))
* goto mem_fail;
*
* node->pack = 0;
*
* switch(ptype)
* {
* case GPGGA:
* if(0 == (node->pack = malloc(sizeof(nmeaGPGGA))))
* goto mem_fail;
* node->packType = GPGGA;
* if(!nmea_parse_GPGGA(
* (const char *)parser->buffer + nparsed,
* sen_sz, (nmeaGPGGA *)node->pack))
* {
* free(node);
* node = 0;
* }
* break;
* case GPGSA:
* if(0 == (node->pack = malloc(sizeof(nmeaGPGSA))))
* goto mem_fail;
* node->packType = GPGSA;
* if(!nmea_parse_GPGSA(
* (const char *)parser->buffer + nparsed,
* sen_sz, (nmeaGPGSA *)node->pack))
* {
* free(node);
* node = 0;
* }
* break;
* ...
* \endcode
*/
#include "tok.h"
#include "parse.h"
#include "context.h"
#include "gmath.h"
#include "units.h"
#include
#include
int _nmea_parse_time( const char *buff, int buff_sz, nmeaTIME *res )
{
int success = 0;
switch ( buff_sz )
{
case sizeof( "hhmmss" ) - 1:
success = ( 3 == nmea_scanf( buff, buff_sz,
"%2d%2d%2d", &( res->hour ), &( res->min ), &( res->sec )
) );
break;
case sizeof( "hhmmss.s" ) - 1:
success = ( 4 == nmea_scanf( buff, buff_sz,
"%2d%2d%2d.%d", &( res->hour ), &( res->min ), &( res->sec ), &( res->msec )
) );
res->msec = res->msec * 100; // tenths sec * 100 = thousandths
break;
case sizeof( "hhmmss.ss" ) - 1:
success = ( 4 == nmea_scanf( buff, buff_sz,
"%2d%2d%2d.%d", &( res->hour ), &( res->min ), &( res->sec ), &( res->msec )
) );
res->msec = res->msec * 10; // hundredths sec * 10 = thousandths
break;
case sizeof( "hhmmss.sss" ) - 1:
success = ( 4 == nmea_scanf( buff, buff_sz,
"%2d%2d%2d.%d", &( res->hour ), &( res->min ), &( res->sec ), &( res->msec )
) );
// already thousandths
break;
default:
nmea_error( "Parse of time error (format error)!" );
success = 0;
break;
}
return ( success ? 0 : -1 );
}
/**
* \brief Define packet type by header (nmeaPACKTYPE).
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @return The defined packet type
* @see nmeaPACKTYPE
*/
int nmea_pack_type( const char *buff, int buff_sz )
{
static const char *P_HEADS[] =
{
"GPGGA",
"GPGSA",
"GPGSV",
"GPRMC",
"GPVTG",
"HCHDG",
"HCHDT",
"GNRMC",
"GPGST",
};
// BUFFER_SIZE = size(P_HEADS) - 1;
int buffer_size = 8;
NMEA_ASSERT( buff );
if ( buff_sz < buffer_size )
return GPNON;
else if ( 0 == memcmp( buff, P_HEADS[0], buffer_size ) )
return GPGGA;
else if ( 0 == memcmp( buff, P_HEADS[1], buffer_size ) )
return GPGSA;
else if ( 0 == memcmp( buff, P_HEADS[2], buffer_size ) )
return GPGSV;
else if ( 0 == memcmp( buff, P_HEADS[3], buffer_size ) )
return GPRMC;
else if ( 0 == memcmp( buff, P_HEADS[4], buffer_size ) )
return GPVTG;
else if ( 0 == memcmp( buff, P_HEADS[5], buffer_size ) )
return HCHDG;
else if ( 0 == memcmp( buff, P_HEADS[6], buffer_size ) )
return HCHDT;
else if ( 0 == memcmp( buff, P_HEADS[7], buffer_size ) )
return GPRMC;
else if ( 0 == memcmp( buff, P_HEADS[8], buffer_size ) )
return GPGST;
return GPNON;
}
/**
* \brief Find tail of packet ("\r\n") in buffer and check control sum (CRC).
* @param buff a constant character pointer of packets buffer.
* @param buff_sz buffer size.
* @param res_crc a integer pointer for return CRC of packet (must be defined).
* @return Number of bytes to packet tail.
*/
int nmea_find_tail( const char *buff, int buff_sz, int *res_crc )
{
static const int tail_sz = 3 /* *[CRC] */ + 2 /* \r\n */;
const char *end_buff = buff + buff_sz;
int nread = 0;
int crc = 0;
NMEA_ASSERT( buff && res_crc );
*res_crc = -1;
for ( ; buff < end_buff; ++buff, ++nread )
{
if ( ( '$' == *buff ) && nread )
{
buff = 0;
break;
}
else if ( '*' == *buff )
{
if ( buff + tail_sz <= end_buff && '\r' == buff[3] && '\n' == buff[4] )
{
*res_crc = nmea_atoi( buff + 1, 2, 16 );
nread = buff_sz - ( int )( end_buff - ( buff + tail_sz ) );
if ( *res_crc != crc )
{
*res_crc = -1;
buff = 0;
}
}
break;
}
else if ( nread )
crc ^= ( int ) * buff;
}
if ( *res_crc < 0 && buff )
nread = 0;
return nread;
}
/**
* \brief Parse GGA packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_GPGGA( const char *buff, int buff_sz, nmeaGPGGA *pack )
{
char time_buff[NMEA_TIMEPARSE_BUF];
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaGPGGA ) );
nmea_trace_buff( buff, buff_sz );
char type;
if ( 15 != nmea_scanf( buff, buff_sz,
"$G%CGGA,%s,%f,%C,%f,%C,%d,%d,%f,%f,%C,%f,%C,%f,%d*",
&( type ),
&( time_buff[0] ),
&( pack->lat ), &( pack->ns ), &( pack->lon ), &( pack->ew ),
&( pack->sig ), &( pack->satinuse ), &( pack->HDOP ), &( pack->elv ), &( pack->elv_units ),
&( pack->diff ), &( pack->diff_units ), &( pack->dgps_age ), &( pack->dgps_sid ) ) )
{
nmea_error( "G?GGA parse error!" );
return 0;
}
if ( type != 'P' && type != 'N' )
{
nmea_error( "G?GGA invalid type " );
return 0;
}
if ( 0 != _nmea_parse_time( &time_buff[0], ( int )strlen( &time_buff[0] ), &( pack->utc ) ) )
{
nmea_error( "GPGGA time parse error!" );
return 0;
}
return 1;
}
/**
* \brief Parse GST packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_GPGST( const char *buff, int buff_sz, nmeaGPGST *pack )
{
char time_buff[NMEA_TIMEPARSE_BUF];
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaGPGST ) );
nmea_trace_buff( buff, buff_sz );
char type;
if ( 9 != nmea_scanf( buff, buff_sz,
"$G%CGST,%s,%f,%f,%f,%f,%f,%f,%f*",
&( type ),
&( time_buff[0] ),
&( pack->rms_pr ), &( pack->err_major ), &( pack->err_minor ), &( pack->err_ori ),
&( pack->sig_lat ), &( pack->sig_lon ), &( pack->sig_alt ) ) )
{
nmea_error( "G?GST parse error!" );
return 0;
}
if ( type != 'P' && type != 'N' )
{
nmea_error( "G?GST invalid type " );
return 0;
}
if ( 0 != _nmea_parse_time( &time_buff[0], ( int )strlen( &time_buff[0] ), &( pack->utc ) ) )
{
nmea_error( "G?GST time parse error!" );
return 0;
}
return 1;
}
/**
* \brief Parse GSA packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_GPGSA( const char *buff, int buff_sz, nmeaGPGSA *pack )
{
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaGPGSA ) );
nmea_trace_buff( buff, buff_sz );
if ( 18 != nmea_scanf( buff, buff_sz,
"$G%CGSA,%C,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%f,%f,%f*",
&( pack->pack_type ), &( pack->fix_mode ), &( pack->fix_type ),
&( pack->sat_prn[0] ), &( pack->sat_prn[1] ), &( pack->sat_prn[2] ), &( pack->sat_prn[3] ), &( pack->sat_prn[4] ), &( pack->sat_prn[5] ),
&( pack->sat_prn[6] ), &( pack->sat_prn[7] ), &( pack->sat_prn[8] ), &( pack->sat_prn[9] ), &( pack->sat_prn[10] ), &( pack->sat_prn[11] ),
&( pack->PDOP ), &( pack->HDOP ), &( pack->VDOP ) ) )
{
nmea_error( "G?GSA parse error!" );
return 0;
}
if ( pack->pack_type != 'P' && pack->pack_type != 'N' && pack->pack_type != 'L' )
{
nmea_error( "G?GSA invalid type " );
return 0;
}
return 1;
}
/**
* \brief Parse GSV packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_GPGSV( const char *buff, int buff_sz, nmeaGPGSV *pack )
{
int nsen, nsat;
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaGPGSV ) );
nmea_trace_buff( buff, buff_sz );
nsen = nmea_scanf( buff, buff_sz,
"$G%CGSV,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d,"
"%d,%d,%d,%d*",
&( pack->pack_type ),
&( pack->pack_count ), &( pack->pack_index ), &( pack->sat_count ),
&( pack->sat_data[0].id ), &( pack->sat_data[0].elv ), &( pack->sat_data[0].azimuth ), &( pack->sat_data[0].sig ),
&( pack->sat_data[1].id ), &( pack->sat_data[1].elv ), &( pack->sat_data[1].azimuth ), &( pack->sat_data[1].sig ),
&( pack->sat_data[2].id ), &( pack->sat_data[2].elv ), &( pack->sat_data[2].azimuth ), &( pack->sat_data[2].sig ),
&( pack->sat_data[3].id ), &( pack->sat_data[3].elv ), &( pack->sat_data[3].azimuth ), &( pack->sat_data[3].sig ) );
nsat = ( pack->pack_index - 1 ) * NMEA_SATINPACK;
nsat = ( nsat + NMEA_SATINPACK > pack->sat_count ) ? pack->sat_count - nsat : NMEA_SATINPACK;
nsat = nsat * 4 + 3 /* first three sentence`s */;
if ( nsen - 1 < nsat || nsen - 1 > ( NMEA_SATINPACK * 4 + 3 ) )
{
nmea_error( "G?GSV parse error!" );
return 0;
}
if ( pack->pack_type != 'P' && pack->pack_type != 'N' && pack->pack_type != 'L' && pack->pack_type != 'A' && pack->pack_type != 'B' && pack->pack_type != 'Q' )
{
nmea_error( "G?GSV invalid type " );
return 0;
}
return 1;
}
/**
* \brief Parse RMC packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_GPRMC( const char *buff, int buff_sz, nmeaGPRMC *pack )
{
int nsen;
char type;
char time_buff[NMEA_TIMEPARSE_BUF];
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaGPRMC ) );
nmea_trace_buff( buff, buff_sz );
nsen = nmea_scanf( buff, buff_sz,
"$G%CRMC,%s,%C,%f,%C,%f,%C,%f,%f,%2d%2d%2d,%f,%C,%C,%C*",
&( type ), &( time_buff[0] ),
&( pack->status ), &( pack->lat ), &( pack->ns ), &( pack->lon ), &( pack->ew ),
&( pack->speed ), &( pack->direction ),
&( pack->utc.day ), &( pack->utc.mon ), &( pack->utc.year ),
&( pack->declination ), &( pack->declin_ew ), &( pack->mode ), &( pack->navstatus ) );
if ( nsen < 14 || nsen > 16 )
{
nmea_error( "G?RMC parse error!" );
return 0;
}
if ( type != 'P' && type != 'N' )
{
nmea_error( "G?RMC invalid type " );
return 0;
}
if ( 0 != _nmea_parse_time( &time_buff[0], ( int )strlen( &time_buff[0] ), &( pack->utc ) ) )
{
nmea_error( "GPRMC time parse error!" );
return 0;
}
if ( pack->utc.year < 90 )
pack->utc.year += 100;
pack->utc.mon -= 1;
return 1;
}
/**
* \brief Parse HDT packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_GPHDT( const char *buff, int buff_sz, nmeaGPHDT *pack )
{
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaGPHDT ) );
nmea_trace_buff( buff, buff_sz );
char type;
char talker_id;
if ( 3 != nmea_scanf( buff, buff_sz,
"$G%CHDT,%f,%C*",
&( talker_id ),
&( pack->heading ), &( type ) ) )
{
nmea_error( "G?HDT parse error!" );
return 0;
}
if ( talker_id != 'P' && talker_id != 'N' )
{
nmea_error( "G?HDT invalid type " );
return 0;
}
if ( type != 'T' )
{
nmea_error( "G?HDT invalid type " );
return 0;
}
return 1;
}
/**
* \brief Parse VTG packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_GPVTG( const char *buff, int buff_sz, nmeaGPVTG *pack )
{
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaGPVTG ) );
nmea_trace_buff( buff, buff_sz );
char type;
if ( 9 != nmea_scanf( buff, buff_sz,
"$G%CVTG,%f,%C,%f,%C,%f,%C,%f,%C*",
&type,
&( pack->dir ), &( pack->dir_t ),
&( pack->dec ), &( pack->dec_m ),
&( pack->spn ), &( pack->spn_n ),
&( pack->spk ), &( pack->spk_k ) ) )
{
nmea_error( "G?VTG parse error!" );
return 0;
}
if ( type != 'P' && type != 'N' )
{
nmea_error( "G?VTG invalid type " );
return 0;
}
if ( pack->dir_t != 'T' ||
pack->dec_m != 'M' ||
pack->spn_n != 'N' ||
pack->spk_k != 'K' )
{
nmea_error( "G?VTG parse error (format error)!" );
return 0;
}
return 1;
}
/**
* \brief Parse HCHDG packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_HCHDG( const char *buff, int buff_sz, nmeaHCHDG *pack )
{
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaHCHDG ) );
nmea_trace_buff( buff, buff_sz );
if ( 5 != nmea_scanf( buff, buff_sz,
"$HCHDG,%f,%f,%C,%f,%C*",
&( pack->mag_heading ), &( pack->mag_deviation ),
&( pack->ew_deviation ), &( pack->mag_variation ),
&( pack->ew_variation ) ) )
{
nmea_error( "HCHDG parse error!" );
return 0;
}
if ( pack->ew_deviation != 'E' && pack->ew_deviation != 'W' )
{
nmea_error( "HCHDG invalid deviation direction" );
return 0;
}
if ( pack->ew_variation != 'E' && pack->ew_variation != 'W' )
{
nmea_error( "HCHDG invalid variation direction" );
return 0;
}
return 1;
}
/**
* \brief Parse HDT packet from buffer.
* @param buff a constant character pointer of packet buffer.
* @param buff_sz buffer size.
* @param pack a pointer of packet which will filled by function.
* @return 1 (true) - if parsed successfully or 0 (false) - if fail.
*/
int nmea_parse_HCHDT( const char *buff, int buff_sz, nmeaHCHDT *pack )
{
NMEA_ASSERT( buff && pack );
memset( pack, 0, sizeof( nmeaHCHDT ) );
nmea_trace_buff( buff, buff_sz );
if ( 2 != nmea_scanf( buff, buff_sz,
"$HCHDT,%f,%C*",
&( pack->direction ), &( pack->t_flag ) ) )
{
nmea_error( "HCHDT parse error!" );
return 0;
}
return 1;
}
/**
* \brief Fill nmeaINFO structure by GGA packet data.
* @param pack a pointer of packet structure.
* @param info a pointer of summary information structure.
*/
void nmea_GPGGA2info( nmeaGPGGA *pack, nmeaINFO *info )
{
NMEA_ASSERT( pack && info );
info->utc.hour = pack->utc.hour;
info->utc.min = pack->utc.min;
info->utc.sec = pack->utc.sec;
info->utc.msec = pack->utc.msec;
info->sig = pack->sig;
info->HDOP = pack->HDOP;
info->elv = pack->elv;
info->lat = ( ( pack->ns == 'N' ) ? pack->lat : -( pack->lat ) );
info->lon = ( ( pack->ew == 'E' ) ? pack->lon : -( pack->lon ) );
info->smask |= GPGGA;
}
/**
* \brief Fill nmeaINFO structure by GST packet data.
* @param pack a pointer of packet structure.
* @param info a pointer of summary information structure.
*/
void nmea_GPGST2info( nmeaGPGST *pack, nmeaINFO *info )
{
NMEA_ASSERT( pack && info );
info->utc.hour = pack->utc.hour;
info->utc.min = pack->utc.min;
info->utc.sec = pack->utc.sec;
info->utc.msec = pack->utc.msec;
info->rms_pr = pack->rms_pr;
info->err_major = pack->err_major;
info->err_minor = pack->err_minor;
info->err_ori = pack->err_ori;
info->sig_lat = pack->sig_lat;
info->sig_lon = pack->sig_lon;
info->sig_alt = pack->sig_alt;
info->smask |= GPGST;
}
/**
* \brief Fill nmeaINFO structure by GSA packet data.
* @param pack a pointer of packet structure.
* @param info a pointer of summary information structure.
*/
void nmea_GPGSA2info( nmeaGPGSA *pack, nmeaINFO *info )
{
int i, j, nuse = 0;
NMEA_ASSERT( pack && info );
info->fix = pack->fix_type;
info->PDOP = pack->PDOP;
info->HDOP = pack->HDOP;
info->VDOP = pack->VDOP;
for ( i = 0; i < NMEA_MAXSAT; ++i )
{
for ( j = 0; j < info->satinfo.inview; ++j )
{
if ( pack->sat_prn[i] && pack->sat_prn[i] == info->satinfo.sat[j].id )
{
info->satinfo.sat[j].in_use = 1;
nuse++;
}
}
}
info->satinfo.inuse = nuse;
info->smask |= GPGSA;
}
/**
* \brief Fill nmeaINFO structure by GSV packet data.
* @param pack a pointer of packet structure.
* @param info a pointer of summary information structure.
*/
void nmea_GPGSV2info( nmeaGPGSV *pack, nmeaINFO *info )
{
int isat, isi, nsat;
NMEA_ASSERT( pack && info );
if ( pack->pack_index > pack->pack_count ||
pack->pack_index * NMEA_SATINPACK > NMEA_MAXSAT )
return;
if ( pack->pack_index < 1 )
pack->pack_index = 1;
info->satinfo.inview = pack->sat_count;
nsat = ( pack->pack_index - 1 ) * NMEA_SATINPACK;
nsat = ( nsat + NMEA_SATINPACK > pack->sat_count ) ? pack->sat_count - nsat : NMEA_SATINPACK;
for ( isat = 0; isat < nsat; ++isat )
{
isi = ( pack->pack_index - 1 ) * NMEA_SATINPACK + isat;
info->satinfo.sat[isi].id = pack->sat_data[isat].id;
info->satinfo.sat[isi].elv = pack->sat_data[isat].elv;
info->satinfo.sat[isi].azimuth = pack->sat_data[isat].azimuth;
info->satinfo.sat[isi].sig = pack->sat_data[isat].sig;
}
info->smask |= GPGSV;
}
/**
* \brief Fill nmeaINFO structure by RMC packet data.
* @param pack a pointer of packet structure.
* @param info a pointer of summary information structure.
*/
void nmea_GPRMC2info( nmeaGPRMC *pack, nmeaINFO *info )
{
NMEA_ASSERT( pack && info );
if ( 'A' == pack->status )
{
if ( NMEA_SIG_BAD == info->sig )
info->sig = NMEA_SIG_MID;
if ( NMEA_FIX_BAD == info->fix )
info->fix = NMEA_FIX_2D;
}
else if ( 'V' == pack->status )
{
info->sig = NMEA_SIG_BAD;
info->fix = NMEA_FIX_BAD;
}
info->utc = pack->utc;
info->lat = ( ( pack->ns == 'N' ) ? pack->lat : -( pack->lat ) );
info->lon = ( ( pack->ew == 'E' ) ? pack->lon : -( pack->lon ) );
info->speed = pack->speed * NMEA_TUD_KNOTS;
info->direction = pack->direction;
info->smask |= GPRMC;
}
/**
* \brief Fill nmeaINFO structure by VTG packet data.
* @param pack a pointer of packet structure.
* @param info a pointer of summary information structure.
*/
void nmea_GPVTG2info( nmeaGPVTG *pack, nmeaINFO *info )
{
NMEA_ASSERT( pack && info );
info->direction = pack->dir;
info->declination = pack->dec;
info->speed = pack->spk;
info->smask |= GPVTG;
}