This commit is contained in:
Mark Qvist 2014-04-03 22:21:37 +02:00
commit c898b090dd
1049 changed files with 288572 additions and 0 deletions

31
bertos/net/nmeap/COPYING Normal file
View file

@ -0,0 +1,31 @@
/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.
This product is licensed for use and distribution under the BSD Open Source License:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

1153
bertos/net/nmeap/Doxyfile Normal file

File diff suppressed because it is too large Load diff

23
bertos/net/nmeap/Makefile Normal file
View file

@ -0,0 +1,23 @@
# rules
export CC=gcc
export CDEFS = -DNDEBUG
# directories
BASE :=$(shell pwd)
export SRC=$(BASE)/src
export TST=$(BASE)/tst
export INC=$(BASE)/inc
export LIB=$(BASE)/lib
all :
cd $(SRC) && $(MAKE) all
cd $(TST) && $(MAKE) all
clean :
cd $(SRC) && $(MAKE) clean
cd $(TST) && $(MAKE) clean
doc :
doxygen

24
bertos/net/nmeap/README Normal file
View file

@ -0,0 +1,24 @@
NMEAP is licensed under the BSD Open Source License. See the file COPYING for terms of the license
VERSION 0.2 - bug fixes and tutorial
a. fixed a bug in test3.c
b. added a tutorial in doc/tutorial.html
Installation:
Unpack the tarball or zip file into the desired working directory.
Building:
Under Linux, execute 'make' from the top level directory.
Under Win32, execute 'nmake -f win32.mak' from the top level directory
Using:
This library is statically linked to the application. Just include it in
your linker command line. See the file 'nmeap.h' and the examples in the
'tst' directory for usage instructions.

View file

@ -0,0 +1,151 @@
<html>
<body>
<h1>NMEAP TUTORIAL AND REFERENCE</h1>
<hr />
<pre>
copyright (c) 2005 David M. Howard
This work is licensed under the Creative Commons Attribution License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by/2.0/ or send a letter to
Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305,USA
You are free:
* to copy, distribute, display, and perform the work
* to make derivative works
* to make commercial use of the work
Under the following conditions:
Attribution. You must give the original author credit.
* For any reuse or distribution, you must make clear to others the
license terms of this work.
* Any of these conditions can be waived if you get permission from
the author.
</pre>
<hr />
<h2>Table Of Contents</h2>
<ol>
<li><a href="#c1">Installing the Source Code</a></li>
<li><a href="#c2">Building the Library</a></li>
<li><a href="#c3">Description and Examples</a></li>
<li><a href="#c4">API Documentation</a></li>
</ol>
<a name="c1">&nbsp;</a>
</a><h2>1. Installing the source code</h2>
Get the source code from <a href="http://sourceforge.net/projects/nmeap/">NMEAP at sourceforge.net</a>
<pre>
Linux:
expand the tarball to a directory of your choice.
>tar --gzip -xf [tarball name]
>cd [nmeap...]
Windows:
use Winzip to unzip the zipfile to a directory of your choice
</pre>
<a name="c2">&nbsp;</a>
<h2>2. Building the library </h2>
<pre>
Linux:
>cd [working directory]
>make
This builds a static library named libnmeap.a in the 'lib' directory. there is no option for a dynamic library.
This thing is so small that it isn't worth the trouble. It also builds the test/examples programs in
'tst'.
Windows:
>cd [working directory]
>nmake -f win32.mak
Again, this builds a static library names libnmeap.lib in the 'lib' direcotry, plus the test programs in 'tst'
</pre>
<a name="c3">&nbsp;</a>
<h2>3. Description and Examples</h2>
<p>The NMEA-0183 standard specifies how the output is formatted for GPS data output, usually on a serial port. The data
consists of 'sentences' delimited by CR/LF end of line markers. A lot has been written about the format, so this document
won't cover the specifics. A good place to start is the <a href="http://vancouver-webpages.com/peter/nmeafaq.txt">NMEA FAQ</a>
maintained by Peter Bennett.</p>
<p>NMEAP is an extensible C language parser library that takes NMEA sentences as input and spits out the decoded data as output. You link
NMEAP in to your application, set it up, initialize it and feed it bytes. It signals you when it has found a complete valid sentence and
provides the parsed out data to you. Parsing NMEA-0183 is pretty easy but it has a few tricky bits. The value of NMEAP is not that it is
rocket science to write an NMEA parser, but that it provides a relatively efficient implementation that works, along with an
extension framework to add more sentence parsers without hacking the base source code.</p>
<p>An NMEA 'sentence' has the following format:</p>
<pre>
$name,data1,data2,...,dataN*XX[CR/LF]
OR
$name,data1,data2,...,dataN[CR/LF]
where
header := a 5 digit sentence identifier. all ASCII upper case. e.g. GPGGA
data1..dataN := some number of data elements, all ASCII numbers or letters, in all kinds of weird formats.
fields can be empty, in which case 2 commas will be side by side.
normally data fields are identified by their position in the sentence.
*XX := a '*' plus two ASCII hex digits of checksum. this field is optional.
[CR/LF] := end of line is terminated by a carriage return/linefeed pair.
example from the <a href="http://vancouver-webpages.com/peter/nmeafaq.txt">NMEA FAQ</a>:
$GPGGA,123519,4807.038,N,01131.324,E,1,08,0.9,545.4,M,46.9,M,,*42
</pre>
<p>The NMEAP parser works as follows:
<ol>
<li>the application sets up the parser and specifies which sentences are to be parsed
and what is to be done with the output data from the parser.</li>
<li>the application reads raw bytes from its IO device and passes the bytes to the parser,
either byte by byte or as a buffer/length pair.</li>
<li>nmeap:
<ul>
<li>runs the input bytes through a lexical scanner that recognizes complete and valid sentences</li>
<li>when a sentence is recognized, a second lexical scanner divides the sentence into discrete tokens.</li>
<li>the name field is matched internally to a sentence parser for that name</li>
<li> the sentence parser picks out the data strings and decodes them into an nmeap or user
defined data structure with normal data types such as integer, float, double etc. </li>
<li>notifies the client application that a sentence was found and decoded, either thru a callout
to an event handler (ala Expat) or via a return code and a shared data structure, or both.</li>
</ul>
</li>
</ol>
<h4>Sentence Parsers</h4>
<p>Most of the work in NMEAP is done by the sentence parsers. Each type of NMEA sentence string has an associated parser. NMEAP provides
standard ones, and the user can add more in a systematic way.
The sentence parser is responsible for knowing the token position of the data elements and whatever format they
are in. There are functions in nmeap to decode standard data element formats. If something is nonstandard,
the sentence parser decodes it. Each sentence parser has a 'struct' type associated with it
that the decoded data gets poked into an instance of that data structure, which is provided by the client application when nmeap is set
up.</p>
<h4>Memory Allocation</h4>
<p>All memory allocation is done by the application. Several data items are required. The application can declare them statically or use
malloc or whatever. NMEAP doesn't do any memory allocation on its own. This is an important requirement for portability and especially in
embedded systems where memory allocation needs to be tightly defined and controlled.
</p>
<h4>Threads</h4>
<p>NMEAP as implemented is not meant to be called from multiple threads. It expects to execute within the context of a single thread. The sentence callouts execute
in the context of the thread of the nmeap client thread. Given how nmeap works, it doesn't really make sense to make nmeap thread-safe
because the usage pattern is intrinsically single thread. If one wanted to, one could add some mutex locking within the nmeap function
calls to make it thread safe. In a multithreaded environment, a more likely approach to thread-safety is to put synchronization in the client side of the application,
within the sentence parser callouts or inline handling of sentence data.
</p>
<h4>IO</h4>
<p>NMEAP is IO agnostic. That is a pompous way of saying that NMEAP doesn't do the IO, the client application does it. There are way too
many IO schemes to handle to keep it portable, especially in the embedded world. That said, the example programs contain a Linux and a Win32 specific
program that includes serial IO for those two platforms.
</p>
<h4>Examples</h4>
Look at the code for the following example programs to see the usage patterns. The are all located in the
'tst' directory. There are big, obvious comments delineating the steps to setting up and using NMEAP.
The IO is simulated in the samples. Follow the comments in the code
to see the sequence of operations to setup and run the parser. When you are ready just plug in your own IO.
<ol>
<li>tst/test1.c Setup for standard GGA and RMC sentences with byte by byte IO (easiest to code up)</li>
<li>tst/test2.c Setup for standard GGA and RMC sentences with block IO (more efficient from a system call standpoint)</li>
<li>tst/test3.c Adding a custom parser</li>
<li>tst/wingps.c A console program that reads a serial port and writes the decoded data to standard out for WIN32 applications</li>
</ol>
<a name="c4">&nbsp;</a>
<h3>API Documentation</h3>
The documentation for the actual API is in <a href="www.doxygen.org">Doxygen<a> HTML format and is contained in the 'doc' directory of
the source distribution. Or, all the external data structures, constants and functions are defined in 'inc/nmeap.h'.
<p>END</p>
</body>
</html>

View file

@ -0,0 +1,227 @@
/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.
This product is licensed for use and distribution under the BSD Open Source License.
see the file COPYING for more details.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NMEAP_H__
#define __NMEAP_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "cfg/cfg_nmea.h"
/*
============================================
COMPILE TIME CONFIGURATION CONSTANTS
============================================
*/
/* these constants affect the size of the context object. tweak them as desired but know what you are doing */
/** maximum number of sentence parsers supported */
#define NMEAP_MAX_SENTENCES CONFIG_NMEAP_MAX_SENTENCES
/** length of sentence name. leave this at 5 unless you really know what you are doing */
#define NMEAP_MAX_SENTENCE_NAME_LENGTH 5
/** max length of a complete sentence. the standard says 82 bytes, but its probably better to go at least 128 since
* some units don't adhere to the 82 bytes especially for proprietary sentences */
#define NMEAP_MAX_SENTENCE_LENGTH CONFIG_NMEAP_MAX_SENTENCE_LENGTH
/** max tokens in one sentence. 24 is enough for any standard sentence */
#define NMEAP_MAX_TOKENS CONFIG_NMEAP_MAX_TOKENS
/* predefined message ID's */
/* GGA MESSAGE ID */
#define NMEAP_GPGGA 1
/* RMC MESSAGE ID */
#define NMEAP_GPRMC 2
/** user defined parsers should make ID numbers using NMEAP_USER as the base value, plus some increment */
#define NMEAP_USER 100
/* forward references */
struct nmeap_context;
struct nmeap_sentence;
/*
============================================
CALLOUTS
============================================
*/
/**
* sentence callout function type
* a callout is fired for each registered sentence type
* the callout gets the object context and a pointer to sentence specific data.
* the callout must cast the 'sentence_data' to the appropriate type for that callout
* @param context nmea object context
* @param sentence_data sentence specific data
*/
typedef void (*nmeap_callout_t)(struct nmeap_context *context,void *sentence_data,void *user_data);
/**
* sentence parser function type
* stored in the object context and called internally when the sentence name matches
* the specified value
* the callout gets the object context and a pointer to sentence specific data.
* the callout must cast the 'sentence_data' to the appropriate type for that callout
* @param context nmea object context
* @param sentence_data sentence specific data
* @return id of sentence (each sentence parser knows its own ID)
*/
typedef int (*nmeap_sentence_parser_t)(struct nmeap_context *context,struct nmeap_sentence *sentence);
/* ==== opaque types === */
#include "nmeap_def.h"
/*
============================================
STANDARD SENTENCE DATA STRUCTURES
============================================
*/
/** extracted data from a GGA message */
struct nmeap_gga {
double latitude;
double longitude;
double altitude;
unsigned long time;
int satellites;
int quality;
double hdop;
double geoid;
};
typedef struct nmeap_gga nmeap_gga_t;
/** extracted data from an RMC message */
struct nmeap_rmc {
unsigned long time;
char warn;
double latitude;
double longitude;
double speed;
double course;
unsigned long date;
double magvar;
};
typedef struct nmeap_rmc nmeap_rmc_t;
/*
============================================
METHODS
============================================
*/
/**
* initialize an NMEA parser. call this function to initialize a user allocated context object
* @param context nmea object context. allocated by user statically or dynamically.
* @param user_data pointer to user defined data
* @return 0 if ok, -1 if initialization failed
*/
int nmeap_init(nmeap_context_t *context,void *user_data);
/**
* register an NMEA sentence parser
* @param context nmea object context
* @param sentence_name string matching the sentence name for this parser. e.g. "GPGGA". not including the '$'
* @param sentence_parser parser function for this sentence
* @param sentence_callout callout triggered when this sentence is received and parsed.
* if null, no callout is triggered for this sentence
* @param sentence_data user allocated sentence specific data defined by the application. the parser uses
this data item to store the extracted data. This data object needs to persist over the life
of the parser, so be careful if allocated on the stack.
* @return 0 if registered ok, -1 if registration failed
*/
int nmeap_addParser(nmeap_context_t *context,
const char *sentence_name,
nmeap_sentence_parser_t sentence_parser,
nmeap_callout_t sentence_callout,
void *sentence_data
);
/**
* parse a buffer of nmea data.
* @param context nmea object context
* @param buffer buffer of input characters
* @param length [in,out] pointer to length of buffer. on return, contains number of characters not used for
* the current sentence
* @return -1 if error, 0 if the data did not complete a sentence, sentence code if a sentence was found in the stream
*/
int nmeap_parseBuffer(nmeap_context_t *context,const char *buffer,int *length);
/**
* parse one character of nmea data.
* @param context nmea object context
* @param ch input character
* @return -1 if error, 0 if the data did not complete a sentence, sentence code if a sentence was found in the stream
*/
int nmeap_parse(nmeap_context_t *context,char ch);
/**
* built-in parser for GGA sentences.
* @param context nmea object context
* @param sentence sentence object for this parser
*/
int nmeap_gpgga(nmeap_context_t *context,nmeap_sentence_t *sentence);
/**
* built-in parser for RMC sentences.
* @param context nmea object context
* @param sentence sentence object for this parser
*/
int nmeap_gprmc(nmeap_context_t *context,nmeap_sentence_t *sentence);
/**
* extract latitude from 2 tokens in ddmm.mmmm,h format.
* @param plat pointer to token with numerical latitude
* @param phem pointer to token with hemisphere
* @return latitude in degrees and fractional degrees
*/
double nmeap_latitude(const char *plat,const char *phem);
/**
* extract longitude from 2 tokens in ddmm.mmmm,h format.
* @param plat pointer to token with numerical longitude
* @param phem pointer to token with hemisphere
* @return longitude in degrees and fractional degrees
*/
double nmeap_longitude(const char *plat,const char *phem);
/**
* extract altitude from 2 tokens in xx.x format.
* @param palt pointer to token with numerical altitude
* @param punits pointer to token with measure unint
* @return altitude in meter or feet
*/
double nmeap_altitude(const char *palt,const char *punits);
#ifdef __cplusplus
} // extern C
#endif
#endif

View file

@ -0,0 +1,72 @@
/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.
This product is licensed for use and distribution under the BSD Open Source License.
see the file COPYING for more details.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NMEAP_DEF_H__
#define __NMEAP_DEF_H__
/**
* context for a single sentence
*/
typedef struct nmeap_sentence {
char name[NMEAP_MAX_SENTENCE_NAME_LENGTH + 1];
int id;
nmeap_sentence_parser_t parser;
nmeap_callout_t callout;
void *data;
} nmeap_sentence_t;
/**
* parser context
*/
struct nmeap_context {
/** support up to 8 sentences */
nmeap_sentence_t sentence[NMEAP_MAX_SENTENCES]; /* sentence descriptors */
int sentence_count; /* number of initialized descriptors */
/** sentence input buffer */
char input[NMEAP_MAX_SENTENCE_LENGTH + 1]; /* input line buffer */
int input_count; /* index into 'input */
int input_state; /* current lexical scanner state */
char input_name[6]; /* sentence name */
char icks; /* input checksum */
char ccks; /* computed checksum */
/* tokenization */
char *token[NMEAP_MAX_TOKENS]; /* list of delimited tokens */
int tokens; /* list of tokens */
/** errors and debug. optimize these as desired */
unsigned long msgs; /* count of good messages */
unsigned long err_hdr; /* header error */
unsigned long err_ovr; /* overrun error */
unsigned long err_unk; /* unknown error */
unsigned long err_id; /* bad character in id */
unsigned long err_cks; /* bad checksum */
unsigned long err_crl; /* expecting cr or lf, got something else */
char debug_input[NMEAP_MAX_SENTENCE_LENGTH + 1]; /* input line buffer for debug */
/** opaque user data */
void *user_data;
};
typedef struct nmeap_context nmeap_context_t;
#endif /* __NMEAP_DEF_H__ */

View file

@ -0,0 +1,31 @@
# specify compiler flags
CFLAGS = -I $(INC) $(CDEFS) -g -O0 -Werror -Wall
# set library name
LIBNAME = libnmeap.a
COBJ = nmeap01.o
INCLUDES= $(INC)/nmeap.h $(INC)/nmeap_def.h
# build everything
all : $(LIB)/$(LIBNAME)
# build the library
$(LIB)/$(LIBNAME) : $(COBJ)
-$(RM) $(LIB)/$(LIBNAME)
$(AR) -q $(LIB)/$(LIBNAME) $(COBJ)
# build all c files into .o files
$(COBJ): %.o: %.c
$(CC) -c $(CFLAGS) $(SRC)/$< -o $@
# erase all intermediate and output files
clean :
-$(RM) *.o
-$(RM) *~
-$(RM) $(LIB)/$(LIBNAME)
# include file dependencies
$(COBJ) : $(INCLUDES)

View file

@ -0,0 +1,20 @@
INCLUDES= ..\inc\nmeap.h ..\inc\nmeap_def.h
CSRC = nmeap01.c
LIBNAME = ..\lib\libnmeap.lib
# build everything
all : $(LIBNAME)
$(LIBNAME) : nmeap01.obj
-erase $(LIBNAME)
lib /OUT:$(LIBNAME) nmeap01.obj
nmeap01.obj : nmeap01.c $(INCLUDES)
cl /DNDEBUG /c /I..\inc nmeap01.c
# erase all intermediate and output files
clean :
-erase *.obj
-erase $(LIBNAME)

View file

@ -0,0 +1,634 @@
/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.
This product is licensed for use and distribution under the BSD Open Source License.
see the file COPYING for more details.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* nmeap01.c
* nmeap gps data parser
*
* see the file COPYING for terms of the licnese
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "../inc/nmeap.h"
#include <cfg/debug.h>
#define assert(x) ASSERT(x)
#include "cfg/cfg_nmea.h"
#define LOG_LEVEL NMEA_LOG_LEVEL
#define LOG_FORMAT NMEA_LOG_FORMAT
#include <cfg/log.h>
#ifdef _DEBUG
#undef NDEBUG
#define printf(str,...) LOG_INFO(str, ## __VA_ARGS__)
#endif
/* this only works if you are sure you have an upper case hex digit */
#define HEXTOBIN(ch) ((ch <= '9') ? ch - '0' : ch - ('A' - 10))
/* forward references */
int nmeap_init(nmeap_context_t *context,void *user_data);
int nmeap_addParser(nmeap_context_t *context,
const char *sentence_name,
nmeap_sentence_parser_t sentence_parser,
nmeap_callout_t sentence_callout,
void *sentence_data
);
int nmeap_tokenize(nmeap_context_t *context);
int nmeap_process(nmeap_context_t *context);
int nmeap_parse(nmeap_context_t *context,char ch);
int nmeap_parseBuffer(nmeap_context_t *context,const char *buffer,int *length);
/**
* get a latitude out of a pair of nmea tokens
*/
double nmeap_latitude(const char *plat,const char *phem)
{
double lat;
int deg;
double min;
int ns;
assert(plat != 0);
assert(phem != 0);
if (*plat == 0) {
return 0.0;
}
if (*phem == 0) {
return 0.0;
}
/* north lat is +, south lat is - */
if (*phem == 'N') {
ns = 1;
}
else {
ns = -1;
}
/* latitude is degrees, minutes, fractional minutes */
/* no validation is performed on the token. it better be good.*/
/* if it comes back 0.0 then probably the token was bad */
lat = atof(plat);
/* extract the degree part */
deg = (int)(lat / 100.0);
/* mask out the degrees */
min = lat - (deg * 100.0);
/* compute the actual latitude in degrees.decimal-degrees */
lat = (deg + (min / 60.0)) * ns;
return lat;
}
/**
* get a longitude out of a pair of nmea tokens
*/
double nmeap_longitude(const char *plon,const char *phem)
{
double lon;
int deg;
double min;
int ew;
assert(plon != 0);
assert(phem != 0);
if (*plon == 0) {
return 0.0;
}
if (*phem == 0) {
return 0.0;
}
/* west long is negative, east long is positive */
if (*phem == 'E') {
ew = 1;
}
else {
ew = -1;
}
/* longitude is degrees, minutes, fractional minutes */
/* no validation is performed on the token. it better be good.*/
/* if it comes back 0.0 then probably the token was bad */
lon = atof(plon);
/* extract the degree part */
deg = (int)(lon / 100.0);
/* mask out the degrees */
min = lon - (deg * 100.0);
/* compute the actual lonitude in degrees.decimal-degrees */
lon = (deg + (min / 60.0)) * ew;
return lon;
}
/**
* get an altitude longitude out of a pair of nmea tokens
* ALTITUDE is returned in METERS
*/
double nmeap_altitude(const char *palt,const char *punits)
{
double alt;
if (*palt == 0) {
return 0.0;
}
/* convert with no error checking */
alt = atof(palt);
if (*punits == 'M') {
/* already in meters */
}
else if (*punits == 'F') {
/* convert to feet */
alt = alt * 3.2808399;
}
return alt;
}
/**
* initialize an NMEA parser
*/
int nmeap_init(nmeap_context_t *context,void *user_data)
{
assert(context != 0);
memset(context,0,sizeof(*context));
context->user_data = user_data;
return 0;
}
/**
* register an NMEA sentence parser
*/
int nmeap_addParser(nmeap_context_t *context,
const char *sentence_name,
nmeap_sentence_parser_t sentence_parser,
nmeap_callout_t sentence_callout,
void *sentence_data
)
{
nmeap_sentence_t *s = 0;
/* runtime error */
assert(context != 0);
/* sentence capacity overflow */
if (context->sentence_count >= NMEAP_MAX_SENTENCES) {
return -1;
}
/* point at next empty sentence buffer */
s = &context->sentence[context->sentence_count];
/* advance sentence data count */
context->sentence_count++;
/* clear the sentence data */
memset(s,0,sizeof(*s));
/* name */
strncpy(s->name,sentence_name,NMEAP_MAX_SENTENCE_NAME_LENGTH);
/* parser */
s->parser = sentence_parser;
/* callout */
s->callout = sentence_callout;
/* data */
s->data = sentence_data;
return 0;
}
/**
* tokenize a buffer
*/
int nmeap_tokenize(nmeap_context_t *context)
{
char *s;
int tokens;
int state;
/* first token is header. assume it is there */
tokens = 0;
s = context->input;
context->token[tokens] = s;
/* get rest of tokens */
tokens = 1;
state = 0;
while((*s != 0)&&(tokens < NMEAP_MAX_TOKENS)) {
switch(state) {
case 0:
/* looking for end of a token */
if (*s == ',') {
/* delimit at the comma */
*s = 0;
/* new token */
state = 1;
}
break;
case 1:
/* start of next token, might be another comma */
context->token[tokens++] = s;
if (*s == ',') {
/* delimit at the comma */
*s = 0;
}
else {
/* not a comma */
state = 0;
}
break;
default:
state = 0;
break;
}
// next character
s++;
}
return tokens;
}
/**
* process a sentence
*/
int nmeap_process(nmeap_context_t *context)
{
int id = 0;
int i;
nmeap_sentence_t *s;
/* copy the input to a debug buffer */
/* remove debug_input when everything is working. */
strncpy(context->debug_input,context->input,sizeof(context->debug_input));
/* tokenize the input */
context->tokens = nmeap_tokenize(context);
/* try to find a matching sentence parser */
/* this search is O(n). it has a lot of potential for optimization, at the expense of complexity, if you have a lot of sentences */
/* binary search instead of linear (have to keep sentences in sorted order) O(NlogN) */
/* OR, when sentences are added, create a TRIE structure to find the names with a constant time search O(5) */
for(i=0;i<context->sentence_count;i++) {
s = &context->sentence[i];
assert(s != 0);
if (strncmp(context->input_name,s->name,5) == 0) {
/* found a match, call its parser */
id = (*context->sentence[i].parser)(context,s);
if (id > 0) {
break;
}
}
}
return id;
}
/**
+-5-+ +---+
v | v |
+------+ +------+ +------+ +------+ +------+
| 0 |--$--> |1-hdr |--alnum--> |2-data|----\r-->| 6-LF |---\n--->| done |--> 0
+------+ +------+ +------+ +------+ +------+
| ^
* +--------\r-------+
V |
+------+ +------+ +------+
|3-cks |--xdigit-->|4-cks |-xdigit->| 5-CR |
+------+ +------+ +------+
return to start conditions:
1. buffer overflow
2. invalid character for state
checksum calculation
two hex digits represent the XOR of all characters between, but not
including, the "$" and "*". A checksum is required on some
sentences.
*/
int nmeap_parse(nmeap_context_t *context,char ch)
{
int status = 0;
/* check for input buffer overrun first to avoid duplicating code in the
individual states
*/
if ((size_t)context->input_count >= (sizeof(context->input)-1)) {
/* input buffer overrun, restart state machine */
context->input_state = 0;
/* reset input count */
context->input_count = 0;
}
/* store the byte */
context->input[context->input_count] = ch;
/* next buffer position */
context->input_count++;
/* run it through the lexical scanner */
switch(context->input_state) {
/* LOOKING FOR $ */
case 0:
if (ch == '$') {
/*look for id */
context->input_state = 1;
context->ccks = 0;
context->icks = 0;
}
else {
/* header error, start over */
context->err_hdr++;
context->input_state = 0;
context->input_count = 0;
}
break;
/* LOOKING FOR 5 CHARACTER SENTENCE ID */
case 1:
/* allow numbers even though it isn't usually done */
/* a proprietary id might have a numeral */
if (isalnum((unsigned char)ch)) {
/* store name separately */
context->input_name[context->input_count - 2] = ch;
/* checksum */
context->ccks ^= ch;
/* end of header? */
if (context->input_count >= 6) {
/* yes, get body */
context->input_state = 2;
}
}
else {
/* bad character, start over */
context->err_id++;
context->input_state = 0;
context->input_count = 0;
}
break;
/* LOOKING FOR CR OR CHECKSUM INDICATOR */
case 2:
if (ch == '*') {
/* this sentence has a checksum */
context->input_state = 3;
}
else if (ch == '\r') {
/* carriage return, no checksum, force a match */
context->icks = 0;
context->ccks = 0;
context->input_state = 6;
}
else {
/* continue accumulating data */
/* checksum */
context->ccks ^= ch;
}
break;
/* LOOKING FOR FIRST CHECKSUM CHARACTER */
case 3:
/* must be upper case hex digit */
if (isxdigit((unsigned char)ch) && (ch <= 'F')) {
/* got first checksum byte */
context->input_state = 4;
context->icks = HEXTOBIN(ch) << 4;
}
else {
/* input error, restart */
context->err_cks++;
context->input_state = 0;
context->input_count = 0;
}
break;
/* LOOKING FOR SECOND CHECKSUM CHARACTER */
case 4:
/* must be upper case hex digit */
if (isxdigit((unsigned char)ch) && (ch <= 'F')) {
/* got second checksum byte */
context->input_state = 5;
context->icks += HEXTOBIN(ch);
}
else {
/* input error, restart */
context->err_cks++;
context->input_state = 0;
context->input_count = 0;
}
break;
/* LOOKING FOR CR */
case 5:
if (ch == '\r') {
/* carriage return */
context->input_state = 6;
}
else {
/* input error, restart */
context->err_crl++;
context->input_state = 0;
context->input_count = 0;
}
break;
/* LOOKING FOR LINE FEED */
case 6:
if (ch == '\n') {
/* linefeed, line complete */
/* delimit buffer */
context->input[context->input_count] = 0;
/* if the checksums match, process the sentence */
if (context->ccks == context->icks) {
/* process */
status = nmeap_process(context);
/* count good messages */
context->msgs++;
}
else {
/* count checksum errors */
context->err_cks++;
}
/* restart next time */
context->input_state = 0;
context->input_count = 0;
}
else {
/* input error, restart */
context->err_crl++;
context->input_state = 0;
context->input_count = 0;
}
break;
default:
context->err_unk++;
context->input_state = 0;
break;
}
return status;
}
/**
* parse a buffer of nmea data
*/
int nmeap_parseBuffer(nmeap_context_t *context,const char *buffer,int *length)
{
int i;
int status;
int rem;
int tlen;
tlen = *length;
rem = *length;
status = 0;
/* for each byte in the buffer */
for(i=0;i<tlen;i++) {
/* decrement remaining byte count */
rem--;
/* parse the byte */
status = nmeap_parse(context,buffer[i]);
if (status != 0) {
/* message found or error */
break;
}
}
/* return remaining byte count */
*length = rem;
return status;
}
/**
* standard GPGGA sentence parser
*/
int nmeap_gpgga(nmeap_context_t *context,nmeap_sentence_t *sentence)
{
#ifndef NDEBUG
int i;
#endif
/* get pointer to sentence data */
nmeap_gga_t *gga = (nmeap_gga_t *)sentence->data;
/* if there is a data element, extract data from the tokens */
if (gga != 0) {
gga->latitude = nmeap_latitude(context->token[2],context->token[3]);
gga->longitude = nmeap_longitude(context->token[4],context->token[5]);
gga->altitude = nmeap_altitude(context->token[9],context->token[10]);
gga->time = atoi(context->token[1]);
gga->satellites = atoi(context->token[7]);
gga->quality = atoi(context->token[6]);
gga->hdop = atof(context->token[8]);
gga->geoid = nmeap_altitude(context->token[11],context->token[12]);
}
#ifndef NDEBUG
/* print raw input string */
printf("%s",context->debug_input);
/* print some validation data */
printf("%s==%s %02x==%02x\n",context->input_name,sentence->name,context->icks,context->ccks);
/* print the tokens */
for(i=0;i<context->tokens;i++) {
printf("%d:%s\n",i,context->token[i]);
}
#endif
/* if the sentence has a callout, call it */
if (sentence->callout != 0) {
(*sentence->callout)(context,gga,context->user_data);
}
return NMEAP_GPGGA;
}
/**
* standard GPRMCntence parser
*/
int nmeap_gprmc(nmeap_context_t *context,nmeap_sentence_t *sentence)
{
#ifndef NDEBUG
int i;
#endif
/* get pointer to sentence data */
nmeap_rmc_t *rmc = (nmeap_rmc_t *)sentence->data;
/* if there is a data element, use it */
if (rmc != 0) {
/* extract data from the tokens */
rmc->time = atoi(context->token[1]);
rmc->warn = *context->token[2];
rmc->latitude = nmeap_latitude(context->token[3],context->token[4]);
rmc->longitude = nmeap_longitude(context->token[5],context->token[6]);
rmc->speed = atof(context->token[7]);
rmc->course = atof(context->token[8]);
rmc->date = atoi(context->token[9]);
rmc->magvar = atof(context->token[10]);
}
#ifndef NDEBUG
/* print raw input string */
printf("%s",context->debug_input);
/* print some validation data */
printf("%s==%s %02x==%02x\n",context->input_name,sentence->name,context->icks,context->ccks);
/* print the tokens */
for(i=0;i<context->tokens;i++) {
printf("%d:%s\n",i,context->token[i]);
}
#endif
/* if the sentence has a callout, call it */
if (sentence->callout != 0) {
(*sentence->callout)(context,rmc,context->user_data);
}
return NMEAP_GPRMC;
}

View file

@ -0,0 +1,17 @@
all : test1 test2 test3
test1 : $(LIB)/libnmeap.a $(TST)/test1.c
gcc -g -O0 -I $(INC) $(CDEFS) -Wall -Werror -o test1 $(TST)/test1.c $(LIB)/libnmeap.a
test2 : $(LIB)/libnmeap.a $(TST)/test2.c
gcc -g -O0 -I $(INC) $(CDEFS) -Wall -Werror -o test2 $(TST)/test2.c $(LIB)/libnmeap.a
test3 : $(LIB)/libnmeap.a $(TST)/test3.c
gcc -g -O0 -I $(INC) $(CDEFS) -Wall -Werror -o test3 $(TST)/test3.c $(LIB)/libnmeap.a
clean:
-$(RM) test1
-$(RM) test2
-$(RM) test3

View file

@ -0,0 +1,193 @@
/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.
This product is licensed for use and distribution under the BSD Open Source License.
see the file COPYING for more details.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
========================================================================================================
EXAMPLE : SETUP FOR GGA AND RMC SENTENCES WITH CHARACTER BY CHARACTER IO
=======================================================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nmeap.h"
nmeap_gga_t g_gga;
char test_vector[] = {
"$GPGGA,123519,3929.946667,N,11946.086667,E,1,08,0.9,545.4,M,46.9,M,,*4A\r\n" /* good */
"$xyz,1234,asdfadfasdfasdfljsadfkjasdfk\r\n" /* junk */
"$GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*68\r\n" /* good */
"$GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*48\r\n" /* checksum error */
};
char *pvec = test_vector;
/** simulate character by character IO */
int readchar()
{
int ch;
if (*pvec == 0) {
ch = -1;
}
else {
ch = *pvec++;
}
return ch;
}
/** do something with the GGA data */
static void print_gga(nmeap_gga_t *gga)
{
printf("found GPGGA message %.6f %.6f %.0f %lu %d %d %f %f\n",
gga->latitude ,
gga->longitude,
gga->altitude ,
gga->time ,
gga->satellites,
gga->quality ,
gga->hdop ,
gga->geoid
);
}
/** called when a gpgga message is received and parsed */
static void gpgga_callout(nmeap_context_t *context,void *data,void *user_data)
{
nmeap_gga_t *gga = (nmeap_gga_t *)data;
printf("-------------callout\n");
print_gga(gga);
}
/** do something with the RMC data */
static void print_rmc(nmeap_rmc_t *rmc)
{
printf("found GPRMC Message %lu %c %.6f %.6f %f %f %lu %f\n",
rmc->time,
rmc->warn,
rmc->latitude,
rmc->longitude,
rmc->speed,
rmc->course,
rmc->date,
rmc->magvar
);
}
/** called when a gprmc message is received and parsed */
static void gprmc_callout(nmeap_context_t *context,void *data,void *user_data)
{
nmeap_rmc_t *rmc = (nmeap_rmc_t *)data;
printf("-------------callout\n");
print_rmc(rmc);
}
/* ---------------------------------------------------------------------------------------*/
/* STEP 1 : allocate the data structures. be careful if you put them on the stack because */
/* they need to be live for the duration of the parser */
/* ---------------------------------------------------------------------------------------*/
static nmeap_context_t nmea; /* parser context */
static nmeap_gga_t gga; /* this is where the data from GGA messages will show up */
static nmeap_rmc_t rmc; /* this is where the data from RMC messages will show up */
static int user_data; /* user can pass in anything. typically it will be a pointer to some user data */
int main(int argc,char *argv[])
{
int status;
char ch;
/* ---------------------------------------*/
/*STEP 2 : initialize the nmea context */
/* ---------------------------------------*/
status = nmeap_init(&nmea,(void *)&user_data);
if (status != 0) {
printf("nmeap_init %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 3 : add standard GPGGA parser */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"GPGGA",nmeap_gpgga,gpgga_callout,&gga);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 4 : add standard GPRMC parser */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"GPRMC",nmeap_gprmc,gprmc_callout,&rmc);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 5 : process input until done */
/* -------------------------------------- */
for(;;) {
/* ---------------------------------------*/
/*STEP 6 : get a byte at a time */
/* -------------------------------------- */
ch = readchar();
if (ch <= 0) {
break;
}
/* --------------------------------------- */
/*STEP 7 : pass it to the parser */
/* status indicates whether a complete msg */
/* arrived for this byte */
/* NOTE : in addition to the return status */
/* the message callout will be fired when */
/* a complete message is processed */
/* --------------------------------------- */
status = nmeap_parse(&nmea,ch);
/* ---------------------------------------*/
/*STEP 8 : process the return code */
/* -------------------------------------- */
switch(status) {
case NMEAP_GPGGA:
/* GOT A GPGGA MESSAGE */
printf("-------------switch\n");
print_gga(&gga);
printf("-------------\n");
break;
case NMEAP_GPRMC:
/* GOT A GPRMC MESSAGE */
printf("-------------switch\n");
print_rmc(&rmc);
printf("-------------\n");
break;
default:
break;
}
}
return 0;
}

View file

@ -0,0 +1,208 @@
/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.
This product is licensed for use and distribution under the BSD Open Source License.
see the file COPYING for more details.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
========================================================================================================
EXAMPLE : SETUP FOR GGA AND RMC SENTENCES WITH CHARACTER BY CHARACTER IO
=======================================================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nmeap.h"
nmeap_gga_t g_gga;
char test_vector[] = {
"$GPGGA,123519,3929.946667,N,11946.086667,E,1,08,0.9,545.4,M,46.9,M,,*4A\r\n" /* good */
"$xyz,1234,asdfadfasdfasdfljsadfkjasdfk\r\n" /* junk */
"$GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*68\r\n" /* good */
"$GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*48\r\n" /* checksum error */
};
char *pvec = test_vector;
/** simulate block IO */
int readbuffer(char *buffer,int len)
{
int i;
if (*pvec == 0) {
// end of file
return -1;
}
for(i=0;i<len;i++) {
/* quit when no more data */
if (*pvec == 0) {
break;
}
buffer[i] = *pvec++;
}
return i;
}
/** do something with the GGA data */
static void print_gga(nmeap_gga_t *gga)
{
printf("found GPGGA message %.6f %.6f %.0f %lu %d %d %f %f\n",
gga->latitude ,
gga->longitude,
gga->altitude ,
gga->time ,
gga->satellites,
gga->quality ,
gga->hdop ,
gga->geoid
);
}
/** called when a gpgga message is received and parsed */
static void gpgga_callout(nmeap_context_t *context,void *data,void *user_data)
{
nmeap_gga_t *gga = (nmeap_gga_t *)data;
printf("-------------callout\n");
print_gga(gga);
}
/** do something with the RMC data */
static void print_rmc(nmeap_rmc_t *rmc)
{
printf("found GPRMC Message %lu %c %.6f %.6f %f %f %lu %f\n",
rmc->time,
rmc->warn,
rmc->latitude,
rmc->longitude,
rmc->speed,
rmc->course,
rmc->date,
rmc->magvar
);
}
/** called when a gprmc message is received and parsed */
static void gprmc_callout(nmeap_context_t *context,void *data,void *user_data)
{
nmeap_rmc_t *rmc = (nmeap_rmc_t *)data;
printf("-------------callout\n");
print_rmc(rmc);
}
/* ---------------------------------------------------------------------------------------*/
/* STEP 1 : allocate the data structures. be careful if you put them on the stack because */
/* they need to be live for the duration of the parser */
/* ---------------------------------------------------------------------------------------*/
static nmeap_context_t nmea; /* parser context */
static nmeap_gga_t gga; /* this is where the data from GGA messages will show up */
static nmeap_rmc_t rmc; /* this is where the data from RMC messages will show up */
static int user_data; /* user can pass in anything. typically it will be a pointer to some user data */
int main(int argc,char *argv[])
{
int status;
int rem;
int offset;
int len;
char buffer[32];
/* ---------------------------------------*/
/*STEP 2 : initialize the nmea context */
/* ---------------------------------------*/
status = nmeap_init(&nmea,(void *)&user_data);
if (status != 0) {
printf("nmeap_init %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 3 : add standard GPGGA parser */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"GPGGA",nmeap_gpgga,gpgga_callout,&gga);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 4 : add standard GPRMC parser */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"GPRMC",nmeap_gprmc,gprmc_callout,&rmc);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 5 : process input until done */
/* -------------------------------------- */
for(;;) {
/* ---------------------------------------*/
/*STEP 6 : get a buffer of input */
/* -------------------------------------- */
len = rem = readbuffer(buffer,sizeof(buffer));
if (len <= 0) {
break;
}
/* ----------------------------------------------*/
/*STEP 7 : process input until buffer is used up */
/* --------------------------------------------- */
offset = 0;
while(rem > 0) {
/* --------------------------------------- */
/*STEP 8 : pass it to the parser */
/* status indicates whether a complete msg */
/* arrived for this byte */
/* NOTE : in addition to the return status */
/* the message callout will be fired when */
/* a complete message is processed */
/* --------------------------------------- */
status = nmeap_parseBuffer(&nmea,&buffer[offset],&rem);
offset += (len - rem);
/* ---------------------------------------*/
/*STEP 9 : process the return code */
/* -------------------------------------- */
switch(status) {
case NMEAP_GPGGA:
printf("-------------switch\n");
print_gga(&gga);
printf("-------------\n");
break;
case NMEAP_GPRMC:
printf("-------------switch\n");
print_rmc(&rmc);
printf("-------------\n");
break;
default:
break;
}
}
}
return 0;
}

View file

@ -0,0 +1,306 @@
/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.
This product is licensed for use and distribution under the BSD Open Source License.
see the file COPYING for more details.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
========================================================================================================
EXAMPLE : SETUP FOR GGA AND RMC SENTENCES + A CUSTOM SENTENCE PARSER WITH CHARACTER BY CHARACTER IO
=======================================================================================================
*/
/*
$PGRMF
GARMIN PROPRIETARY GPS Position Fix Data
$PGRMF,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15*HH
1 = GPS week number
2 = GPS seconds in current week
3 = UTC date, ddmmyy format
4 = UTC time, hhmmss format
5 = GPS leap second count
6 = Latitude, dddmm.mmmm format
7 = Latitude hemisphere, N or S
8 = Longitude, dddmm.mmmm format
9 = Longitude hemisphere, E or W
10 = Mode (M=Manual, A=Automatic)
11 = Fix type (0=No fix, 1=2D fix, 2=3D fix)
12 = Speed over ground, kilometres / hour
13 = Course over ground, degrees true
14 = PDOP (Position dilution of precision), rounded to nearest integer
15 = TDOP (Time dilution of precision), rounded to nearest integer
HH = Checksum
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "nmeap.h"
nmeap_gga_t g_gga;
char test_vector[] = {
"$GPGGA,123519,3929.946667,N,11946.086667,E,1,08,0.9,545.4,M,46.9,M,,*4A\r\n"
"$GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*68\r\n"
"$PGRMF,1,100,191105,123519,13,3929.946667,N,12311.12,W,A,2,100.1,181.2,3,8*35\r\n"
};
char *pvec = test_vector;
/** simulate character by character IO */
int readchar()
{
int ch;
if (*pvec == 0) {
ch = -1;
}
else {
ch = *pvec++;
}
return ch;
}
/* --------------------------------------------------------------*/
/*STEP 1a : define a data structure to contain the sentence data */
/* ------------------------------------------------------------- */
struct garmin_rmf {
/* field position in sentence */
int week; /* 1 = GPS week number */
int seconds_of_week; /* 2 = GPS seconds in current week */
unsigned long date; /* 3 = UTC date, ddmmyy format */
unsigned long time; /* 4 = UTC time, hhmmss format */
int leap; /* 5 = GPS leap second count */
double lat; /* 6,7 = Latitude, dddmm.mmmm format (north positive) */
double lon; /* 8,9 = Longitude, dddmm.mmmm format (east positive) */
int mode; /* 10 = Mode (M=Manual, A=Automatic) */
int fix; /* 11 = Fix type (0=No fix, 1=2D fix, 2=3D fix) */
double speed; /* 12 = Speed over ground, kilometres / hour */
double course; /* 13 = Course over ground, degrees true */
int pdop; /* 14 = PDOP (Position dilution of precision), rounded to nearest integer */
int tdop; /* 15 = TDOP (Time dilution of precision), rounded to nearest integer */
};
typedef struct garmin_rmf garmin_rmf_t;
/* --------------------------------------------------------------*/
/*STEP 1b : define an id value for the message */
/* ------------------------------------------------------------- */
#define GARMIN_PGRMF (NMEAP_USER + 0)
/* --------------------------------------------------------------*/
/* STEP 1c : write the sentence parser */
/* ------------------------------------------------------------- */
int custom_pgrmf(nmeap_context_t *context,nmeap_sentence_t *sentence)
{
#ifndef NDEBUG
int i;
#endif
/* get pointer to sentence data */
garmin_rmf_t *rmf = (garmin_rmf_t *)sentence->data;
if (rmf != 0) {
/* if the sentence has a data storage element, use it */
/* extract data from the tokens */
rmf->week = atoi(context->token[1]);
rmf->seconds_of_week = atoi(context->token[2]);
rmf->date = (unsigned long)atol(context->token[3]);
rmf->time = (unsigned long)atol(context->token[4]);
rmf->leap = atoi(context->token[5]);
rmf->lat = nmeap_latitude(context->token[6],context->token[7]);
rmf->lon = nmeap_longitude(context->token[8],context->token[9]);
rmf->mode = atoi(context->token[10]);
rmf->fix = atoi(context->token[11]);
rmf->speed = atof(context->token[12]);
rmf->course = atof(context->token[13]);
rmf->pdop = atoi(context->token[14]);
rmf->tdop = atoi(context->token[15]);
}
/* else there was no data element to store into */
#ifndef NDEBUG
/* print raw input string */
printf("%s",context->debug_input);
/* print some validation data */
printf("%s==%s %02x==%02x\n",context->input_name,sentence->name,context->icks,context->ccks);
/* print the tokens */
for(i=0;i<context->tokens;i++) {
printf("%d:%s\n",i,context->token[i]);
}
#endif
/* if the sentence has a callout, call it */
if (sentence->callout != 0) {
(*sentence->callout)(context,rmf,context->user_data);
}
return GARMIN_PGRMF;
}
/* -------------------------------------------------------------*/
/*STEP 2 : write a function to do something with the data */
/* ------------------------------------------------------------ */
static void print_pgrmf(garmin_rmf_t *rmf)
{
assert(rmf != 0);
printf(" w sec date time lp lat lon m f spd crs p t\n");
printf("found PGRMF message %d %d %lu %lu %d %.6f %.6f %d %d %.2f %.2f %d %d\n",
rmf->week,
rmf->seconds_of_week,
rmf->date,
rmf->time,
rmf->leap,
rmf->lat,
rmf->lon,
rmf->mode,
rmf->fix,
rmf->speed,
rmf->course,
rmf->pdop,
rmf->tdop
);
}
/* -------------------------------------------------------------*/
/*STEP 3 : if using the callout method, write the callout */
/* ------------------------------------------------------------ */
static void pgrmf_callout(nmeap_context_t *context,void *data,void *user_data)
{
garmin_rmf_t *rmf = (garmin_rmf_t *)data;
printf("-------------callout\n");
print_pgrmf(rmf);
}
/* ---------------------------------------------------------------------------------------*/
/* STEP 4 : allocate the data structures. be careful if you put them on the stack because */
/* they need to be live for the duration of the parser */
/* ---------------------------------------------------------------------------------------*/
static nmeap_context_t nmea; /* parser context */
static nmeap_gga_t gga; /* this is where the data from GGA messages will show up */
static nmeap_rmc_t rmc; /* this is where the data from RMC messages will show up */
static garmin_rmf_t rmf; /* this is where the data from RMF messages will show up */
static int user_data; /* user can pass in anything. typically it will be a pointer to some user data */
int main(int argc,char *argv[])
{
int status;
char ch;
/* ---------------------------------------*/
/*STEP 5 : initialize the nmea context */
/* ---------------------------------------*/
status = nmeap_init(&nmea,(void *)&user_data);
if (status != 0) {
printf("nmeap_init %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 6 : add standard GPGGA parser */
/* (no callout this time) */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"GPGGA",nmeap_gpgga,0,&gga);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 7 : add standard GPRMC parser */
/* (no callout this time) */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"GPRMC",nmeap_gprmc,0,&rmc);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 8 : ADD THE CUSTOM PARSER */
/* with callout ) */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"PGRMF",custom_pgrmf,pgrmf_callout,&rmf);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 9 : process input until done */
/* -------------------------------------- */
for(;;) {
/* ---------------------------------------*/
/*STEP 10: get a byte at a time */
/* -------------------------------------- */
ch = readchar();
if (ch <= 0) {
break;
}
/* --------------------------------------- */
/*STEP 11: pass it to the parser */
/* status indicates whether a complete msg */
/* arrived for this byte */
/* NOTE : in addition to the return status */
/* the message callout will be fired when */
/* a complete message is processed */
/* --------------------------------------- */
status = nmeap_parse(&nmea,ch);
/* ---------------------------------------*/
/*STEP 12 : process the return code */
/* -------------------------------------- */
switch(status) {
case NMEAP_GPGGA:
/* GOT A GPGGA MESSAGE */
printf("-------------switch\n");
printf("GPGGA\n");
printf("-------------\n");
break;
case NMEAP_GPRMC:
/* GOT A GPRMC MESSAGE */
printf("-------------switch\n");
printf("GPRMC\n");
printf("-------------\n");
break;
case GARMIN_PGRMF:
/* GOT A PGRMF MESSAGE */
printf("-------------switch\n");
print_pgrmf(&rmf);
printf("-------------\n");
break;
default:
break;
}
}
return 0;
}

View file

@ -0,0 +1,29 @@
INCLUDES= ..\inc\nmeap.h ..\inc\nmeap_def.h
CSRC = nmeap01.c
LIBNAME = ..\lib\libnmeap.lib
# build everything
all : test1.exe test2.exe test3.exe wingps.exe
test1.exe : test1.c $(LIBNAME)
cl /DNDEBUG /c /I..\inc test1.c
link /OUT:test1.exe test1.obj $(LIBNAME)
test2.exe : test2.c $(LIBNAME)
cl /DNDEBUG /c /I..\inc test2.c
link /OUT:test2.exe test2.obj $(LIBNAME)
test3.exe : test3.c $(LIBNAME)
cl /DNDEBUG /c /I..\inc test3.c
link /OUT:test3.exe test3.obj $(LIBNAME)
wingps.exe : wingps.c $(LIBNAME)
cl /DNDEBUG /c /I..\inc wingps.c
link /OUT:wingps.exe wingps.obj $(LIBNAME)
# erase all intermediate and output files
clean :
-erase *.obj
-erase *.exe

View file

@ -0,0 +1,244 @@
/*
Copyright (c) 2005, David M Howard (daveh at dmh2000.com)
All rights reserved.
This product is licensed for use and distribution under the BSD Open Source License.
see the file COPYING for more details.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
========================================================================================================
EXAMPLE : SETUP FOR GGA AND RMC SENTENCES WITH SERIAL IO FOR WIN32
=======================================================================================================
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nmeap.h"
/** open a serial port */
static HANDLE openPort(const char *port,int baud)
{
HANDLE h;
DCB dcb;
COMMTIMEOUTS tmo;
int status;
/* open the port */
h = CreateFile( port,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if (h == INVALID_HANDLE_VALUE) {
/* quit on error */
return h;
}
/* read current configuration */
status = GetCommState(h,&dcb);
if (status == 0) {
CloseHandle(h);
return INVALID_HANDLE_VALUE;
}
/* set the baud rate and other parameters */
dcb.BaudRate = baud;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
/* set configuration */
status = SetCommState(h, &dcb);
if (status == 0) {
CloseHandle(h);
return INVALID_HANDLE_VALUE;
}
/* read timeout configuration */
status = GetCommTimeouts(h,&tmo);
if (status == 0) {
CloseHandle(h);
return INVALID_HANDLE_VALUE;
}
/* set to indefinite blocking */
tmo.ReadIntervalTimeout = 0;
tmo.ReadTotalTimeoutConstant = 0;
tmo.ReadTotalTimeoutMultiplier = 0;
status = SetCommTimeouts(h,&tmo);
if (status == 0) {
CloseHandle(h);
return INVALID_HANDLE_VALUE;
}
return h;
}
/** read a byte (blocking) */
static int readPort(HANDLE h)
{
BOOL status;
char ch;
DWORD count;
status = ReadFile(h,&ch,1,&count,0);
if (status == 0) {
return -1;
}
return (int)ch;
}
static void closePort(HANDLE h)
{
CloseHandle(h);
}
/** print current data */
static void printGps(nmeap_gga_t *gga,nmeap_rmc_t *rmc)
{
printf("%lu %lu %.6f %.6f %.0f %f %f %d %d\n",
gga->time,
rmc->date,
gga->latitude ,
gga->longitude,
gga->altitude ,
rmc->course,
rmc->speed,
gga->satellites,
gga->quality
);
}
/* ---------------------------------------------------------------------------------------*/
/* STEP 1 : allocate the data structures. be careful if you put them on the stack because */
/* they need to be live for the duration of the parser */
/* ---------------------------------------------------------------------------------------*/
static nmeap_context_t nmea; /* parser context */
static nmeap_gga_t gga; /* this is where the data from GGA messages will show up */
static nmeap_rmc_t rmc; /* this is where the data from RMC messages will show up */
static int user_data; /* user can pass in anything. typically it will be a pointer to some user data */
int main(int argc,char *argv[])
{
int status;
char ch;
const char *port;
int baud;
HANDLE h;
/* require both arguments */
if (argc != 3) {
printf("%s <comport> <baud>\n",argv[0]);
return 1;
}
/* serial port argument */
port = argv[1];
/* baud rate argument */
status = sscanf(argv[2],"%d",&baud);
if (status != 1) {
printf("%s <comport> <baud>\n",argv[0]);
printf("invalid <baud> : %s\n",argv[2]);
return 1;
}
/** open the serial port */
h = openPort(port,baud);
if (h == INVALID_HANDLE_VALUE) {
printf("can't open port : %s\n",port);
return 1;
}
/* ---------------------------------------*/
/*STEP 2 : initialize the nmea context */
/* ---------------------------------------*/
status = nmeap_init(&nmea,(void *)&user_data);
if (status != 0) {
printf("nmeap_init %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 3 : add standard GPGGA parser */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"GPGGA",nmeap_gpgga,0,&gga);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 4 : add standard GPRMC parser */
/* -------------------------------------- */
status = nmeap_addParser(&nmea,"GPRMC",nmeap_gprmc,0,&rmc);
if (status != 0) {
printf("nmeap_add %d\n",status);
exit(1);
}
/* ---------------------------------------*/
/*STEP 5 : process input until done */
/* -------------------------------------- */
for(;;) {
/* ---------------------------------------*/
/*STEP 6 : get a byte at a time */
/* -------------------------------------- */
ch = readPort(h);
if (ch <= 0) {
break;
}
/* --------------------------------------- */
/*STEP 7 : pass it to the parser */
/* status indicates whether a complete msg */
/* arrived for this byte */
/* NOTE : in addition to the return status */
/* the message callout will be fired when */
/* a complete message is processed */
/* --------------------------------------- */
status = nmeap_parse(&nmea,ch);
/* ---------------------------------------*/
/*STEP 8 : process the return code */
/* -------------------------------------- */
switch(status) {
case NMEAP_GPGGA:
/* GOT A GPGGA MESSAGE */
printGps(&gga,&rmc);
break;
case NMEAP_GPRMC:
/* GOT A GPRMC MESSAGE */
printGps(&gga,&rmc);
break;
default:
break;
}
}
/* close and quit */
closePort(h);
return 0;
}

View file

@ -0,0 +1,22 @@
# directories
SRC=src
TST=tst
all :
cd $(SRC)
$(MAKE) -f nmeap.mak all
cd ..\$(TST)
$(MAKE) -f tst.mak all
cd ..
clean :
cd $(SRC)
$(MAKE) -f nmeap.mak clean
cd ..\$(TST)
$(MAKE) -f tst.mak clean
cd ..
doc :
doxygen