mirror of
https://github.com/markqvist/OpenModem.git
synced 2025-07-22 15:00:43 -04:00
Working
This commit is contained in:
commit
c898b090dd
1049 changed files with 288572 additions and 0 deletions
31
bertos/net/nmeap/COPYING
Normal file
31
bertos/net/nmeap/COPYING
Normal 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
1153
bertos/net/nmeap/Doxyfile
Normal file
File diff suppressed because it is too large
Load diff
23
bertos/net/nmeap/Makefile
Normal file
23
bertos/net/nmeap/Makefile
Normal 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
24
bertos/net/nmeap/README
Normal 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.
|
||||
|
||||
|
||||
|
151
bertos/net/nmeap/doc/tutorial.html
Normal file
151
bertos/net/nmeap/doc/tutorial.html
Normal 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"> </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"> </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"> </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"> </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>
|
227
bertos/net/nmeap/inc/nmeap.h
Normal file
227
bertos/net/nmeap/inc/nmeap.h
Normal 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
|
||||
|
72
bertos/net/nmeap/inc/nmeap_def.h
Normal file
72
bertos/net/nmeap/inc/nmeap_def.h
Normal 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__ */
|
31
bertos/net/nmeap/src/Makefile
Normal file
31
bertos/net/nmeap/src/Makefile
Normal 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)
|
||||
|
20
bertos/net/nmeap/src/nmeap.mak
Normal file
20
bertos/net/nmeap/src/nmeap.mak
Normal 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)
|
||||
|
||||
|
634
bertos/net/nmeap/src/nmeap01.c
Normal file
634
bertos/net/nmeap/src/nmeap01.c
Normal 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;
|
||||
}
|
||||
|
||||
|
17
bertos/net/nmeap/tst/Makefile
Normal file
17
bertos/net/nmeap/tst/Makefile
Normal 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
|
||||
|
||||
|
193
bertos/net/nmeap/tst/test1.c
Normal file
193
bertos/net/nmeap/tst/test1.c
Normal 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;
|
||||
}
|
||||
|
208
bertos/net/nmeap/tst/test2.c
Normal file
208
bertos/net/nmeap/tst/test2.c
Normal 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;
|
||||
}
|
||||
|
306
bertos/net/nmeap/tst/test3.c
Normal file
306
bertos/net/nmeap/tst/test3.c
Normal 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;
|
||||
}
|
||||
|
29
bertos/net/nmeap/tst/tst.mak
Normal file
29
bertos/net/nmeap/tst/tst.mak
Normal 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
|
||||
|
||||
|
244
bertos/net/nmeap/tst/wingps.c
Normal file
244
bertos/net/nmeap/tst/wingps.c
Normal 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;
|
||||
}
|
22
bertos/net/nmeap/win32.mak
Normal file
22
bertos/net/nmeap/win32.mak
Normal 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
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue