上传第一版代码

PDRHistoryBranch
詹力 2022-09-15 16:35:25 +08:00
parent 021739e0be
commit 5af266e05b
435 changed files with 2446064 additions and 0 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
.DS_store

View File

@ -0,0 +1,95 @@
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific works
("Commons") that the public can reliably and without fear of later claims of
infringement build upon, modify, incorporate in other works, reuse and
redistribute as freely as possible in any form whatsoever and for any purposes,
including without limitation commercial purposes. These owners may contribute to
the Commons to promote the ideal of a free culture and the further production of
creative, cultural and scientific works, or to gain reputation or greater
distribution for their Work in part through the use and efforts of others.
For these and/or other purposes and motivations, and without any expectation of
additional consideration or compensation, the person associating CC0 with a Work
(the "Affirmer"), to the extent that he or she is an owner of Copyright and
Related Rights in the Work, voluntarily elects to apply CC0 to the Work and
publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and Related
Rights"). Copyright and Related Rights include, but are not limited to, the
following:
the right to reproduce, adapt, distribute, perform, display, communicate, and
translate a Work; moral rights retained by the original author(s) and/or
performer(s); publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work; rights protecting against unfair competition in
regards to a Work, subject to the limitations in paragraph 4(a), below; rights
protecting the extraction, dissemination, use and reuse of data in a Work;
database rights (such as those arising under Directive 96/9/EC of the European
Parliament and of the Council of 11 March 1996 on the legal protection of
databases, and under any national implementation thereof, including any amended
or successor version of such directive); and other similar, equivalent or
corresponding rights throughout the world based on applicable law or treaty, and
any national implementations thereof. 2. Waiver. To the greatest extent
permitted by, but not in contravention of, applicable law, Affirmer hereby
overtly, fully, permanently, irrevocably and unconditionally waives, abandons,
and surrenders all of Affirmer's Copyright and Related Rights and associated
claims and causes of action, whether now known or unknown (including existing as
well as future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or treaty
(including future time extensions), (iii) in any current or future medium and
for any number of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the "Waiver").
Affirmer makes the Waiver for the benefit of each member of the public at large
and to the detriment of Affirmer's heirs and successors, fully intending that
such Waiver shall not be subject to revocation, rescission, cancellation,
termination, or any other legal or equitable action to disrupt the quiet
enjoyment of the Work by the public as contemplated by Affirmer's express
Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free, non
transferable, non sublicensable, non exclusive, irrevocable and unconditional
license to exercise Affirmer's Copyright and Related Rights in the Work (i) in
all territories worldwide, (ii) for the maximum duration provided by applicable
law or treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional purposes
(the "License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any reason
be judged legally invalid or ineffective under applicable law, such partial
invalidity or ineffectiveness shall not invalidate the remainder of the License,
and in such case Affirmer hereby affirms that he or she will not (i) exercise
any of his or her remaining Copyright and Related Rights in the Work or (ii)
assert any associated claims and causes of action with respect to the Work, in
either case contrary to Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document. Affirmer offers
the Work as-is and makes no representations or warranties of any kind concerning
the Work, express, implied, statutory or otherwise, including without limitation
warranties of title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or the
present or absence of errors, whether or not discoverable, all to the greatest
extent permissible under applicable law. Affirmer disclaims responsibility for
clearing rights of other persons that may apply to the Work or any use thereof,
including without limitation any person's Copyright and Related Rights in the
Work. Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the Work. Affirmer
understands and acknowledges that Creative Commons is not a party to this
document and has no duty or obligation with respect to this CC0 or use of the
Work.

View File

@ -0,0 +1,244 @@
# NMEAParser
An Arduino library to parse NMEA sentences.
NMEA is a communication standard in the marine equipment industry: GPS, anemometers,... The NMEAParser library allows you to analyze NMEA sentences and associate handlers to each of those that need to be recognized. The library provides the means to associate a handler to each identifier and also provides functions to retrieve the arguments of the sentence in different data formats: character, integer, float, character string.
## Changelog
* ```1.1``` : Added joker in type. Remove binaries from ```extra```
* ```1.0``` : Initial release
## Memory footprint
On an Arduino Uno, an instance of a NMEAParser requires 97 bytes with only one handler. 8 bytes per additional handler are required.
## Using NMEAParser
As usual, the library should be included at the beginning of the sketch.
```C++
#include <NMEAParser.h>
```
Then, you must instantiate a parser as follows:
```C++
NMEAParser<4> parser;
```
The ```4``` is the maximum number of handlers desired, ```parser``` is the name of the object that will allow the analysis of NMEA sentences. you can of course use the name you want.
In ```setup``` you configure your parser as you wish using the following functions.
### ```void addHandler(<type>, <handler>)```
where ```<type>``` is a character string and the type of sentence to recongnize, and ```<handler>``` the function to call when a sentence is recognize. ```<type>``` can be a string stored un RAM or a string stored in flash : ```F("ASTRI")```. If ```<type>``` has more than 5 characters, it is trucated.
For example, suppose you want to recognize the following sounder sentences which give the water depth below keel (DBK) and below surface (DBS):
```
$SDDBK,...
```
and
```
$SDDBS,...
```
You will install two handlers as follows (assuming your parsing object is named ```parser```):
```C++
parser.addHandler("SDDBK", handleDepthBelowKeel);
parser.addHanlder("SDDBS", handleDepthBelowSurface);
```
```handleDepthBelowKeel``` and ```handleDepthBelowSurface``` are functions that will be called when sentences are recognized.
With version 1.1, ```<type>``` may include hyphens. An hyphens matches any character. For instance if you want the handler to match all sentences coming from the sounder, you would write:
```C++
parser.addHandler("SD---", handleSounder);
```
```handleSounder``` function would be called for any sentence with the type beginning with SD.
### ```void setDefaultHandler(<handler>)```
When a sentence is succefully parsed but none of the handler correspond to it, ```<handler>``` is called. It is a function corresponding to a ```void f(void)``` prototype. By default, no function is called.
### ```void setErrorHandler(<handler>)```
When a sentence is malformed : too long, wrong characters, etc, ```<handler>``` is called. It is a function corresponding to a ```void f(void)``` prototype. By default, no function is called.
### ```void setHandleCRC(<doCRC>)```
Specifies whether the CRC is checked or not. By default, the CRC is checked. If you do not want CRC verification, pass ```false``` to ```setHandleCRC```.
---
In the handlers, you will get the arguments of the sentence, the sentence type or the error if any by using the following functions:
### ```bool getArg(<argnum>, <argval>)```
is used to get the arguments of the sentence. ```<argnum>``` is the number of the argument, starting at 0 for the argument that follows the identifier. ```<argval``` is a variable where the argument value will be stored if successful. ```getArg``` returns a boolean which is true if successfull, false if not.
Continuing with the example, [both sentences have the same 6 arguments](https://gpsd.gitlab.io/gpsd/NMEA.html#_dbk_depth_below_keel)
* Argument 0 is a float number giving the depth in feet.
* Argument 1 is a ```f``` for feet.
* Argument 2 is a float number giving the depth in meters.
* Argument 3 is a ```M``` for Meters.
* Argument 4 is a float number giving the depth in fathoms.
* At last Argument 5 is a ```F``` for Fathoms.
Suppose you are interested by the depths in meters and you have two float variables to store theses data:
```C++
float depthBelowKeel;
float depthBelowSurface;
```
You would implement ```handleDepthBelowKeel``` and ```handleDepthBelowSurface``` as follow:
```C++
void handleDepthBelowKeel(void)
{
float depth;
if (parser.getArg(2, depth))
{
depthBelowKeel = depth;
}
}
void handleDepthBelowSurface(void)
{
float depth;
if (parser.getArg(2, depth))
{
depthBelowSurface = depth;
}
}
```
### ```bool getType(<type>) / bool getType(<num>, <charType>)```
3 versions of ```getType``` exist. The first one puts the type of the sentence in ```<type>``` which is a ```char *```. The second one does the same but ```<type>``` is a ```String```. Return ```true``` if a type has been parsed, ```false``` otherwise. The third one puts a character of the type at position ```<num>```
### ```uint8_t argCount()```
Return the number of arguments.
### ```NMEA::ErrorCode error()```
Return the error. The returned code can be:
* ```NMEA::NO_ERROR```: no error;
* ```NMEA::UNEXPECTED_CHAR```: a char which is not expected in the sentence has been encountered;
* ```NMEA::BUFFER_FULL```: the sentence is too long to fit in the buffer;
* ```NMEA::TYPE_TOO_LONG```: the sentence type has more than 5 characters;
* ```NMEA::CRC_ERROR```: the CRC is wrong;
* ```NMEA::INTERNAL_ERROR```: the internal state of the parser is wrong, should not happen by the way.
### Feeding characters to the parser
Characters are fed to the parser in ```loop```, assuming we get the characters from ```Serial```, the following way:
```C++
while (Serial.available()) {
parser << Serial.read();
}
```
This can also be done in ```serialEvent```. ```while``` could be replaced by ```if```.
## A complete example
Let's say you want to turn the Arduino's LED on and off. We define a NMEA sentence taking a single argument: 1 to turn on the LED and 0 to turn it off. The sentence can therefore be:
```
$ARLED,1*43
```
to turn the LED on and
```
$ARLED,0*42
```
to turn the LED off. We define a single handler to retrieve the argument and control the LED accordingly:
```C++
void ledHandler()
{
Serial.print("Got ARLED with ");
Serial.print(parser.argCount());
Serial.println(" arguments");
int ledState;
if (parser.getArg(0,ledState)) {
digitalWrite(LED_BUILTIN, ledState == 0 ? LOW : HIGH);
}
}
```
We define 2 other handlers for anything else than ```ARLED``` and for errors
```C++
void errorHandler()
{
Serial.print("*** Error : ");
Serial.println(parser.error());
}
void unknownCommand()
{
Serial.print("*** Unkown command : ");
char buf[6];
parser.getType(buf);
Serial.println(buf);
}
```
In ```setup```, the handlers are installed:
```C++
void setup() {
Serial.begin(115200);
parser.setErrorHandler(errorHandler);
parser.addHandler("ARLED", ledHandler);
parser.setDefaultHandler(unknownCommand);
pinMode(LED_BUILTIN, OUTPUT);
}
```
At last in loop, we feed the parser with the chars coming from ```Serial```.
```C++
void loop() {
if (Serial.available()) {
parser << Serial.read();
}
}
```
---
# Extra software
Additional software can be found in the ```extra``` directory.
## NMEA sentences generator
The ```gen``` subdirectory contains ```nmeagen```, a NMEA sentence generator program. This program generates well formed sentences with good or bad CRC. It can be used to test the parser. To build ```nmeagen```, run the ```build.sh``` script. ```nmeagen``` takes 1 or 2 arguments. The first argument is the number of sentences to generate. The second optional one is the number of sentences with bad CRC.
## Test program
The ```test``` subdirectory contains a test program that compile on Linux or Mac OS X. It takes sentences from the standard input, parse them and print out the type, the arguments and if an error occured.
---
# Additional links
* [NMEA CRC calculator](https://nmeachecksum.eqth.net)
* [NMEA 0183 on Wikipedia](https://fr.wikipedia.org/wiki/NMEA_0183)
* [NMEA revealed](https://gpsd.gitlab.io/gpsd/NMEA.html)
That's all folks !

View File

@ -0,0 +1,60 @@
/*
* NMEAParser library.
*
* A NMEA example to switch ON or OFF the built in LED
*
* Sentences to send are :
*
* $ARLED,1*43
*
* to switch the LED on and
*
* $ARLED,0*42
*
* to switch the LED off
*
* Set the serial monitor end of line to <cr><lf>
*/
#include <NMEAParser.h>
/* A parser with only one handler */
NMEAParser<1> parser;
void errorHandler()
{
Serial.print("*** Error : ");
Serial.println(parser.error());
}
void unknownCommand()
{
Serial.print("*** Unkown command : ");
char buf[6];
parser.getType(buf);
Serial.println(buf);
}
void ledHandler()
{
Serial.print("Got ARLED with ");
Serial.print(parser.argCount());
Serial.println(" arguments");
int ledState;
if (parser.getArg(0,ledState)) {
digitalWrite(LED_BUILTIN, ledState == 0 ? LOW : HIGH);
}
}
void setup() {
Serial.begin(115200);
parser.setErrorHandler(errorHandler);
parser.addHandler("ARLED", ledHandler);
parser.setDefaultHandler(unknownCommand);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
if (Serial.available()) {
parser << Serial.read();
}
}

View File

@ -0,0 +1,67 @@
/*
* NMEAParser library.
*
* A NMEA example to switch ON or OFF the built in LED but with the use
* of wild char in the type
*
* Sentences to send are :
*
* $ARLE1*2B
*
* to switch the LED on and
*
* $ARLE0*2A
*
* to switch the LED off
*
* Set the serial monitor end of line to <cr><lf>
*/
#include <NMEAParser.h>
/* A parser with only one handler */
NMEAParser<1> parser;
void errorHandler()
{
Serial.print("*** Error : ");
Serial.println(parser.error());
}
void unknownCommand()
{
Serial.print("*** Unkown command : ");
char buf[6];
parser.getType(buf);
Serial.println(buf);
}
void ledHandler()
{
Serial.print("Got ARLEx with ");
Serial.print(parser.argCount());
Serial.println(" arguments");
char wantedLedState;
if (parser.getType(4, wantedLedState)) { // get the 4th character ot the type
if (wantedLedState == '0' || wantedLedState == '1') {
digitalWrite(LED_BUILTIN, wantedLedState == '0' ? LOW : HIGH);
}
else {
Serial.println("x should be 0 or 1");
}
}
}
void setup() {
Serial.begin(115200);
parser.setErrorHandler(errorHandler);
parser.addHandler("ARLE-", ledHandler);
parser.setDefaultHandler(unknownCommand);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
if (Serial.available()) {
parser << Serial.read();
}
}

View File

@ -0,0 +1,77 @@
/*
* NMEAParser library.
*
* Parsing example.
*
* 2 handlers are added and 2 sentences are parsed
*/
#include <NMEAParser.h>
/* A parser is declared with 2 handlers at most */
NMEAParser<2> parser;
const char firstSentence[] = "$DBAXK*54\r\n";
const char secondSentence[] = "$EJCHQ,03,O,UUEIE,S,953.11,S,4.172,ASBUX,J*54\r\n";
void errorHandler()
{
Serial.print("*** Error : ");
Serial.println(parser.error());
}
void firstHandler()
{
Serial.print("Got DBAXK with ");
Serial.print(parser.argCount());
Serial.println(" arguments");
}
void secondHandler()
{
Serial.print("Got $EJCHQ with ");
Serial.print(parser.argCount());
Serial.println(" arguments");
int arg0;
char arg1;
char arg2[10];
char arg3;
float arg4;
char arg5;
float arg6;
String arg7;
char arg8;
if (parser.getArg(0,arg0)) Serial.println(arg0);
if (parser.getArg(1,arg1)) Serial.println(arg1);
if (parser.getArg(2,arg2)) Serial.println(arg2);
if (parser.getArg(3,arg3)) Serial.println(arg3);
if (parser.getArg(4,arg4)) Serial.println(arg4);
if (parser.getArg(5,arg5)) Serial.println(arg5);
if (parser.getArg(6,arg6)) Serial.println(arg6);
if (parser.getArg(7,arg7)) Serial.println(arg7);
if (parser.getArg(8,arg8)) Serial.println(arg8);
}
void setup()
{
Serial.begin(115200);
parser.setErrorHandler(errorHandler);
parser.addHandler("DBAXK", firstHandler);
parser.addHandler("EJCHQ", secondHandler);
Serial.print("Parsing : ");
Serial.print(firstSentence);
for (uint8_t i = 0; i < strlen(firstSentence); i++) {
parser << firstSentence[i];
}
Serial.print("Parsing : ");
Serial.print(secondSentence);
for (uint8_t i = 0; i < strlen(secondSentence); i++) {
parser << secondSentence[i];
}
}
void loop()
{
}

View File

@ -0,0 +1,101 @@
#include <stdio.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
char intToHex(uint8_t v)
{
if (v < 10) return '0' + v;
else if (v < 16) return 'A' + v - 10;
else return '-';
}
void generateSentence(bool goodCRC)
{
uint8_t crc = 0;
putchar('$');
/* generate the id */
for (int i = 0; i < 5; i++) {
char letter = 'A' + random() % 26;
putchar(letter);
crc ^= letter;
}
int numberOfArgs = random() % 10;
for (int i = 0; i < numberOfArgs; i++) {
putchar(',');
crc ^= ',';
int kind = random() % 5;
switch (kind) {
case 0: /* integer */
for (int j = 0; j < random() % 5 + 1; j++) {
char digit = '0' + random() % 10;
putchar(digit);
crc ^= digit;
}
break;
case 1: /* char */
if (1) {
char letter = 'A' + random() % 26;
putchar(letter);
crc ^= letter;
}
break;
case 2: /* string */
for (int i = 0; i < 5; i++) {
char letter = 'A' + random() % 26;
putchar(letter);
crc ^= letter;
}
break;
case 3: /* float */
for (int j = 0; j < random() % 5 + 1; j++) {
char digit = '0' + random() % 10;
putchar(digit);
crc ^= digit;
}
putchar('.');
crc ^= '.';
for (int j = 0; j < random() % 5 + 1; j++) {
char digit = '0' + random() % 10;
putchar(digit);
crc ^= digit;
}
case 4: /* nothing */
break;
}
}
if (! goodCRC) crc = ~crc;
putchar('*');
putchar(intToHex(crc >> 4));
putchar(intToHex(crc & 0x0F));
putchar('\r');
putchar('\n');
}
int main(int argc, char *argv[])
{
if (argc < 2 || argc > 3) {
printf("Usage: nmeagen <n> [<c>] where <n> is the number of sentences\n"
" and <c> the number of sentences with a wrong CRC\n");
exit(1);
}
else {
int numberOfSentences = strtol(argv[1], NULL, 0);
int numberOfWrongCRCSentences = 0;
if (argc == 3) {
numberOfWrongCRCSentences = strtol(argv[2], NULL, 0);
}
/* Init a the random seed with the epoch */
srandom(time(NULL));
for (int i = 0; i < numberOfSentences; i++) {
generateSentence(true);
}
for (int i = 0; i < numberOfWrongCRCSentences; i++) {
generateSentence(false);
}
}
}

View File

@ -0,0 +1,3 @@
#!/bin/bash
gcc -o nmeagen NMEASentencesGenerator.c

View File

@ -0,0 +1,30 @@
$FRMIO,G,33,081,I,Q,Q,J,48*2E
$APYOG,LDCXT,JSCIE,KUJAU,64,EJMVQ,F,P,AJQLD*12
$SUHEG,GAGCE,G,Q*31
$KIEHA,E,W,R,SXGMG,64,40,WCIEC,2253,THWNT*6E
$WPHGT,FRYBV,5689,C*68
$YDXHC,A,J*45
$GUIAZ,CWIMW*2B
$SOEOE,ZBKFY,HKTHJ,536,504,946,0,705,BIDPI,ACEOK*4B
$TEHJW,NNHPF,4,34,DFSFJ,8638,F,B*59
$LKGPI,XTUUO*36
$ZCHFN,769,0202,12*4E
$DQTMV,Y,712,0*2B
$MQPEM,H,95,E,M,U,92,N,340,D*47
$APUXX,RKPFP,MUQOA,VOCGB,39,6,D,V,U*78
$MBLXP,22573,3,AVNFF*3C
$LCBXF,V,21,AKCLF,YIASR,PHIUS,VPRJM*11
$OFXPI,2,B,14,A*7C
$IUKFO,D,MSETP,99,GYBDX*05
$UDSFE,QHPNK,D,N,R*55
$EDULQ,ISGJX,FDXLJ,O,H,M,B,ROZIU,C*4A
$FUONE,P,Y*5E
$VLVIU*50
$WTBCG,14,819,ATHAO,V,MSUHV,NSCLD*76
$WJZLK,3,Q,31*0C
$MRFGM,NCVPP,NZCBL,F,U,C,672,LPFAS*56
$UFOJW,V,AIUHA,BFBKN*2C
$OHOSS,HOIBI,8695,031,B,CVSMD,154,DEUCB*79
$MYHGK,ZKTDK,YFJBI,OLBOI,74,KPASB,XBCSE,Q,U*00
$ZIENW*4F
$ONAYP,N,27,A*6F

View File

@ -0,0 +1,3 @@
#!/bin/bash
c++ -o testNMEA testNMEA.cpp

View File

@ -0,0 +1,63 @@
#include "../../src/NMEAParser.h"
#include <string.h>
NMEAParser<4> commandNMEA;
int errorCount = 0;
void error()
{
printf("=================================================\n");
int err = commandNMEA.error();
printf("*** ERROR %d ",err);
switch (err) {
case NMEA::UNEXPECTED_CHAR:
printf("(UNEXPECTED CHAR)\n");
break;
case NMEA::BUFFER_FULL:
printf("(BUFFER FULL)\n");
break;
case NMEA::CRC_ERROR:
printf("(CRC ERROR)\n");
break;
case NMEA::INTERNAL_ERROR:
printf("(INTERNAL ERROR)\n");
break;
default:
printf("(?)\n");
break;
}
commandNMEA.printBuffer();
printf("=================================================\n");
errorCount++;
}
void defaultHandler()
{
printf("------------\n");
char buf[82];
if (commandNMEA.getType(buf)) {
printf("%s\n", buf);
for (int i = 0; i < commandNMEA.argCount(); i++) {
if (commandNMEA.getArg(i, buf)) {
printf(" %s\n", buf);
}
}
}
}
int main()
{
printf("Debut du test\n");
commandNMEA.setErrorHandler(error);
commandNMEA.setDefaultHandler(defaultHandler);
int count = 0;
int v;
while ((v = getchar()) != EOF) {
commandNMEA << v;
if (v == '\n') count++;
}
printf("*** Processed %d NMEA sentences\n", count);
printf("*** Got %d error(s)\n", errorCount);
}

View File

@ -0,0 +1,32 @@
#######################################
# Syntax Coloring Map For NMEAParser
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
NMEAParser KEYWORD1
NMEA KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
addHandler KEYWORD2
setErrorHandler KEYWORD2
setDefaultHandler KEYWORD2
argCount KEYWORD2
getArg KEYWORD2
getType KEYWORD2
error KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
NO_ERROR LITERAL1
UNEXPECTED_CHAR LITERAL1
BUFFER_FULL LITERAL1
CRC_ERROR LITERAL1
INTERNAL_ERROR LITERAL1

View File

@ -0,0 +1,9 @@
name=NMEAParser
version=1.1
author=Glinnes Hulden
maintainer=Glinnes Hulden
sentence=A simple Arduino library to parse NMEA sentences.
paragraph=A simple Arduino library to parse NMEA sentences.
category=Communication
url=https://github.com/Glinnes/NMEAParser
architectures=*

View File

@ -0,0 +1,657 @@
/*
* NMEA parser library for Arduino
*
* Simple and compact NMEA parser designed for Arduino
*
* Author : Glinnes Hulden
*
* This work is distributed under license CC0.
* Check https://creativecommons.org/publicdomain/zero/1.0/deed.en
*
* No Copyright
*
* The person who associated a work with this deed has dedicated the work
* to the public domain by waiving all of his or her rights to the work
* worldwide under copyright law, including all related and neighboring rights,
* to the extent allowed by law.
*
* You can copy, modify, distribute and perform the work, even for commercial
* purposes, all without asking permission. See Other Information below.
*
* Other Information
*
* In no way are the patent or trademark rights of any person affected by CC0,
* nor are the rights that other persons may have in the work or in how the
* work is used, such as publicity or privacy rights.
* Unless expressly stated otherwise, the person who associated a work with
* this deed makes no warranties about the work, and disclaims liability for
* all uses of the work, to the fullest extent permitted by applicable law.
* When using or citing the work, you should not imply endorsement by the
* author or the affirmer.
*/
#ifndef __NMEAParser_h__
#define __NMEAParser_h__
#ifdef __amd64__
/* To use on my development platform */
#include <stddef.h>
#include <stdint.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#else
#include <Arduino.h>
#endif
namespace NMEA {
/*
* Error codes
*/
typedef enum {
NO_ERROR,
UNEXPECTED_CHAR,
BUFFER_FULL,
TYPE_TOO_LONG,
CRC_ERROR,
INTERNAL_ERROR
} ErrorCode;
}
/*
* The library consists of a single template: NMEAParser.
*/
template <size_t S> class NMEAParser {
private:
typedef void (*NMEAErrorHandler)(void);
typedef void (*NMEAHandler)(void);
typedef struct { char mToken[6]; NMEAHandler mHandler; } NMEAHandlerEntry;
typedef enum { INIT, SENT, ARG, CRCH, CRCL, CRLFCR, CRLFLF } State;
public:
/*
* maximum sentence size is 82 including the starting '$' and the <cr><lf>
* at the end. Since '$', the '*', the 2 characters CRC and the <cr><lf>
* are not bufferized, 82 - 6 + 1 = 77 chars are enough.
* is enough.
*/
static const uint8_t kSentenceMaxSize = 77;
private:
/*
* buffer to store the NMEA sentence excluding starting '$', the ','
* separator, the '*', the CRC and the <cr><lf>. The tail of the buffer
* is used to store the index of the arguments.
*/
char mBuffer[kSentenceMaxSize];
/*
* Current index to store a char of the sentence
*/
uint8_t mIndex;
/*
* Current index to store the index of an argument
*/
uint8_t mArgIndex;
/*
* A handler to notify a malformed sentence
*/
NMEAErrorHandler mErrorHandler;
/*
* A handler for well formed but unrecognized sentences
*/
NMEAHandler mDefaultHandler;
/*
* An array of NMEA handler : pointers to functions to call when a sentence
* is recognized
*/
NMEAHandlerEntry mHandlers[S];
/*
* The current number of mHandlers
*/
uint8_t mHandlerCount;
/*
* Parsing automaton variable
*/
State mState;
/*
* mError
*/
NMEA::ErrorCode mError;
/*
* True if CRC is handled, false otherwise. Defaults to true
*/
bool mHandleCRC;
/*
* Variables used to computed and parse the CRC
*/
uint8_t mComputedCRC;
uint8_t mGotCRC;
/*
* NMEAParserStringify is used internally to temporarely replace a char
* in the buffer by a '\0' so that libc string functions may be used.
* Instantiating a NMEAParserStringify object in a pair of {} defines
* a section in which the 'stringification' is done : the constructor
* does that according to the arguments and se destructor restore the buffer.
*/
class NMEAParserStringify {
uint8_t mPos;
char mTmp;
NMEAParser<S> *mParent;
public:
NMEAParserStringify(NMEAParser<S> *inParent, uint8_t inPos) :
mPos(inPos),
mParent(inParent)
{
mTmp = mParent->mBuffer[mPos];
mParent->mBuffer[mPos] = '\0';
}
~NMEAParserStringify()
{
mParent->mBuffer[mPos] = mTmp;
}
};
/*
* Call the error handler if defined
*/
void callErrorHandler(void)
{
if (mErrorHandler != NULL) {
mErrorHandler();
}
}
/*
* Reset the parser
*/
void reset() {
mState = INIT;
mIndex = 0;
mArgIndex = kSentenceMaxSize;
mError = NMEA::NO_ERROR;
}
/*
* Called when the parser encounter a char that should not be there
*/
void unexpectedChar()
{
mError = NMEA::UNEXPECTED_CHAR;
callErrorHandler();
reset();
}
/*
* Called when the buffer is full because of a malformed sentence
*/
void bufferFull()
{
mError = NMEA::BUFFER_FULL;
callErrorHandler();
reset();
}
/*
* Called when the type of the sentence is longer than 5 characters
*/
void typeTooLong()
{
mError = NMEA::TYPE_TOO_LONG;
callErrorHandler();
reset();
}
/*
* Called when the CRC is wrong
*/
void crcError()
{
mError = NMEA::CRC_ERROR;
callErrorHandler();
reset();
}
/*
* Called when the state of the parser is not ok
*/
void internalError()
{
mError = NMEA::INTERNAL_ERROR;
callErrorHandler();
reset();
}
/*
* retuns true is there is at least one byte left in the buffer
*/
bool spaceAvail()
{
return (mIndex < mArgIndex);
}
/*
* convert a one hex digit char into an int. Used for the CRC
*/
static int8_t hexToNum(const char inChar)
{
if (isdigit(inChar)) return inChar - '0';
else if (isupper(inChar) && inChar <= 'F') return inChar - 'A' + 10;
else if (islower(inChar) && inChar <= 'f') return inChar - 'a' + 10;
else return -1;
}
static bool strnwcmp(const char *s1, const char *s2, uint8_t len)
{
while (len-- > 0) {
if (*s1 != *s2 && *s1 != '-' && *s2 != '-') return false;
s1++;
s2++;
}
return true;
}
/*
* return the slot number for a handler. -1 if not found
*/
int8_t getHandler(const char *inToken)
{
/* Look for the token */
for (uint8_t i = 0; i < mHandlerCount; i++) {
if (strnwcmp(mHandlers[i].mToken, inToken, 5)) {
return i;
}
}
return -1;
}
/*
* When all the sentence has been parsed, process it by calling the handler
*/
void processSentence()
{
/* Look for the token */
uint8_t endPos = startArgPos(0);
int8_t slot;
{
NMEAParserStringify stfy(this, endPos);
slot = getHandler(mBuffer);
}
if (slot != -1) {
mHandlers[slot].mHandler();
}
else {
if (mDefaultHandler != NULL) {
mDefaultHandler();
}
}
}
/*
* Return true if inArgNum corresponds to an actual argument
*/
bool validArgNum(uint8_t inArgNum)
{
return inArgNum < (kSentenceMaxSize - mArgIndex);
}
/*
* Return the start index of the inArgNum th argument
*/
uint8_t startArgPos(uint8_t inArgNum)
{
return mBuffer[kSentenceMaxSize - 1 - inArgNum];
}
/*
* Return the end index of the inArgNum th argument
*/
uint8_t endArgPos(uint8_t inArgNum)
{
return mBuffer[kSentenceMaxSize - 2 - inArgNum];
}
public:
/*
* Constructor initialize the parser.
*/
NMEAParser() :
mErrorHandler(NULL),
mDefaultHandler(NULL),
mHandlerCount(0),
mError(NMEA::NO_ERROR),
mHandleCRC(true),
mComputedCRC(0),
mGotCRC(0)
{
reset();
}
/*
* Add a sentence handler
*/
void addHandler(const char *inToken, NMEAHandler inHandler)
{
if (mHandlerCount < S) {
if (getHandler(inToken) == -1) {
strncpy(mHandlers[mHandlerCount].mToken, inToken, 5);
mHandlers[mHandlerCount].mToken[5] = '\0';
mHandlers[mHandlerCount].mHandler = inHandler;
mHandlerCount++;
}
}
}
#ifdef __AVR__
/*
* Add a sentence handler. Version with a token stored in flash.
*/
void addHandler(const __FlashStringHelper *ifsh, NMEAHandler inHandler)
{
char buf[6];
PGM_P p = reinterpret_cast<PGM_P>(ifsh);
for (uint8_t i = 0; i < 6; i++) {
char c = pgm_read_byte(p++);
buf[i] = c;
if (c == '\0') break;
}
addHandler(buf, inHandler);
}
#endif
/*
* Set the error handler which is called when a sentence is malformed
*/
void setErrorHandler(NMEAErrorHandler inHandler)
{
mErrorHandler = inHandler;
}
/*
* Set the default handler which is called when a sentence is well formed
* but has no handler associated to
*/
void setDefaultHandler(NMEAHandler inHandler)
{
mDefaultHandler = inHandler;
}
/*
* Give a character to the parser
*/
void operator<<(char inChar)
{
int8_t tmp;
switch (mState) {
/* Waiting for the starting $ character */
case INIT:
mError = NMEA::NO_ERROR;
if (inChar == '$') {
mComputedCRC = 0;
mState = SENT;
}
else unexpectedChar();
break;
case SENT:
if (isalnum(inChar)) {
if (spaceAvail()) {
if (mIndex < 5) {
mBuffer[mIndex++] = inChar;
mComputedCRC ^= inChar;
}
else {
typeTooLong();
}
}
else bufferFull();
}
else {
switch(inChar) {
case ',' :
mComputedCRC ^= inChar;
mBuffer[--mArgIndex] = mIndex;
mState = ARG;
break;
case '*' :
mGotCRC = 0;
mBuffer[--mArgIndex] = mIndex;
mState = CRCH;
break;
default :
unexpectedChar();
break;
}
}
break;
case ARG:
if (spaceAvail()) {
switch(inChar) {
case ',' :
mComputedCRC ^= inChar;
mBuffer[--mArgIndex] = mIndex;
break;
case '*' :
mGotCRC = 0;
mBuffer[--mArgIndex] = mIndex;
mState = CRCH;
break;
default :
mComputedCRC ^= inChar;
mBuffer[mIndex++] = inChar;
break;
}
}
else bufferFull();
break;
case CRCH:
tmp = hexToNum(inChar);
if (tmp != -1) {
mGotCRC |= (uint8_t)tmp << 4;
mState = CRCL;
}
else unexpectedChar();
break;
case CRCL:
tmp = hexToNum(inChar);
if (tmp != -1) {
mGotCRC |= (uint8_t)tmp;
mState = CRLFCR;
}
else unexpectedChar();
break;
case CRLFCR:
if (inChar == '\r') {
mState = CRLFLF;
}
else unexpectedChar();
break;
case CRLFLF:
if (inChar == '\n') {
if (mHandleCRC && (mGotCRC != mComputedCRC)) {
crcError();
}
else {
processSentence();
}
reset();
}
else unexpectedChar();
break;
default:
internalError();
break;
}
}
/*
* Returns the number of arguments discovered in a well formed sentence.
*/
uint8_t argCount()
{
return kSentenceMaxSize - mArgIndex - 1;
}
/*
* Returns one of the arguments. Different versions according to data type.
*/
bool getArg(uint8_t num, char &arg)
{
if (validArgNum(num)) {
uint8_t startPos = startArgPos(num);
uint8_t endPos = endArgPos(num);
arg = mBuffer[startPos];
return (endPos - startPos) == 1;
}
else return false;
}
bool getArg(uint8_t num, char *arg)
{
if (validArgNum(num)) {
uint8_t startPos = startArgPos(num);
uint8_t endPos = endArgPos(num);
{
NMEAParserStringify stfy(this, endPos);
strcpy(arg, &mBuffer[startPos]);
}
return true;
}
else return false;
}
#ifdef ARDUINO
bool getArg(uint8_t num, String &arg)
{
if (validArgNum(num)) {
uint8_t startPos = startArgPos(num);
uint8_t endPos = endArgPos(num);
{
NMEAParserStringify stfy(this, endPos);
arg = &mBuffer[startPos];
}
return true;
}
else return false;
}
#endif
bool getArg(uint8_t num, int &arg)
{
if (validArgNum(num)) {
uint8_t startPos = startArgPos(num);
uint8_t endPos = endArgPos(num);
{
NMEAParserStringify stfy(this, endPos);
arg = atoi(&mBuffer[startPos]);
}
return true;
}
else return false;
}
bool getArg(uint8_t num, float &arg)
{
if (validArgNum(num)) {
uint8_t startPos = startArgPos(num);
uint8_t endPos = endArgPos(num);
{
NMEAParserStringify stfy(this, endPos);
arg = atof(&mBuffer[startPos]);
}
return true;
}
else return false;
}
/*
* Returns the type of sentence.
*/
bool getType(char *arg)
{
if (mIndex > 0) {
uint8_t endPos = startArgPos(0);
{
NMEAParserStringify stfy(this, endPos);
strncpy(arg, mBuffer, 5);
arg[5] = '\0';
}
return true;
}
else return false;
}
#ifdef ARDUINO
bool getType(String &arg)
{
if (mIndex > 0) {
uint8_t endPos = startArgPos(0);
{
NMEAParserStringify stfy(this, endPos);
arg = mBuffer;
}
return true;
}
else return false;
}
#endif
bool getType(uint8_t inIndex, char &outTypeChar)
{
if (mIndex > 0) {
uint8_t endPos = startArgPos(0);
if (inIndex < endPos) {
outTypeChar = mBuffer[inIndex];
return true;
}
else return false;
}
else return false;
}
NMEA::ErrorCode error() {
return mError;
}
void setHandleCRC(bool inHandleCRC)
{
mHandleCRC = inHandleCRC;
}
#ifdef __amd64__
void printBuffer()
{
{
NMEAParserStringify stfy(this, startArgPos(0));
printf("%s\n", mBuffer);
}
for (uint8_t i = 0; i < argCount(); i++) {
uint8_t startPos = startArgPos(i);
uint8_t endPos = endArgPos(i);
{
NMEAParserStringify stfy(this, endPos);
printf("%s\n", &mBuffer[startPos]);
}
}
}
#endif
};
#endif

View File

@ -0,0 +1,17 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
custom: ['paypal.me/tilz0R']

View File

@ -0,0 +1,385 @@
#Build Keil files
*.rar
*.o
*.d
*.crf
*.htm
*.dep
*.map
*.bak
*.axf
*.lnp
*.lst
*.ini
*.scvd
*.iex
*.sct
*.MajerleT
*.tjuln
*.tilen
*.dbgconf
*.uvguix
*.uvoptx
*.__i
*.i
!docs/*.txt
RTE/
# IAR Settings
**/settings/*.crun
**/settings/*.dbgdt
**/settings/*.cspy
**/settings/*.cspy.*
**/settings/*.xcl
**/settings/*.dni
**/settings/*.wsdt
**/settings/*.wspos
# IAR Debug Exe
**/Exe/*.sim
# IAR Debug Obj
**/Obj/*.pbd
**/Obj/*.pbd.*
**/Obj/*.pbi
**/Obj/*.pbi.*
*.TMP
/docs_src/x_Doxyfile.doxy
.DS_Store
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
_build/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
*.out
*.sim
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# TypeScript v1 declaration files
typings/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
log_file.txt
.metadata/
.mxproject
.settings/
project.ioc
mx.scratch
*.tilen majerle
*.exe

View File

@ -0,0 +1,5 @@
" tree-specific vimrc to comply with project coding style
" see https://github.com/MaJerle/c-code-style
if &ft == "c" || &ft == "cpp"
setlocal shiftwidth=4 tabstop=4 softtabstop=4 expandtab autoindent cc=80 foldmethod=indent
endif

View File

@ -0,0 +1,39 @@
################################################################################
# Lightweight GPS (lwgps) CMake support
################################################################################
# The lwgps library can be configured with a lwgps_opts.h file.
# If such a file is used, the user should set the LWGPS_CONFIG_PATH path variable where
# the configuration file is located so that is is included properly.
#
# Other than that, only two steps are necessary to compile and link against LWGPS:
# 1. Use add_subdirectory to add the lwgps folder
# 2. Link against lwgps with target_link_libraries
################################################################################
cmake_minimum_required(VERSION 3.13)
set(LIB_LWGPS_NAME lwgps)
add_library(${LIB_LWGPS_NAME})
add_subdirectory(${LIB_LWGPS_NAME})
add_subdirectory(examples)
# The project CMakeLists can set a LWGPS_CONFIG_PATH path including the lwgps_opts.h file
# and add it.
if(NOT LWGPS_CONFIG_PATH)
message(STATUS "Lightweight GPS configuration path not set.")
endif()
# Extract the absolute path of the provided configuration path
if(IS_ABSOLUTE ${LWGPS_CONFIG_PATH})
set(LWGPS_CONFIG_PATH_ABSOLUTE ${LWGPS_CONFIG_PATH})
else()
get_filename_component(LWGPS_CONFIG_PATH_ABSOLUTE
${LWGPS_CONFIG_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR}
)
endif()
target_include_directories(${LIB_LWGPS_NAME} PRIVATE
${LWGPS_CONFIG_PATH_ABSOLUTE}
)

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Tilen MAJERLE
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,40 @@
# Lightweight GPS NMEA parser
Platform independent GPS NMEA parser for embedded systems.
<h3>Read first: <a href="http://docs.majerle.eu/projects/lwgps/">Documentation</a></h3>
## Features
* Written in ANSI C99
* Platform independent, easy to use
* Built-in support for 4 GPS statements
* ``GPGGA`` or ``GNGGA``: GPS fix data
* ``GPGSA`` or ``GNGSA``: GPS active satellites and dillusion of position
* ``GPGSV`` or ``GNGSV``: List of satellites in view zone
* ``GPRMC`` or ``GNRMC``: Recommended minimum specific GPS/Transit data
* Optional ``float`` or ``double`` floating point units
* Low-level layer is separated from application layer, thus allows you to add custom communication with GPS device
* Works with operating systems
* Works with different communication interfaces
* User friendly MIT license
## Contribute
Fresh contributions are always welcome. Simple instructions to proceed::
1. Fork Github repository
2. Respect [C style & coding rules](https://github.com/MaJerle/c-code-style) used by the library
3. Create a pull request to develop branch with new features or bug fixes
Alternatively you may:
1. Report a bug
2. Ask for a feature request
## Test
To build the code and run basic tests on your host::
cd examples
make test

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.452
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lwgps_dev", "lwgps_dev.vcxproj", "{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}.Debug|x64.ActiveCfg = Debug|Win32
{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}.Debug|x64.Build.0 = Debug|Win32
{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}.Debug|x86.ActiveCfg = Debug|Win32
{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}.Debug|x86.Build.0 = Debug|Win32
{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}.Release|x64.ActiveCfg = Release|x64
{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}.Release|x64.Build.0 = Release|x64
{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}.Release|x86.ActiveCfg = Release|Win32
{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C58E98E3-45C5-48F1-A7AF-BCFEC103DD09}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{C7C465FB-17B3-4226-BBD5-E79C1B3796C6}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>LwGPS</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>.;..\..\lwgps\src\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\examples\test_code.c" />
<ClCompile Include="..\..\lwgps\src\lwgps\lwgps.c" />
<ClCompile Include="main.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Source Files\LwGPS">
<UniqueIdentifier>{d2afbed4-545f-40e7-bf68-d835c5948bcc}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\lwgps\src\lwgps\lwgps.c">
<Filter>Source Files\LwGPS</Filter>
</ClCompile>
<ClCompile Include="main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\examples\test_code.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,44 @@
/**
* \file lwgps_opts_template.h
* \brief LwGPS configuration file
*/
/*
* Copyright (c) 2020 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of LwGPS - Lightweight GPS NMEA parser library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: $2.1.0$
*/
#ifndef LWGPS_HDR_OPTS_H
#define LWGPS_HDR_OPTS_H
/* Rename this file to "lwgps_opts.h" for your application */
/*
* Open "include/lwgps/lwgps_opt.h" and
* copy & replace here settings you want to change values
*/
#endif /* LWGPS_HDR_OPTS_H */

View File

@ -0,0 +1,32 @@
/*
* This example uses direct processing function,
* to process dummy NMEA data from GPS receiver
*/
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "lwgps/lwgps.h"
/* External function */
extern void run_tests();
int
main() {
lwgps_float_t distance, bearing;
run_tests();
/* Calculate distance and bearing */
lwgps_distance_bearing(40.6, -73.7, 48.3, 11.7, &distance, &bearing);
printf("Distance: %lf meters\r\n", (double)distance);
printf("Bearing: %lf degrees\r\n", (double)bearing);
lwgps_distance_bearing(48.3, 11.7, 40.6, -73.7, &distance, &bearing);
printf("Distance: %lf meters\r\n", (double)distance);
printf("Bearing: %lf degrees\r\n", (double)bearing);
return 0;
}
/* JFK: 40.642569, -73.783790 */
/* Munich: 48.353962, 11.775114 */

View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -0,0 +1,6 @@
.. _api_lwgps:
LwGPS
=====
.. doxygengroup:: LWGPS

View File

@ -0,0 +1,12 @@
.. _api_lwgps_opt:
Configuration
=============
This is the default configuration of the middleware.
When any of the settings shall be modified, it shall be done in dedicated application config ``lwgps_opts.h`` file.
.. note::
Check :ref:`getting_started` to create configuration file.
.. doxygengroup:: LWGPS_OPT

View File

@ -0,0 +1,13 @@
.. _api_reference:
API reference
=============
List of all the modules:
.. toctree::
:maxdepth: 2
:glob:
*

View File

@ -0,0 +1,129 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
from sphinx.builders.html import StandaloneHTMLBuilder
import subprocess, os
# Run doxygen first
# read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True'
# if read_the_docs_build:
subprocess.call('doxygen doxyfile.doxy', shell=True)
# -- Project information -----------------------------------------------------
project = 'LwGPS'
copyright = '2020, Tilen MAJERLE'
author = 'Tilen MAJERLE'
# The full version, including alpha/beta/rc tags
version = 'v2.1.0'
# Try to get branch at which this is running
# and try to determine which version to display in sphinx
git_branch = ''
res = os.popen('git branch').read().strip()
for line in res.split("\n"):
if line[0] == '*':
git_branch = line[1:].strip()
# Decision for display version
try:
if git_branch.index('develop') >= 0:
version = "latest-develop"
except Exception:
print("Exception for index check")
# For debugging purpose
print("GIT BRANCH: " + git_branch)
print("VERSION: " + version)
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.autosectionlabel',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
'sphinx.ext.ifconfig',
'sphinx.ext.viewcode',
'sphinx_sitemap',
'breathe',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
highlight_language = 'c'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_theme_options = {
'canonical_url': '',
'analytics_id': '', # Provided by Google in your dashboard
'display_version': True,
'prev_next_buttons_location': 'bottom',
'style_external_links': False,
'logo_only': False,
# Toc options
'collapse_navigation': True,
'sticky_navigation': True,
'navigation_depth': 4,
'includehidden': True,
'titles_only': False
}
html_logo = 'static/images/logo.svg'
github_url = 'https://github.com/MaJerle/lwgps'
html_baseurl = 'https://docs.majerle.eu/projects/lwgps/'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['static']
html_css_files = [
'css/common.css',
'css/custom.css',
]
html_js_files = [
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css'
]
master_doc = 'index'
#
# Breathe configuration
#
#
#
breathe_projects = {
"lwgps": "_build/xml/"
}
breathe_default_project = "lwgps"
breathe_default_members = ('members', 'undoc-members')

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
.. _examples:
Examples and demos
==================
There are several basic examples provided with the library.
Parse block of data
^^^^^^^^^^^^^^^^^^^
In this example, block of data is prepared as big string array and sent to processing function in single shot.
Application can then check if GPS signal has been detected as valid and use other data accordingly.
.. literalinclude:: ../../examples/example.c
:language: c
:linenos:
:caption: Minimum example code
Parse received data from interrupt/DMA
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Second example is a typical use case with interrupts on embedded systems.
For each received character, application uses ``ringbuff`` as intermediate buffer.
Data are later processed outside interrupt context.
.. note::
For the sake of this example, application *implements* interrupts as function call in *while loop*.
.. literalinclude:: ../../examples/example_buff.c
:language: c
:linenos:
:caption: Example of buffer
Distance and bearing
^^^^^^^^^^^^^^^^^^^^
Library provides calculation of distance and bearing between ``2`` coordinates on earth.
This is useful if used with autonomnous devices to understand in which direction
device has to move to reach end point while knowing start coordinate.
.. literalinclude:: ../../examples/example_dist_bear.c
:language: c
:linenos:
:caption: Distance and bearing calculation
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,100 @@
.. _getting_started:
Getting started
===============
.. _download_library:
Download library
^^^^^^^^^^^^^^^^
Library is primarly hosted on `Github <https://github.com/MaJerle/lwgps>`_.
* Download latest release from `releases area <https://github.com/MaJerle/lwgps/releases>`_ on Github
* Clone `develop` branch for latest development
Download from releases
**********************
All releases are available on Github `releases area <https://github.com/MaJerle/lwgps/releases>`_.
Clone from Github
*****************
First-time clone
""""""""""""""""
* Download and install ``git`` if not already
* Open console and navigate to path in the system to clone repository to. Use command ``cd your_path``
* Clone repository with one of available ``3`` options
* Run ``git clone --recurse-submodules https://github.com/MaJerle/lwgps`` command to clone entire repository, including submodules
* Run ``git clone --recurse-submodules --branch develop https://github.com/MaJerle/lwgps`` to clone `development` branch, including submodules
* Run ``git clone --recurse-submodules --branch master https://github.com/MaJerle/lwgps`` to clone `latest stable` branch, including submodules
* Navigate to ``examples`` directory and run favourite example
Update cloned to latest version
"""""""""""""""""""""""""""""""
* Open console and navigate to path in the system where your resources repository is. Use command ``cd your_path``
* Run ``git pull origin master --recurse-submodules`` command to pull latest changes and to fetch latest changes from submodules
* Run ``git submodule foreach git pull origin master`` to update & merge all submodules
.. note::
This is preferred option to use when you want to evaluate library and run prepared examples.
Repository consists of multiple submodules which can be automatically downloaded when cloning and pulling changes from root repository.
Add library to project - Generic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
At this point it is assumed that you have successfully download library, either cloned it or from releases page.
* Copy ``lwgps`` folder to your project
* Add ``lwgps/src/include`` folder to `include path` of your toolchain
* Add source files from ``lwgps/src/`` folder to toolchain build
* Copy ``lwgps/src/include/lwgps/lwgps_opts_template.h`` to project folder and rename it to ``lwgps_opts.h``
* Build the project
Add library to project - CMake
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Including the library with CMake is very easy.
* Add the ``lwgps`` folder with ``add_subdirectory``
* Copy ``lwgps/src/include/lwgps/lwgps_opts_template.h`` to the project folder and rename it
to ``lwgps_opts.h``
* Set the ``LWGPS_CONFIG_PATH`` path variable containing the ``lwgps_opts.h`` file
in the project ``CMakeLists.txt``
* Link your project against the ``lwgps`` library with ``target_link_libraries``
* The include directory should be set automatically by CMake
Configuration file
^^^^^^^^^^^^^^^^^^
Library comes with template config file, which can be modified according to needs.
This file shall be named ``lwgps_opts.h`` and its default template looks like the one below.
.. note::
Default configuration template file location: ``lwgps/src/include/lwgps/lwgps_opts_template.h``.
File must be renamed to ``lwgps_opts.h`` first and then copied to the project directory (or simply renamed in-place) where compiler
include paths have access to it by using ``#include "lwgps_opts.h"``.
.. tip::
Check :ref:`api_lwgps_opt` section for possible configuration settings
.. literalinclude:: ../../lwgps/src/include/lwgps/lwgps_opts_template.h
:language: c
:linenos:
:caption: Template options file
Minimal example code
^^^^^^^^^^^^^^^^^^^^
Run below example to test and verify library
.. literalinclude:: ../../examples/example.c
:language: c
:linenos:
:caption: Test verification code

View File

@ -0,0 +1,71 @@
LwGPS |version| documentation
=============================
Welcome to the documentation for version |version|.
LwGPS is lightweight, platform independent library to parse NMEA statements from GPS receivers. It is highly optimized for embedded systems.
.. image:: static/images/logo.svg
:align: center
.. rst-class:: center
.. rst-class:: index_links
:ref:`download_library` :ref:`getting_started` `Open Github <https://github.com/MaJerle/lwgps>`_ `Donate <https://paypal.me/tilz0R>`_
Features
^^^^^^^^
* Written in ANSI C99
* Platform independent, easy to use
* Built-in support for 4 GPS statements
* ``GPGGA`` or ``GNGGA``: GPS fix data
* ``GPGSA`` or ``GNGSA``: GPS active satellites and dillusion of position
* ``GPGSV`` or ``GNGSV``: List of satellites in view zone
* ``GPRMC`` or ``GNRMC``: Recommended minimum specific GPS/Transit data
* Optional ``float`` or ``double`` floating point units
* Low-level layer is separated from application layer, thus allows you to add custom communication with GPS device
* Works with operating systems
* Works with different communication interfaces
* User friendly MIT license
Requirements
^^^^^^^^^^^^
* C compiler
* Driver for receiving data from GPS receiver
* Few *kB* of non-volatile memory
Contribute
^^^^^^^^^^
Fresh contributions are always welcome. Simple instructions to proceed:
#. Fork Github repository
#. Respect `C style & coding rules <https://github.com/MaJerle/c-code-style>`_ used by the library
#. Create a pull request to ``develop`` branch with new features or bug fixes
Alternatively you may:
#. Report a bug
#. Ask for a feature request
License
^^^^^^^
.. literalinclude:: ../LICENSE
Table of contents
^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
self
get-started/index
user-manual/index
api-reference/index
examples/index

View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View File

@ -0,0 +1,8 @@
breathe>=4.9.1
colorama
docutils>=0.14
sphinx>=2.0.1
sphinx_rtd_theme
sphinx-tabs
sphinxcontrib-svg2pdfconverter
sphinx-sitemap

View File

@ -0,0 +1,64 @@
/* Center aligned text */
.center {
text-align: center;
}
/* Paragraph with main links on index page */
.index-links {
text-align: center;
margin-top: 10px;
}
.index-links a {
display: inline-block;
border: 1px solid #0E4263;
padding: 5px 20px;
margin: 2px 5px;
background: #2980B9;
border-radius: 4px;
color: #FFFFFF;
}
.index-links a:hover, .index-links a:active {
background: #0E4263;
}
/* Table header p w/0 margin */
.index-links a table thead th {
vertical-align: middle;
}
table thead th p {
margin: 0;
}
.table-nowrap td {
white-space: normal !important;
}
/* Breathe output changes */
.breathe-sectiondef.container {
background: #f9f9f9;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #efefef;
}
.breathe-sectiondef.container .breathe-sectiondef-title {
background: #2980b9;
color: #FFFFFF;
padding: 4px;
margin: -10px -10px 0 -10px;
}
.breathe-sectiondef.container .function,
.breathe-sectiondef.container .member,
.breathe-sectiondef.container .class,
.breathe-sectiondef.container .type {
border-bottom: 1px solid #efefef;
}
.breathe-sectiondef.container .function:last-child,
.breathe-sectiondef.container .member:last-child,
.breathe-sectiondef.container .class:last-child,
.breathe-sectiondef.container .type:last-child {
border-bottom: none;
margin-bottom: 0;
}
/*# sourceMappingURL=common.css.map */

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1 @@
<mxfile modified="2019-03-17T18:59:38.494Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/7.8.7 Chrome/58.0.3029.110 Electron/1.7.5 Safari/537.36" version="10.4.9" etag="K-7s2_uXvQkXh2xx6gpu" type="device"><diagram id="7c61e978-b8e5-555b-ca9a-a1ec85429089" name="Page-1">7Ztfb5swFMU/TR4n2RgIPC5d1z5s0rR06rMDJlgBHDlOk+zTz4BpSM2kaupyq6vmITXXf8C/wwPn0MzYTX2803xbfle5qGYByY8z9mUWBDQkzP5pK6e+ktB5X1hrmbtB58JS/hauSFx1L3OxuxholKqM3F4WM9U0IjMXNa61OlwOK1R1edYtXwuvsMx45VcfZW5KV6Vxeu64F3JdulMngdvfimebtVb7xp1vFrCi+/TdNR/WchvdlTxXh1GJ3c7YjVbK9K36eCOqlu2ArZ/39S+9z9etRWNeMyHoJzzxau+2vnxwV2ZOA41uP6KdQWZscSilEcstz9reg9Xf1kpTV/aI2mbFV6JaPGO4UZXStqtRjR2/KFRjnNw0cMfDEMuKMWI/tu7vw23tSWgjjqOS29edULUw+mSHuF4W9TPcLRikDvnhLChzpXIkZehq3N1C6+eFzxhtw5Gcpso8qiuChGoMRzX0qVIkVFM4qrFPNUBClQZwWOc+VoYFawSHNfGxhliwJnBYUx9rhASrvV3BsA6Ps2OuMRauISBX6nOdY+E6B+Q6YQd+IOHKCCBX3xDgsVkJgeM6YQmwGK2QAnKNfK5YrFYYAnJFbLbCOSBXxG4rIoBcEdutiAFyRey3ohiO65CqY/RbUQrIFbHfigNAroj9VhwBcvX91uJkhK2oxn79+vzTui9SiafutWU3gKykGfrpp0epxXlEXNlrW6y0ba3b1oOsZbNuz7frkbTblvVWacMtr8BeH7l/tF+Gb0Q7JOPdcqpoxxl/QSSSv3jjxtiU4v9Lct8Kfkh+ffd/Xc19m7osrSKtDLIW7xN6IatqVM8jkeShre+MVhsx6kmsh4njN5LpFS/Dhzx3rFLyFir5phdN+JVAPjxMmF4s4VcC+fAwYXqxhF8JYFg79ZIRS/iVAoa1w7oYw68UMKxlE6YXS/iVAoa1zDe9aMIvSgDTWjbxb4dY0i9KAONaNvGaEUv8RQlgXssmDByW/IsSwMCW+Z7rIw25vh28ahrCfD/4TXUq9WFIK8pOtS0iGyO03m87wa0IJJdFIbqNt5rtNoO0K9HrLI4i2xurx7tU7mWkwkVSZFORSpwlYlW8Ub79CitFwwmt/yFTsYfnn4R0faPf3bDbPw==</diagram></mxfile>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -0,0 +1 @@
<mxfile modified="2019-03-17T18:28:51.770Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/7.8.7 Chrome/58.0.3029.110 Electron/1.7.5 Safari/537.36" etag="Lw4IH-C7q6o9UbXhWxg1" version="10.4.9" type="device"><diagram id="oLfy0yLwNNLmw2rXhwVB" name="Page-1">7VpNc9owEP01HMnYkmXsYyBpy6Fpm5CU9tIRtjCaGIsRopD++sq2DJZlkuCYkHaSQ0a7+rL2vd2VNunAwXzzkePF7DMLSdwBVrjpwIsOAD2/J3+niodcgaCfKyJOw1xl7xQ39A9R45RyRUOy1MYJxmJBF7oyYElCAqHpMOdsrQ+bsljfdIEjYihuAhyb2u80FDOltV1/1/GJ0GimtvaAOu8cF4OtXLGc4ZCtSyp42YEDzpjIW/PNgMSp6Qqz5PM+7OndfhgniXjOhC+Mrn6Ovv2KRugD/0a7v4bTUVet8hvHK3Xgz4Nbqfhy3QFuLNftT7hsRWnr9qbfSfdw8Xwhxay3uxWzIbL/9vx6pA4sHgorrmdUkJsFDlJ5LXkix87EPJaSLZt4ucixm9INkZ/bn9I4HrCY8Ww6nHoBCQKpXwrO7kmpZ+IhB1nZzlVrFEcjXJBNSaWs85GwORH8QQ4pei2FlGIq8JS8LgFfjJmVMN8qsSJbtF17h4dsKEgOgMcz4DFMS5LwPOW5lBKWEN2wZEPFuNT+IdvWmfStJibOewovAI8ZfclWPCBP846EmveZ0JRMj2osX+g4ibGgv3WfrUND7fCVUfnFO+Q9pCEPLaQvkZ9HzSr7WGWhrbPvW0hgHhFhLJSRY3vs5nyxTX82CMPZKglTL7tIQX3CM0/iiNKhdEe0TUcEj7ChdTcsNmvuh4n8jHHmfKgQfzQ28QGOmPPtGYx5I55YdSDgVzBt6onAfV1PBH5LoVsxpgjejQiTzr7YqOmZ8KCEA6hUYchzgvxpqWT7Zz3kucByPd9FyAY6ISxwBm0LOo68sqXDvHaIZnvgVYkGzdh0NxiYtzeDfTIYC51y+wm0j3I4plEidYEkC5Gd/TTIU3l7PlcdcxqG6Ya1eUbPRC0kDtvX87jdMxNHHePAsRIHfEY+fjQKHDU5vNWY7zWM+Q56YqFjuyJ4R/tgtCFq6a796mjDljK8/jjrvT/Oqo8zHWcHtnQlNJh3bMIggzB29/vw+tJM1heXd8NBjd4g2KGFlRhPSNzHwX2UJd+CSYqcp3ntuRU/tsyk7dbQy20haUcArybjcLwZXlm3V5zeB5NJ97+FycCkBrm9MEHnjcHkvsNUA5NTzYqnhqn3wixpJrbHiimZsHvqZlLx1m2Kh/HwbVJnyYP/yS5dfktXbGhXK6VHu3TVsumlBfF/gU2nIgnstUWS6kLIP/NKP/6rUualhbhTUmZXIUnXVoYinEqbEJ4mKppEnXL55O2y68nHeeNX/knZVfMXl9FYyl+HVwbNzIpb+/WzF10ebKRHd1DU00oUcZBJkTbqZ/XWNSsq1/+NdaFzNOtKcfePBTnVd/+cAS//Ag==</diagram></mxfile>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1 @@
<mxfile modified="2019-03-17T18:24:31.030Z" host="www.draw.io" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/7.8.7 Chrome/58.0.3029.110 Electron/1.7.5 Safari/537.36" version="10.4.9" etag="GRhl1qzylYBum7KQYHDi" type="device"><diagram id="85174833-9250-f023-2320-fd733ea284ef" name="Page-1">7Vxdd6I6FP01PraLBMLHY9V2pg8zd1Y7HWceKUTNGgQH01Hvr78BgkKigsiHt8WXkgMEOHvvk5ND6EAdLTafQns5/xK42BtAxd0M1PEAQqApKvsTWbaJxQRGYpiFxOUH7Q3P5F+cGBE3vhEXr3LH0SDwKFnmjU7g+9ihOZsdhsE6f9g08PIXXdozLBmeHduTrRPi0jl/CKTs7Z8xmc3TKwOF73m1nd+zMHjz+fUGUJ3Gv2T3wk774sev5rYbrDMm9X6gjsIgoMnWYjPCXuTa1GvJeQ9H9u7uO8Q+LXOCNRk++KOvD5//jP3J2Jj88TXnBqT9/LW9N5w+SHy7dJu6CPvuXeRp1vIDnxmHc7rwWAuwTbwh9CfbVm4Rb/2KWmx7SjxvFHhBGPeiTk0HOw6zr2gY/MaZPa8m0pDCzx5v+OlxY8sbyTkpQMx3w+QusSuhu/cH2HmZkRcHC0zDLTtkvYc5RXmeQTi1hdizKfmb797mbJvtuttd4VtA2IWhwoUBLU5wLgtgqvkuVsFb6GB+Vha3oo4QyndE7XCGqdQR28g89t4U0+IMiiBdYsiX0Qsz/PM0gLrHvDZ8DdnWLNp6eWbAMAfq9mLJmvHem10zPoTtf7l7+i6xbD0nFD8vbSdqr1msyfPMXi0T+U/JBruV6MWfA4cUb84lDT9BwAIanCwZTu0CRJZUO+MhAuUQOxseoyYBg6x8byGq5OLqOmU3HAuikIldCRqYSh57S9BhaUGr6HRHTQvaLGZMPK5FMoujb4E0O1Ei1MT4KksRnmBD7ULUlXc+kpZRqNnpkIu0WwOZOlR009IRAjDPEAPeqkBRNc2E8WFmRf0icUCGrepXl1O2H6ORPBZL7GNio3nKHSfQMcrZHpn5zOYwbmC2cxiJmLB0+o7vWBDXjS54MGjkw0oTUUCTo4B+gHGwsSigNTIcG/1wLA7HRh55TYS0rJxVkO9I1dodjnUkMQbcTB6f7mVFj+9/PI4O2CWGnZtLe/Yr9oa7CW1KJc7OTsZ3VciSwIFU+5Cy9caULc+DepyY7sC14VQivT0ZgeWgyVwRbjOpWdT8lcboqLFPteJWmmtVBaRMeE6iUmFc6So8q7owyRHz7tLh2RKmXYbQUdPh2foAdOqKJZpWE0ukjqB1a2Z+VqucMeQM/ftP1v72+FXijpyUX1mKDZAgQBNJAT71fisptgEl7z69G+/u0p6uvGvJ3j0v3rU5S+ms0qAJqInl3dK1BKEjKS1qOFJZao/22WhXr/yibiu/1qXFiQ+BtjDxrK5tsc7ftrblwkKPdnEArkvbYkdNoy2XJ06h7Xj2akWcDwi4iJNZ19AtdtQ04HW9+E1fESWV5vTd0+G3R1dWggaKcU1cqj5UFJGyYS4Bpc8MSsAt1rQq54FGt3kgUPrUoALcVYtTkrrFjhqH+7zcoIf7MnUX8aZxuEtkBjQktj87WufKYO+SEDuUBFFtjLkgQqJGPtT69l8F5dYAAeU4OS4qngFw6XLaD6G0oqlS5cCqtqy0dIVRD/cpuIWFk9WzZHEFZssFFQD6aun5cFcfR8XF1m1nyaCfFJWA26hrDixOilpXdz8pqqDuymO3qO6W66UA9AXTcoiLQFUumIrUabvIBfqKKXNCt0v0rbpGiyJWNk6mEssM38ck27y6SXaJJXn/gw+YBL92/gETSK/WJ0DnDIe1JUBtFy/gpbNZGbz3t0JVQqm2FaptV4VhP5stsWy96GOgql8V1Qc3a+7/aURy+P4fc6j3/wE=</diagram></mxfile>

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2020-12-05T00:17:49.072Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.3.2 Chrome/78.0.3904.113 Electron/7.1.2 Safari/537.36" etag="J3RPSOEyywhlMJWRwdgH" version="12.3.2" type="device" pages="1"><diagram id="Wsjaadh77UIxB9X1bxos" name="Page-1">jZJNb8MgDIZ/TY6VkpB067Vd10nrqkk59IyCG1AhRJSMdL9+ZDH5UDVpJ/BjY+B9HZGd6g6GNvxDM5BRGrMuIi9RmibxU+aXntwHssnzAVRGMCyaQCG+IZxE2goGt0Wh1Vpa0SxhqesaSrtg1BjtlmUXLZe3NrSCB1CUVD7Ss2CWD/Q5jyf+BqLi4eYkxoyioRjBjVOm3QyRfUR2Rms77FS3A9mLF3QZzr3+kR0fZqC2/zlwWrEMNsodT6rdZpv3izwnK+zyRWWLHz66w2eBL7b3IIPRbc2g75REZOu4sFA0tOyzzhvvGbdKYvpmjb6Ocq09uejaorekj0ctYh88fiS8CoyFbobwYwfQCqy5+xLMkgynCqcsDbGbPEvWaASf+RV8pDgm1dh6UtJvUMwQTqb95majT/Y/</diagram></mxfile>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="166px" height="56px" viewBox="-0.5 -0.5 166 56" content="&lt;mxfile host=&quot;Electron&quot; modified=&quot;2020-12-05T00:17:54.452Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.3.2 Chrome/78.0.3904.113 Electron/7.1.2 Safari/537.36&quot; etag=&quot;QrTFHCUJlsVW3_ulGR16&quot; version=&quot;12.3.2&quot; type=&quot;device&quot; pages=&quot;1&quot;&gt;&lt;diagram id=&quot;Wsjaadh77UIxB9X1bxos&quot; name=&quot;Page-1&quot;&gt;jZJNb8MgDIZ/TY6VkpB067Vd10nrqkk59IyCG1AhRJSMdL9+ZDH5UDVpJ/BjY+B9HZGd6g6GNvxDM5BRGrMuIi9RmibxU+aXntwHssnzAVRGMCyaQCG+IZxE2goGt0Wh1Vpa0SxhqesaSrtg1BjtlmUXLZe3NrSCB1CUVD7Ss2CWD/Q5jyf+BqLi4eYkxoyioRjBjVOm3QyRfUR2Rms77FS3A9mLF3QZzr3+kR0fZqC2/zlwWrEMNsodT6rdZpv3izwnK+zyRWWLHz66w2eBL7b3IIPRbc2g75REZOu4sFA0tOyzzhvvGbdKYvpmjb6Ocq09uejaorekj0ctYh88fiS8CoyFbobwYwfQCqy5+xLMkgynCqcsDbGbPEvWaASf+RV8pDgm1dh6UtJvUMwQTqb95majT/Y/&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><rect x="3" y="3" width="160" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" stroke-width="6" pointer-events="all"/><g transform="translate(21.5,6.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="122" height="41" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 36px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 123px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;">LwGPS</div></div></foreignObject><text x="61" y="39" fill="#000000" text-anchor="middle" font-size="36px" font-family="Helvetica">LwGPS</text></switch></g></g></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,15 @@
.. _float_double:
Float/double precision
======================
With configuration of ``GSM_CFG_DOUBLE``, it is possible to enable ``double`` floating point precision.
All floating point variables are then configured in *double precision*.
When configuration is set to ``0``, floating point variables are configured in *single precision* format.
.. note::
Single precision uses less memory in application. As a drawback, application may be a subject of data loss at latter digits.
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,38 @@
.. _how_it_works:
How it works
============
LwGPS parses raw data formatted as NMEA 0183 statements from GPS receivers. It supports up to ``4`` different statements:
* ``GPGGA`` or ``GNGGA``: GPS fix data
* ``GPGSA`` or ``GNGSA``: GPS active satellites and dillusion of position
* ``GPGSV`` or ``GNGSV``: List of satellites in view zone
* ``GPRMC`` or ``GNRMC``: Recommended minimum specific GPS/Transit data
.. tip::
By changing different configuration options, it is possible to disable some statements.
Check :ref:`api_lwgps_opt` for more information.
Application must assure to properly receive data from GPS receiver.
Usually GPS receivers communicate with host embedded system with UART protocol and output directly formatted NMEA 0183 statements.
.. note::
Application must take care of properly receive data from GPS.
Application must use :cpp:func:`lwgps_process` function for data processing. Function will:
* Detect statement type, such as *GPGGA* or *GPGSV*
* Parse all the terms of specific statement
* Check valid CRC after each statement
Programmer's model is as following:
* Application receives data from GPS receiver
* Application sends data to :cpp:func:`lwgps_process` function
* Application uses processed data to display altitude, latitude, longitude, and other parameters
Check :ref:`examples` for typical example
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,12 @@
.. _um:
User manual
===========
.. toctree::
:maxdepth: 2
how-it-works
float-double
thread-safety
tests

View File

@ -0,0 +1,14 @@
.. _tests:
Tests during development
========================
During the development, test check is performed to validate raw NMEA input data vs expected result.
.. literalinclude:: ../../examples/test_code.c
:language: c
:linenos:
:caption: Test code for development
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,16 @@
.. _thread_safety:
Thread safety
=============
Library tends to be as simple as possible.
No specific features have been implemented for thread safety.
When library is using multi-thread environment and if multi threads tend to access to shared resources,
user must resolve it with care, using mutual exclusion.
.. tip::
When single thread is dedicated for GPS processing, no special mutual exclusion is necessary.
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,40 @@
# gps-nmea-parser examples and tests Makefile
# (c) 2020 Sirio, Balmelli Analog & Digital
TARGETS := \
example.exe \
example_stat.exe \
test_code.exe \
test_time.exe
.PHONY: all clean test
all: $(TARGETS)
clean:
@rm -fv $(TARGETS)
test: $(TARGETS)
@for tgt in $(TARGETS); do \
echo "\n--- $$tgt ---"; \
./$$tgt; \
done
CFLAGS += -Wall \
-DDEBUG=1 \
-I../lwgps/src/include \
-I./
example.exe: example.c
example_stat.exe: CFLAGS += -DLWGPS_CFG_STATUS=1
example_stat.exe: example_stat.c
test_code.exe: ../lwgps/src/lwgps/lwgps.c test_code.c ../dev/VisualStudio/main.c
test_time.exe: CFLAGS += \
-DLWGPS_CFG_STATEMENT_PUBX=1 \
-DLWGPS_CFG_STATEMENT_PUBX_TIME=1
test_time.exe: ../lwgps/src/lwgps/lwgps.c test_time.c ../dev/VisualStudio/main.c
$(TARGETS) : ../lwgps/src/lwgps/lwgps.c
$(CC) -o $@ $(CFLAGS) $^

View File

@ -0,0 +1,47 @@
/**
* This example uses direct processing function
* to process dummy NMEA data from GPS receiver
*/
#include <string.h>
#include <stdio.h>
#include "lwgps/lwgps.h"
/* GPS handle */
lwgps_t hgps;
/**
* \brief Dummy data from GPS receiver
*/
const char
gps_rx_data[] = ""
"$GPRMC,183729,A,3907.356,N,12102.482,W,000.0,360.0,080301,015.5,E*6F\r\n"
"$GPRMB,A,,,,,,,,,,,,V*71\r\n"
"$GPGGA,183730,3907.356,N,12102.482,W,1,05,1.6,646.4,M,-24.1,M,,*75\r\n"
"$GPGSA,A,3,02,,,07,,09,24,26,,,,,1.6,1.6,1.0*3D\r\n"
"$GPGSV,2,1,08,02,43,088,38,04,42,145,00,05,11,291,00,07,60,043,35*71\r\n"
"$GPGSV,2,2,08,08,02,145,00,09,46,303,47,24,16,178,32,26,18,231,43*77\r\n"
"$PGRME,22.0,M,52.9,M,51.0,M*14\r\n"
"$GPGLL,3907.360,N,12102.481,W,183730,A*33\r\n"
"$PGRMZ,2062,f,3*2D\r\n"
"$PGRMM,WGS84*06\r\n"
"$GPBOD,,T,,M,,*47\r\n"
"$GPRTE,1,1,c,0*07\r\n"
"$GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67\r\n"
"$GPRMB,A,,,,,,,,,,,,V*71\r\n";
int
main() {
/* Init GPS */
lwgps_init(&hgps);
/* Process all input data */
lwgps_process(&hgps, gps_rx_data, strlen(gps_rx_data));
/* Print messages */
printf("Valid status: %d\r\n", hgps.is_valid);
printf("Latitude: %f degrees\r\n", hgps.latitude);
printf("Longitude: %f degrees\r\n", hgps.longitude);
printf("Altitude: %f meters\r\n", hgps.altitude);
return 0;
}

View File

@ -0,0 +1,81 @@
#include "lwgps/lwgps.h"
#include "lwrb/lwrb.h"
#include <string.h>
/* GPS handle */
lwgps_t hgps;
/* GPS buffer */
lwrb_t hgps_buff;
uint8_t hgps_buff_data[12];
/**
* \brief Dummy data from GPS receiver
* \note This data are used to fake UART receive event on microcontroller
*/
const char
gps_rx_data[] = ""
"$GPRMC,183729,A,3907.356,N,12102.482,W,000.0,360.0,080301,015.5,E*6F\r\n"
"$GPRMB,A,,,,,,,,,,,,V*71\r\n"
"$GPGGA,183730,3907.356,N,12102.482,W,1,05,1.6,646.4,M,-24.1,M,,*75\r\n"
"$GPGSA,A,3,02,,,07,,09,24,26,,,,,1.6,1.6,1.0*3D\r\n"
"$GPGSV,2,1,08,02,43,088,38,04,42,145,00,05,11,291,00,07,60,043,35*71\r\n"
"$GPGSV,2,2,08,08,02,145,00,09,46,303,47,24,16,178,32,26,18,231,43*77\r\n"
"$PGRME,22.0,M,52.9,M,51.0,M*14\r\n"
"$GPGLL,3907.360,N,12102.481,W,183730,A*33\r\n"
"$PGRMZ,2062,f,3*2D\r\n"
"$PGRMM,WGS84*06\r\n"
"$GPBOD,,T,,M,,*47\r\n"
"$GPRTE,1,1,c,0*07\r\n"
"$GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67\r\n"
"$GPRMB,A,,,,,,,,,,,,V*71\r\n";
static size_t write_ptr;
static void uart_irqhandler(void);
int
main() {
uint8_t rx;
/* Init GPS */
lwgps_init(&hgps);
/* Create buffer for received data */
lwrb_init(&hgps_buff, hgps_buff_data, sizeof(hgps_buff_data));
while (1) {
/* Add new character to buffer */
/* Fake UART interrupt handler on host microcontroller */
uart_irqhandler();
/* Process all input data */
/* Read from buffer byte-by-byte and call processing function */
if (lwrb_get_full(&hgps_buff)) { /* Check if anything in buffer now */
while (lwrb_read(&hgps_buff, &rx, 1) == 1) {
lwgps_process(&hgps, &rx, 1); /* Process byte-by-byte */
}
} else {
/* Print all data after successful processing */
printf("Latitude: %f degrees\r\n", hgps.latitude);
printf("Longitude: %f degrees\r\n", hgps.longitude);
printf("Altitude: %f meters\r\n", hgps.altitude);
break;
}
}
return 0;
}
/**
* \brief Interrupt handler routing for UART received character
* \note This is not real MCU, it is software method, called from main
*/
static void
uart_irqhandler(void) {
/* Make interrupt handler as fast as possible */
/* Only write to received buffer and process later */
if (write_ptr < strlen(gps_rx_data)) {
/* Write to buffer only */
lwrb_write(&hgps_buff, &gps_rx_data[write_ptr], 1);
++write_ptr;
}
}

View File

@ -0,0 +1,24 @@
#include "lwgps/lwgps.h"
/* Distance and bearing results */
lwgps_float_t dist, bear;
/* New York coordinates */
lwgps_float_t lat1 = 40.685721;
lwgps_float_t lon1 = -73.820465;
/* Munich coordinates */
lwgps_float_t lat2 = 48.150906;
lwgps_float_t lon2 = 11.554176;
/* Go from New York to Munich */
/* Calculate distance and bearing related to north */
lwgps_distance_bearing(lat1, lon1, lat2, lon2, &dist, &bear);
printf("Distance: %f meters\r\n", (float)dist);
printf("Initial bearing: %f degrees\r\n", (float)bear);
/* Go from Munich to New York */
/* Calculate distance and bearing related to north */
lwgps_distance_bearing(lat2, lon2, lat1, lon1, &dist, &bear);
printf("Distance: %f meters\r\n", (float)dist);
printf("Initial bearing: %f degrees\r\n", (float)bear);

View File

@ -0,0 +1,77 @@
/**
* This example tests the callback functionality of lwgps_process()
* when the LWGPS_CFG_STATUS flag is set.
*/
#include <string.h>
#include <stdio.h>
#include "lwgps/lwgps.h"
#if !LWGPS_CFG_STATUS
#error "this test must be compiled with -DLWGPS_CFG_STATUS=1"
#endif /* !LWGPS_CFG_STATUS */
/* GPS handle */
lwgps_t hgps;
/**
* \brief Dummy data from GPS receiver
*/
const char
gps_rx_data[] = ""
"$GPRMC,183729,A,3907.356,N,12102.482,W,000.0,360.0,080301,015.5,E*6F\r\n"
"$GPRMB,A,,,,,,,,,,,,V*71\r\n"
"$GPGGA,183730,3907.356,N,12102.482,W,1,05,1.6,646.4,M,-24.1,M,,*75\r\n"
"$GPGSA,A,3,02,,,07,,09,24,26,,,,,1.6,1.6,1.0*3D\r\n"
"$GPGSV,2,1,08,02,43,088,38,04,42,145,00,05,11,291,00,07,60,043,35*71\r\n"
"$GPGSV,2,2,08,08,02,145,00,09,46,303,47,24,16,178,32,26,18,231,43*77\r\n"
"$PGRME,22.0,M,52.9,M,51.0,M*14\r\n"
"$GPGLL,3907.360,N,12102.481,W,183730,A*33\r\n"
"$PGRMZ,2062,f,3*2D\r\n"
"$PGRMM,WGS84*06\r\n"
"$GPBOD,,T,,M,,*47\r\n"
"$GPRTE,1,1,c,0*07\r\n"
"$GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67\r\n"
"$GPRMB,A,,,,,,,,,,,,V*71\r\n";
const lwgps_statement_t expected[] = {
STAT_RMC,
STAT_UNKNOWN,
STAT_GGA,
STAT_GSA,
STAT_GSV,
STAT_GSV,
STAT_UNKNOWN,
STAT_UNKNOWN,
STAT_UNKNOWN,
STAT_CHECKSUM_FAIL,
STAT_UNKNOWN,
STAT_UNKNOWN,
STAT_RMC,
STAT_UNKNOWN
};
static int err_cnt;
void
callback(lwgps_statement_t res) {
static int i;
if (res != expected[i]) {
printf("failed i %d, expected res %d but received %d\n",
i, expected[i], res);
++err_cnt;
}
++i;
}
int
main() {
/* Init GPS */
lwgps_init(&hgps);
/* Process all input data */
lwgps_process(&hgps, gps_rx_data, strlen(gps_rx_data), callback);
return err_cnt;
}

View File

@ -0,0 +1,71 @@
/*
* This example uses direct processing function,
* to process dummy NMEA data from GPS receiver
*/
#include <string.h>
#include <stdio.h>
#include "lwgps/lwgps.h"
#include "test_common.h"
/* GPS handle */
lwgps_t hgps;
/**
* \brief Dummy data from GPS receiver
*/
const char
gps_rx_data[] = ""
"$GPRMC,183729,A,3907.356,N,12102.482,W,000.0,360.0,080301,015.5,E*6F\r\n"
"$GPGGA,183730,3907.356,N,12102.482,W,1,05,1.6,646.4,M,-24.1,M,,*75\r\n"
"$GPGSA,A,3,02,,,07,,09,24,26,,,,,1.6,1.6,1.0*3D\r\n"
"$GPGSV,2,1,08,02,43,088,38,04,42,145,00,05,11,291,00,07,60,043,35*71\r\n"
"$GPGSV,2,2,08,08,02,145,00,09,46,303,47,24,16,178,32,26,18,231,43*77\r\n"
"";
/**
* \brief Run the test of raw input data
*/
void
run_tests() {
lwgps_init(&hgps); /* Init GPS */
/* Process all input data */
lwgps_process(&hgps, gps_rx_data, strlen(gps_rx_data));
/* Run the test */
RUN_TEST(!INT_IS_EQUAL(hgps.is_valid, 0));
RUN_TEST(INT_IS_EQUAL(hgps.fix, 1));
RUN_TEST(INT_IS_EQUAL(hgps.fix_mode, 3));
RUN_TEST(FLT_IS_EQUAL(hgps.latitude, 39.1226000000));
RUN_TEST(FLT_IS_EQUAL(hgps.longitude, -121.0413666666));
RUN_TEST(FLT_IS_EQUAL(hgps.altitude, 646.4000000000));
RUN_TEST(FLT_IS_EQUAL(hgps.course, 360.0000000000));
RUN_TEST(INT_IS_EQUAL(hgps.dop_p, 1.6000000000));
RUN_TEST(INT_IS_EQUAL(hgps.dop_h, 1.6000000000));
RUN_TEST(INT_IS_EQUAL(hgps.dop_v, 1.0000000000));
RUN_TEST(FLT_IS_EQUAL(hgps.speed, 0.0000000000));
RUN_TEST(FLT_IS_EQUAL(hgps.geo_sep, -24.100000000));
RUN_TEST(FLT_IS_EQUAL(hgps.variation, 15.500000000));
RUN_TEST(INT_IS_EQUAL(hgps.sats_in_view, 8));
RUN_TEST(INT_IS_EQUAL(hgps.sats_in_use, 5));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[0], 2));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[1], 0));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[2], 0));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[3], 7));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[4], 0));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[5], 9));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[6], 24));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[7], 26));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[8], 0));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[9], 0));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[10], 0));
RUN_TEST(INT_IS_EQUAL(hgps.satellites_ids[11], 0));
RUN_TEST(INT_IS_EQUAL(hgps.date, 8));
RUN_TEST(INT_IS_EQUAL(hgps.month, 3));
RUN_TEST(INT_IS_EQUAL(hgps.year, 1));
RUN_TEST(INT_IS_EQUAL(hgps.hours, 18));
RUN_TEST(INT_IS_EQUAL(hgps.minutes, 37));
RUN_TEST(INT_IS_EQUAL(hgps.seconds, 30));
}

View File

@ -0,0 +1,18 @@
#ifndef TEST_COMMON_HDR_H
#define TEST_COMMON_HDR_H
#include <math.h>
#include <stdlib.h>
#define RUN_TEST(x) do { \
if ((x)) { \
printf("Test passed on line %u with condition " # x "\r\n", (unsigned)__LINE__); \
} else { \
printf("Test FAILED on line %u with condition " # x "\r\n", (unsigned)__LINE__ ); \
exit(1); \
} \
} while (0)
#define FLT_IS_EQUAL(x, y) (fabs((double)(x) - (double)(y)) < 0.00001)
#define INT_IS_EQUAL(x, y) ((int)((x) == (y)))
#endif /* TEST_COMMON_HDR_H */

View File

@ -0,0 +1,69 @@
/*
* This example uses direct processing function,
* to process dummy PUBX TIME packets from GPS receiver
*/
#include <string.h>
#include <stdio.h>
#include "lwgps/lwgps.h"
#include "test_common.h"
#if !LWGPS_CFG_STATEMENT_PUBX_TIME
#error "this test must be compiled with -DLWGPS_CFG_STATEMENT_PUBX_TIME=1"
#endif /* !LWGPS_CFG_STATEMENT_PUBX_TIME */
/* GPS handle */
lwgps_t hgps;
/**
* \brief Dummy data from GPS receiver
*/
const char
gps_rx_data_A[] = ""
"$PUBX,04*37\r\n"
"$PUBX,04,073731.00,091202,113851.00,1196,15D,1930035,-2660.664,43*71\r\n"
"";
const char
gps_rx_data_B[] = ""
"$PUBX,04,200714.00,230320,158834.00,2098,18,536057,257.043,16*12\r\b"
"";
/**
* \brief Run the test of raw input data
*/
void
run_tests() {
lwgps_init(&hgps);
/* Process and test block A */
lwgps_process(&hgps, gps_rx_data_A, strlen(gps_rx_data_A));
RUN_TEST(INT_IS_EQUAL(hgps.hours, 7));
RUN_TEST(INT_IS_EQUAL(hgps.minutes, 37));
RUN_TEST(INT_IS_EQUAL(hgps.seconds, 31));
RUN_TEST(INT_IS_EQUAL(hgps.date, 9));
RUN_TEST(INT_IS_EQUAL(hgps.month, 12));
RUN_TEST(INT_IS_EQUAL(hgps.year, 2));
RUN_TEST(FLT_IS_EQUAL(hgps.utc_tow, 113851.00));
RUN_TEST(INT_IS_EQUAL(hgps.utc_wk, 1196));
RUN_TEST(INT_IS_EQUAL(hgps.leap_sec, 15));
RUN_TEST(INT_IS_EQUAL(hgps.clk_bias, 1930035));
RUN_TEST(FLT_IS_EQUAL(hgps.clk_drift, -2660.664));
RUN_TEST(INT_IS_EQUAL(hgps.tp_gran, 43));
/* Process and test block B */
lwgps_process(&hgps, gps_rx_data_B, strlen(gps_rx_data_B));
RUN_TEST(INT_IS_EQUAL(hgps.hours, 20));
RUN_TEST(INT_IS_EQUAL(hgps.minutes, 7));
RUN_TEST(INT_IS_EQUAL(hgps.seconds, 14));
RUN_TEST(INT_IS_EQUAL(hgps.date, 23));
RUN_TEST(INT_IS_EQUAL(hgps.month, 3));
RUN_TEST(INT_IS_EQUAL(hgps.year, 20));
RUN_TEST(FLT_IS_EQUAL(hgps.utc_tow, 158834.00));
RUN_TEST(INT_IS_EQUAL(hgps.utc_wk, 2098));
RUN_TEST(INT_IS_EQUAL(hgps.leap_sec, 18));
RUN_TEST(INT_IS_EQUAL(hgps.clk_bias, 536057));
RUN_TEST(FLT_IS_EQUAL(hgps.clk_drift, 257.043));
RUN_TEST(INT_IS_EQUAL(hgps.tp_gran, 16));
}

View File

@ -0,0 +1 @@
add_subdirectory(src)

View File

@ -0,0 +1,3 @@
add_subdirectory(include)
add_subdirectory(lwgps)

View File

@ -0,0 +1,7 @@
target_include_directories(${LIB_LWGPS_NAME} INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_include_directories(${LIB_LWGPS_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -0,0 +1,3 @@
target_sources(${LIB_LWGPS_NAME} PRIVATE
lwgps.c
)

Some files were not shown because too many files have changed in this diff Show More