重构代码,运行效果和初版一致

PDRHistoryBranch
詹力 2024-12-06 22:17:04 +08:00
parent 318dad3572
commit cdfa997219
377 changed files with 177 additions and 52315 deletions

View File

@ -1 +0,0 @@
.DS_store

View File

@ -1,95 +0,0 @@
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

@ -1,244 +0,0 @@
# 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

@ -1,60 +0,0 @@
/*
* 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

@ -1,67 +0,0 @@
/*
* 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

@ -1,77 +0,0 @@
/*
* 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

@ -1,101 +0,0 @@
#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

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

View File

@ -1,30 +0,0 @@
$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

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

View File

@ -1,63 +0,0 @@
#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

@ -1,32 +0,0 @@
#######################################
# 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

@ -1,9 +0,0 @@
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

@ -1,657 +0,0 @@
/*
* 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

@ -1,17 +0,0 @@
# 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

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

View File

@ -1,385 +0,0 @@
#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

@ -1,5 +0,0 @@
" 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

@ -1,39 +0,0 @@
################################################################################
# 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

@ -1,21 +0,0 @@
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

@ -1,40 +0,0 @@
# 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

@ -1,31 +0,0 @@

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

@ -1,149 +0,0 @@
<?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

@ -1,23 +0,0 @@
<?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

@ -1,44 +0,0 @@
/**
* \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

@ -1,32 +0,0 @@
/*
* 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

@ -1,20 +0,0 @@
# 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

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

View File

@ -1,12 +0,0 @@
.. _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

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

View File

@ -1,129 +0,0 @@
# 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

@ -1,47 +0,0 @@
.. _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

@ -1,100 +0,0 @@
.. _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

@ -1,71 +0,0 @@
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

@ -1,35 +0,0 @@
@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

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

View File

@ -1,64 +0,0 @@
/* 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

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1 +0,0 @@
<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

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -1 +0,0 @@
<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

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1 +0,0 @@
<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

@ -1 +0,0 @@
<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

@ -1,3 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,15 +0,0 @@
.. _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

@ -1,38 +0,0 @@
.. _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

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

View File

@ -1,14 +0,0 @@
.. _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

@ -1,16 +0,0 @@
.. _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

@ -1,40 +0,0 @@
# 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

@ -1,47 +0,0 @@
/**
* 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

@ -1,81 +0,0 @@
#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

@ -1,24 +0,0 @@
#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

@ -1,77 +0,0 @@
/**
* 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

@ -1,71 +0,0 @@
/*
* 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

@ -1,18 +0,0 @@
#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

@ -1,69 +0,0 @@
/*
* 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

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

View File

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

View File

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

View File

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

View File

@ -1,291 +0,0 @@
/**
* \file lwgps.h
* \brief GPS main 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: v2.1.0
*/
#ifndef LWGPS_HDR_H
#define LWGPS_HDR_H
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#include "lwgps/lwgps_opt.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* \defgroup LWGPS Lightweight GPS NMEA parser
* \brief Lightweight GPS NMEA parser
* \{
*/
/**
* \brief GPS float definition, can be either `float` or `double`
* \note Check for \ref LWGPS_CFG_DOUBLE configuration
*/
#if LWGPS_CFG_DOUBLE || __DOXYGEN__
typedef double lwgps_float_t;
#else
typedef float lwgps_float_t;
#endif
/**
* \brief Satellite descriptor
*/
typedef struct {
uint8_t num; /*!< Satellite number */
uint8_t elevation; /*!< Elevation value */
uint16_t azimuth; /*!< Azimuth in degrees */
uint8_t snr; /*!< Signal-to-noise ratio */
} lwgps_sat_t;
/**
* \brief ENUM of possible GPS statements parsed
*/
typedef enum {
STAT_UNKNOWN = 0, /*!< Unknown NMEA statement */
STAT_GGA = 1, /*!< GPGGA statement */
STAT_GSA = 2, /*!< GPGSA statement */
STAT_GSV = 3, /*!< GPGSV statement */
STAT_RMC = 4, /*!< GPRMC statement */
STAT_UBX = 5, /*!< UBX statement (uBlox specific) */
STAT_UBX_TIME = 6, /*!< UBX TIME statement (uBlox specific) */
STAT_CHECKSUM_FAIL = UINT8_MAX /*!< Special case, used when checksum fails */
} lwgps_statement_t;
/**
* \brief GPS main structure
*/
typedef struct {
#if LWGPS_CFG_STATEMENT_GPGGA || __DOXYGEN__
/* Information related to GPGGA statement */
lwgps_float_t latitude; /*!< Latitude in units of degrees */
lwgps_float_t longitude; /*!< Longitude in units of degrees */
lwgps_float_t altitude; /*!< Altitude in units of meters */
lwgps_float_t geo_sep; /*!< Geoid separation in units of meters */
uint8_t sats_in_use; /*!< Number of satellites in use */
uint8_t fix; /*!< Fix status. `0` = invalid, `1` = GPS fix, `2` = DGPS fix, `3` = PPS fix */
uint8_t hours; /*!< Hours in UTC */
uint8_t minutes; /*!< Minutes in UTC */
uint8_t seconds; /*!< Seconds in UTC */
#endif /* LWGPS_CFG_STATEMENT_GPGGA || __DOXYGEN__ */
#if LWGPS_CFG_STATEMENT_GPGSA || __DOXYGEN__
/* Information related to GPGSA statement */
lwgps_float_t dop_h; /*!< Dolution of precision, horizontal */
lwgps_float_t dop_v; /*!< Dolution of precision, vertical */
lwgps_float_t dop_p; /*!< Dolution of precision, position */
uint8_t fix_mode; /*!< Fix mode. `1` = NO fix, `2` = 2D fix, `3` = 3D fix */
uint8_t satellites_ids[12]; /*!< List of satellite IDs in use. Valid range is `0` to `sats_in_use` */
#endif /* LWGPS_CFG_STATEMENT_GPGSA || __DOXYGEN__ */
#if LWGPS_CFG_STATEMENT_GPGSV || __DOXYGEN__
/* Information related to GPGSV statement */
uint8_t sats_in_view; /*!< Number of satellites in view */
#if LWGPS_CFG_STATEMENT_GPGSV_SAT_DET || __DOXYGEN__
lwgps_sat_t sats_in_view_desc[12];
#endif /* LWGPS_CFG_STATEMENT_GPGSV_SAT_DET || __DOXYGEN__ */
#endif /* LWGPS_CFG_STATEMENT_GPGSV || __DOXYGEN__ */
#if LWGPS_CFG_STATEMENT_GPRMC || __DOXYGEN__
/* Information related to GPRMC statement */
uint8_t is_valid; /*!< GPS valid status */
lwgps_float_t speed; /*!< Ground speed in knots */
lwgps_float_t course; /*!< Ground coarse */
lwgps_float_t variation; /*!< Magnetic variation */
uint8_t date; /*!< Fix date */
uint8_t month; /*!< Fix month */
uint8_t year; /*!< Fix year */
#endif /* LWGPS_CFG_STATEMENT_GPRMC || __DOXYGEN__ */
#if LWGPS_CFG_STATEMENT_PUBX_TIME || __DOXYGEN__
#if !LWGPS_CFG_STATEMENT_GPGGA && !__DOXYGEN__
/* rely on time fields from GPGGA if possible */
uint8_t hours;
uint8_t minutes;
uint8_t seconds;
#endif /* !LWGPS_CFG_STATEMENT_GPGGA && !__DOXYGEN__ */
#if !LWGPS_CFG_STATEMENT_GPRMC && !__DOXYGEN__
/* rely on date fields from GPRMC if possible */
uint8_t date;
uint8_t month;
uint8_t year;
#endif /* !LWGPS_CFG_STATEMENT_GPRMC && !__DOXYGEN__ */
/* fields only available in PUBX_TIME */
lwgps_float_t utc_tow; /*!< UTC TimeOfWeek, eg 113851.00 */
uint16_t utc_wk; /*!< UTC week number, continues beyond 1023 */
uint8_t leap_sec; /*!< UTC leap seconds; UTC + leap_sec = TAI */
uint32_t clk_bias; /*!< Receiver clock bias, eg 1930035 */
lwgps_float_t clk_drift; /*!< Receiver clock drift, eg -2660.664 */
uint32_t tp_gran; /*!< Time pulse granularity, eg 43 */
#endif /* LWGPS_CFG_STATEMENT_PUBX_TIME || __DOXYGEN__ */
#if !__DOXYGEN__
struct {
lwgps_statement_t stat; /*!< Statement index */
char term_str[13]; /*!< Current term in string format */
uint8_t term_pos; /*!< Current index position in term */
uint8_t term_num; /*!< Current term number */
uint8_t star; /*!< Star detected flag */
#if LWGPS_CFG_CRC
uint8_t crc_calc; /*!< Calculated CRC string */
#endif /* LWGPS_CFG_CRC */
union {
uint8_t dummy; /*!< Dummy byte */
#if LWGPS_CFG_STATEMENT_GPGGA
struct {
lwgps_float_t latitude; /*!< GPS latitude position in degrees */
lwgps_float_t longitude; /*!< GPS longitude position in degrees */
lwgps_float_t altitude; /*!< GPS altitude in meters */
lwgps_float_t geo_sep; /*!< Geoid separation in units of meters */
uint8_t sats_in_use; /*!< Number of satellites currently in use */
uint8_t fix; /*!< Type of current fix, `0` = Invalid, `1` = GPS fix, `2` = Differential GPS fix */
uint8_t hours; /*!< Current UTC hours */
uint8_t minutes; /*!< Current UTC minutes */
uint8_t seconds; /*!< Current UTC seconds */
} gga; /*!< GPGGA message */
#endif /* LWGPS_CFG_STATEMENT_GPGGA */
#if LWGPS_CFG_STATEMENT_GPGSA
struct {
lwgps_float_t dop_h; /*!< Horizontal dilution of precision */
lwgps_float_t dop_v; /*!< Vertical dilution of precision */
lwgps_float_t dop_p; /*!< Position dilution of precision */
uint8_t fix_mode; /*!< Fix mode, `1` = No fix, `2` = 2D fix, `3` = 3D fix */
uint8_t satellites_ids[12]; /*!< IDs of satellites currently in use */
} gsa; /*!< GPGSA message */
#endif /* LWGPS_CFG_STATEMENT_GPGSA */
#if LWGPS_CFG_STATEMENT_GPGSV
struct {
uint8_t sats_in_view; /*!< Number of stallites in view */
uint8_t stat_num; /*!< Satellite line number during parsing GPGSV data */
} gsv; /*!< GPGSV message */
#endif /* LWGPS_CFG_STATEMENT_GPGSV */
#if LWGPS_CFG_STATEMENT_GPRMC
struct {
uint8_t is_valid; /*!< Status whether GPS status is valid or not */
uint8_t date; /*!< Current UTC date */
uint8_t month; /*!< Current UTC month */
uint8_t year; /*!< Current UTC year */
lwgps_float_t speed; /*!< Current spead over the ground in knots */
lwgps_float_t course; /*!< Current course over ground */
lwgps_float_t variation; /*!< Current magnetic variation in degrees */
} rmc; /*!< GPRMC message */
#endif /* LWGPS_CFG_STATEMENT_GPRMC */
#if LWGPS_CFG_STATEMENT_PUBX_TIME
struct {
uint8_t hours; /*!< Current UTC hours */
uint8_t minutes; /*!< Current UTC minutes */
uint8_t seconds; /*!< Current UTC seconds */
uint8_t date; /*!< Current UTC date */
uint8_t month; /*!< Current UTC month */
uint8_t year; /*!< Current UTC year */
lwgps_float_t utc_tow; /*!< UTC TimeOfWeek, eg 113851.00 */
uint16_t utc_wk; /*!< UTC week number, continues beyond 1023 */
uint8_t leap_sec; /*!< UTC leap seconds; UTC + leap_sec = TAI */
uint32_t clk_bias; /*!< Receiver clock bias, eg 1930035 */
lwgps_float_t clk_drift; /*!< Receiver clock drift, eg -2660.664 */
uint32_t tp_gran; /*!< Time pulse granularity, eg 43 */
} time; /*!< PUBX TIME message */
#endif /* LWGPS_CFG_STATEMENT_PUBX_TIME */
} data; /*!< Union with data for each information */
} p; /*!< Structure with private data */
#endif /* !__DOXYGEN__ */
} lwgps_t;
/**
* \brief List of optional speed transformation from GPS values (in knots)
*/
typedef enum {
/* Metric values */
lwgps_speed_kps, /*!< Kilometers per second */
lwgps_speed_kph, /*!< Kilometers per hour */
lwgps_speed_mps, /*!< Meters per second */
lwgps_speed_mpm, /*!< Meters per minute */
/* Imperial values */
lwgps_speed_mips, /*!< Miles per second */
lwgps_speed_mph, /*!< Miles per hour */
lwgps_speed_fps, /*!< Foots per second */
lwgps_speed_fpm, /*!< Foots per minute */
/* Optimized for runners/joggers */
lwgps_speed_mpk, /*!< Minutes per kilometer */
lwgps_speed_spk, /*!< Seconds per kilometer */
lwgps_speed_sp100m, /*!< Seconds per 100 meters */
lwgps_speed_mipm, /*!< Minutes per mile */
lwgps_speed_spm, /*!< Seconds per mile */
lwgps_speed_sp100y, /*!< Seconds per 100 yards */
/* Nautical values */
lwgps_speed_smph, /*!< Sea miles per hour */
} lwgps_speed_t;
/**
* \brief Signature for caller-suplied callback function from gps_process
* \param[in] res: statement type of recently parsed statement
*/
typedef void (*lwgps_process_fn)(lwgps_statement_t res);
/**
* \brief Check if current GPS data contain valid signal
* \note \ref LWGPS_CFG_STATEMENT_GPRMC must be enabled and `GPRMC` statement must be sent from GPS receiver
* \param[in] _gh: GPS handle
* \return `1` on success, `0` otherwise
*/
#if LWGPS_CFG_STATEMENT_GPRMC || __DOXYGEN__
#define lwgps_is_valid(_gh) ((_gh)->is_valid)
#else
#define lwgps_is_valid(_gh) (0)
#endif /* LWGPS_CFG_STATEMENT_GPRMC || __DOXYGEN__ */
uint8_t lwgps_init(lwgps_t* gh);
#if LWGPS_CFG_STATUS || __DOXYGEN__
uint8_t lwgps_process(lwgps_t* gh, const void* data, size_t len, lwgps_process_fn evt_fn);
#else /* LWGPS_CFG_STATUS */
uint8_t lwgps_process(lwgps_t* gh, const void* data, size_t len);
#endif /* !LWGPS_CFG_STATUS */
uint8_t lwgps_distance_bearing(lwgps_float_t las, lwgps_float_t los, lwgps_float_t lae, lwgps_float_t loe, lwgps_float_t* d, lwgps_float_t* b);
lwgps_float_t lwgps_to_speed(lwgps_float_t sik, lwgps_speed_t ts);
/**
* \}
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LWGPS_HDR_H */

View File

@ -1,183 +0,0 @@
/**
* \file lwgps_opt.h
* \brief LwGPS options
*/
/*
* 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_OPT_H
#define LWGPS_HDR_OPT_H
/* Uncomment to ignore user options (or set macro in compiler flags) */
/* #define LWGPS_IGNORE_USER_OPTS */
/* Include application options */
#ifndef LWGPS_IGNORE_USER_OPTS
#include "lwgps_opts.h"
#endif /* LWGPS_IGNORE_USER_OPTS */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* \defgroup LWGPS_OPT Configuration
* \brief Default configuration setup
* \{
*/
/**
* \brief Enables `1` or disables `0` `double precision` for floating point
* values such as latitude, longitude, altitude.
*
* `double` is used as variable type when enabled, `float` when disabled.
*/
#ifndef LWGPS_CFG_DOUBLE
#define LWGPS_CFG_DOUBLE 1
#endif
/**
* \brief Enables `1` or disables `0` status reporting callback
* by \ref lwgps_process
*
* \note This is an extension, so not enabled by default.
*/
#ifndef LWGPS_CFG_STATUS
#define LWGPS_CFG_STATUS 0
#endif
/**
* \brief Enables `1` or disables `0` `GGA` statement parsing.
*
* \note This statement must be enabled to parse:
* - Latitude, Longitude, Altitude
* - Number of satellites in use, fix (no fix, GPS, DGPS), UTC time
*/
#ifndef LWGPS_CFG_STATEMENT_GPGGA
#define LWGPS_CFG_STATEMENT_GPGGA 1
#endif
/**
* \brief Enables `1` or disables `0` `GSA` statement parsing.
*
* \note This statement must be enabled to parse:
* - Position/Vertical/Horizontal dilution of precision
* - Fix mode (no fix, 2D, 3D fix)
* - IDs of satellites in use
*/
#ifndef LWGPS_CFG_STATEMENT_GPGSA
#define LWGPS_CFG_STATEMENT_GPGSA 1
#endif
/**
* \brief Enables `1` or disables `0` `RMC` statement parsing.
*
* \note This statement must be enabled to parse:
* - Validity of GPS signal
* - Ground speed in knots and coarse in degrees
* - Magnetic variation
* - UTC date
*/
#ifndef LWGPS_CFG_STATEMENT_GPRMC
#define LWGPS_CFG_STATEMENT_GPRMC 1
#endif
/**
* \brief Enables `1` or disables `0` `GSV` statement parsing.
*
* \note This statement must be enabled to parse:
* - Number of satellites in view
* - Optional details of each satellite in view. See \ref LWGPS_CFG_STATEMENT_GPGSV_SAT_DET
*/
#ifndef LWGPS_CFG_STATEMENT_GPGSV
#define LWGPS_CFG_STATEMENT_GPGSV 1
#endif
/**
* \brief Enables `1` or disables `0` detailed parsing of each
* satellite in view for `GSV` statement.
*
* \note When this feature is disabled, only number of "satellites in view" is parsed
*/
#ifndef LWGPS_CFG_STATEMENT_GPGSV_SAT_DET
#define LWGPS_CFG_STATEMENT_GPGSV_SAT_DET 0
#endif
/**
* \brief Enables `1` or disables `0` parsing and generation
* of PUBX (uBlox) messages
*
* PUBX are a nonstandard ublox-specific extensions,
* so disabled by default.
*/
#ifndef LWGPS_CFG_STATEMENT_PUBX
#define LWGPS_CFG_STATEMENT_PUBX 0
#endif
/**
* \brief Enables `1` or disables `0` parsing and generation
* of PUBX (uBlox) TIME messages.
*
* \note TIME messages can be used to obtain:
* - UTC time of week
* - UTC week number
* - Leap seconds (allows conversion to eg. TAI)
*
* This is a nonstandard ublox-specific extension,
* so disabled by default.
*
* This configure option requires LWGPS_CFG_STATEMENT_PUBX
*/
#ifndef LWGPS_CFG_STATEMENT_PUBX_TIME
#define LWGPS_CFG_STATEMENT_PUBX_TIME 0
#endif
/**
* \brief Enables `1` or disables `0` CRC calculation and check
*
* \note When not enabled, CRC check is ignored
*/
#ifndef LWGPS_CFG_CRC
#define LWGPS_CFG_CRC 1
#endif
/* Guard against accidental parser breakage */
#if LWGPS_CFG_STATEMENT_PUBX_TIME && !LWGPS_CFG_STATEMENT_PUBX
#error LWGPS_CFG_STATEMENT_PUBX must be enabled when enabling LWGPS_CFG_STATEMENT_PUBX_TIME
#endif /* LWGPS_CFG_STATEMENT_PUBX_TIME && !LWGPS_CFG_STATEMENT_PUBX */
/**
* \}
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LWGPS_HDR_OPT_H */

View File

@ -1,44 +0,0 @@
/**
* \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: v2.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

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

View File

@ -1,614 +0,0 @@
/**
* \file lwgps.c
* \brief GPS main 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: v2.1.0
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "lwgps/lwgps.h"
#define FLT(x) ((lwgps_float_t)(x))
#define D2R(x) FLT(FLT(x) * FLT(0.01745329251994)) /*!< Degrees to radians */
#define R2D(x) FLT(FLT(x) * FLT(57.29577951308232))/*!< Radians to degrees */
#define EARTH_RADIUS FLT(6371.0) /*!< Earth radius in units of kilometers */
#if LWGPS_CFG_CRC
#define CRC_ADD(_gh, ch) (_gh)->p.crc_calc ^= (uint8_t)(ch)
#else
#define CRC_ADD(_gh, ch)
#endif /* LWGPS_CFG_CRC */
#define TERM_ADD(_gh, ch) do { \
if ((_gh)->p.term_pos < (sizeof((_gh)->p.term_str) - 1)) { \
(_gh)->p.term_str[(_gh)->p.term_pos] = (ch);\
(_gh)->p.term_str[++(_gh)->p.term_pos] = 0; \
} \
} while (0)
#define TERM_NEXT(_gh) do { (_gh)->p.term_str[((_gh)->p.term_pos = 0)] = 0; ++(_gh)->p.term_num; } while (0)
#define CIN(x) ((x) >= '0' && (x) <= '9')
#define CIHN(x) (((x) >= '0' && (x) <= '9') || ((x) >= 'a' && (x) <= 'f') || ((x) >= 'A' && (x) <= 'F'))
#define CTN(x) ((x) - '0')
#define CHTN(x) (((x) >= '0' && (x) <= '9') ? ((x) - '0') : (((x) >= 'a' && (x) <= 'z') ? ((x) - 'a' + 10) : (((x) >= 'A' && (x) <= 'Z') ? ((x) - 'A' + 10) : 0)))
/**
* \brief Parse number as integer
* \param[in] gh: GPS handle
* \param[in] t: Text to parse. Set to `NULL` to parse current GPS term
* \return Parsed integer
*/
static int32_t
prv_parse_number(lwgps_t* gh, const char* t) {
int32_t res = 0;
uint8_t minus;
if (t == NULL) {
t = gh->p.term_str;
}
for (; t != NULL && *t == ' '; ++t) {} /* Strip leading spaces */
minus = (*t == '-' ? (++t, 1) : 0);
for (; t != NULL && CIN(*t); ++t) {
res = 10 * res + CTN(*t);
}
return minus ? -res : res;
}
/**
* \brief Parse number as double and convert it to \ref lwgps_float_t
* \param[in] gh: GPS handle
* \param[in] t: Text to parse. Set to `NULL` to parse current GPS term
* \return Parsed double in \ref lwgps_float_t format
*/
static lwgps_float_t
prv_parse_float_number(lwgps_t* gh, const char* t) {
lwgps_float_t res;
if (t == NULL) {
t = gh->p.term_str;
}
for (; t != NULL && *t == ' '; ++t) {} /* Strip leading spaces */
#if LWGPS_CFG_DOUBLE
res = strtod(t, NULL); /* Parse string to double */
#else /* LWGPS_CFG_DOUBLE */
res = strtof(t, NULL); /* Parse string to float */
#endif /* !LWGPS_CFG_DOUBLE */
return FLT(res); /* Return casted value, based on float size */
}
/**
* \brief Parse latitude/longitude NMEA format to double
*
* NMEA output for latitude is ddmm.sss and longitude is dddmm.sss
* \param[in] gh: GPS handle
* \return Latitude/Longitude value in degrees
*/
static lwgps_float_t
prv_parse_lat_long(lwgps_t* gh) {
lwgps_float_t ll, deg, min;
ll = prv_parse_float_number(gh, NULL); /* Parse value as double */
deg = FLT((int)((int)ll / 100)); /* Get absolute degrees value, interested in integer part only */
min = ll - (deg * FLT(100)); /* Get remaining part from full number, minutes */
ll = deg + (min / FLT(60.0)); /* Calculate latitude/longitude */
return ll;
}
/**
* \brief Parse received term
* \param[in] gh: GPS handle
* \return `1` on success, `0` otherwise
*/
static uint8_t
prv_parse_term(lwgps_t* gh) {
if (gh->p.term_num == 0) { /* Check string type */
if (0) {
#if LWGPS_CFG_STATEMENT_GPGGA
} else if (!strncmp(gh->p.term_str, "$GPGGA", 6) || !strncmp(gh->p.term_str, "$GNGGA", 6)) {
gh->p.stat = STAT_GGA;
#endif /* LWGPS_CFG_STATEMENT_GPGGA */
#if LWGPS_CFG_STATEMENT_GPGSA
} else if (!strncmp(gh->p.term_str, "$GPGSA", 6) || !strncmp(gh->p.term_str, "$GNGSA", 6)) {
gh->p.stat = STAT_GSA;
#endif /* LWGPS_CFG_STATEMENT_GPGSA */
#if LWGPS_CFG_STATEMENT_GPGSV
} else if (!strncmp(gh->p.term_str, "$GPGSV", 6) || !strncmp(gh->p.term_str, "$GNGSV", 6)) {
gh->p.stat = STAT_GSV;
#endif /* LWGPS_CFG_STATEMENT_GPGSV */
#if LWGPS_CFG_STATEMENT_GPRMC
} else if (!strncmp(gh->p.term_str, "$GPRMC", 6) || !strncmp(gh->p.term_str, "$GNRMC", 6)) {
gh->p.stat = STAT_RMC;
#endif /* LWGPS_CFG_STATEMENT_GPRMC */
#if LWGPS_CFG_STATEMENT_PUBX
} else if (!strncmp(gh->p.term_str, "$PUBX", 5)) {
gh->p.stat = STAT_UBX;
#endif /* LWGPS_CFG_STATEMENT_PUBX */
} else {
gh->p.stat = STAT_UNKNOWN; /* Invalid statement for library */
}
return 1;
}
/* Start parsing terms */
if (gh->p.stat == STAT_UNKNOWN) {
#if LWGPS_CFG_STATEMENT_GPGGA
} else if (gh->p.stat == STAT_GGA) { /* Process GPGGA statement */
switch (gh->p.term_num) {
case 1: /* Process UTC time */
gh->p.data.gga.hours = 10 * CTN(gh->p.term_str[0]) + CTN(gh->p.term_str[1]);
gh->p.data.gga.minutes = 10 * CTN(gh->p.term_str[2]) + CTN(gh->p.term_str[3]);
gh->p.data.gga.seconds = 10 * CTN(gh->p.term_str[4]) + CTN(gh->p.term_str[5]);
break;
case 2: /* Latitude */
gh->p.data.gga.latitude = prv_parse_lat_long(gh); /* Parse latitude */
break;
case 3: /* Latitude north/south information */
if (gh->p.term_str[0] == 'S' || gh->p.term_str[0] == 's') {
gh->p.data.gga.latitude = -gh->p.data.gga.latitude;
}
break;
case 4: /* Longitude */
gh->p.data.gga.longitude = prv_parse_lat_long(gh); /* Parse longitude */
break;
case 5: /* Longitude east/west information */
if (gh->p.term_str[0] == 'W' || gh->p.term_str[0] == 'w') {
gh->p.data.gga.longitude = -gh->p.data.gga.longitude;
}
break;
case 6: /* Fix status */
gh->p.data.gga.fix = (uint8_t)prv_parse_number(gh, NULL);
break;
case 7: /* Satellites in use */
gh->p.data.gga.sats_in_use = (uint8_t)prv_parse_number(gh, NULL);
break;
case 9: /* Altitude */
gh->p.data.gga.altitude = prv_parse_float_number(gh, NULL);
break;
case 11: /* Altitude above ellipsoid */
gh->p.data.gga.geo_sep = prv_parse_float_number(gh, NULL);
break;
default:
break;
}
#endif /* LWGPS_CFG_STATEMENT_GPGGA */
#if LWGPS_CFG_STATEMENT_GPGSA
} else if (gh->p.stat == STAT_GSA) { /* Process GPGSA statement */
switch (gh->p.term_num) {
case 2: /* Process fix mode */
gh->p.data.gsa.fix_mode = (uint8_t)prv_parse_number(gh, NULL);
break;
case 15: /* Process PDOP */
gh->p.data.gsa.dop_p = prv_parse_float_number(gh, NULL);
break;
case 16: /* Process HDOP */
gh->p.data.gsa.dop_h = prv_parse_float_number(gh, NULL);
break;
case 17: /* Process VDOP */
gh->p.data.gsa.dop_v = prv_parse_float_number(gh, NULL);
break;
default:
/* Parse satellite IDs */
if (gh->p.term_num >= 3 && gh->p.term_num <= 14) {
gh->p.data.gsa.satellites_ids[gh->p.term_num - 3] = (uint8_t)prv_parse_number(gh, NULL);
}
break;
}
#endif /* LWGPS_CFG_STATEMENT_GPGSA */
#if LWGPS_CFG_STATEMENT_GPGSV
} else if (gh->p.stat == STAT_GSV) { /* Process GPGSV statement */
switch (gh->p.term_num) {
case 2: /* Current GPGSV statement number */
gh->p.data.gsv.stat_num = (uint8_t)prv_parse_number(gh, NULL);
break;
case 3: /* Process satellites in view */
gh->p.data.gsv.sats_in_view = (uint8_t)prv_parse_number(gh, NULL);
break;
default:
#if LWGPS_CFG_STATEMENT_GPGSV_SAT_DET
if (gh->p.term_num >= 4 && gh->p.term_num <= 19) { /* Check current term number */
uint8_t index, term_num = gh->p.term_num - 4; /* Normalize term number from 4-19 to 0-15 */
uint16_t value;
index = ((gh->p.data.gsv.stat_num - 1) << 0x02) + (term_num >> 2); /* Get array index */
if (index < sizeof(gh->sats_in_view_desc) / sizeof(gh->sats_in_view_desc[0])) {
value = (uint16_t)prv_parse_number(gh, NULL); /* Parse number as integer */
switch (term_num & 0x03) {
case 0:
gh->sats_in_view_desc[index].num = value;
break;
case 1:
gh->sats_in_view_desc[index].elevation = value;
break;
case 2:
gh->sats_in_view_desc[index].azimuth = value;
break;
case 3:
gh->sats_in_view_desc[index].snr = value;
break;
default:
break;
}
}
}
#endif /* LWGPS_CFG_STATEMENT_GPGSV_SAT_DET */
break;
}
#endif /* LWGPS_CFG_STATEMENT_GPGSV */
#if LWGPS_CFG_STATEMENT_GPRMC
} else if (gh->p.stat == STAT_RMC) { /* Process GPRMC statement */
switch (gh->p.term_num) {
case 2: /* Process valid status */
gh->p.data.rmc.is_valid = (gh->p.term_str[0] == 'A');
break;
case 7: /* Process ground speed in knots */
gh->p.data.rmc.speed = prv_parse_float_number(gh, NULL);
break;
case 8: /* Process true ground coarse */
gh->p.data.rmc.course = prv_parse_float_number(gh, NULL);
break;
case 9: /* Process date */
gh->p.data.rmc.date = (uint8_t)(10 * CTN(gh->p.term_str[0]) + CTN(gh->p.term_str[1]));
gh->p.data.rmc.month = (uint8_t)(10 * CTN(gh->p.term_str[2]) + CTN(gh->p.term_str[3]));
gh->p.data.rmc.year = (uint8_t)(10 * CTN(gh->p.term_str[4]) + CTN(gh->p.term_str[5]));
break;
case 10: /* Process magnetic variation */
gh->p.data.rmc.variation = prv_parse_float_number(gh, NULL);
break;
case 11: /* Process magnetic variation east/west */
if (gh->p.term_str[0] == 'W' || gh->p.term_str[0] == 'w') {
gh->p.data.rmc.variation = -gh->p.data.rmc.variation;
}
break;
default:
break;
}
#endif /* LWGPS_CFG_STATEMENT_GPRMC */
#if LWGPS_CFG_STATEMENT_PUBX
} else if (gh->p.stat == STAT_UBX) { /* Disambiguate generic PUBX statement */
if (gh->p.term_str[0] == '0' && gh->p.term_str[1] == '4') {
gh->p.stat = STAT_UBX_TIME;
}
#if LWGPS_CFG_STATEMENT_PUBX_TIME
} else if (gh->p.stat == STAT_UBX_TIME) { /* Process PUBX (uBlox) TIME statement */
switch (gh->p.term_num) {
case 2: /* Process UTC time; ignore fractions of seconds */
gh->p.data.time.hours = 10 * CTN(gh->p.term_str[0]) + CTN(gh->p.term_str[1]);
gh->p.data.time.minutes = 10 * CTN(gh->p.term_str[2]) + CTN(gh->p.term_str[3]);
gh->p.data.time.seconds = 10 * CTN(gh->p.term_str[4]) + CTN(gh->p.term_str[5]);
break;
case 3: /* Process UTC date */
gh->p.data.time.date = 10 * CTN(gh->p.term_str[0]) + CTN(gh->p.term_str[1]);
gh->p.data.time.month = 10 * CTN(gh->p.term_str[2]) + CTN(gh->p.term_str[3]);
gh->p.data.time.year = 10 * CTN(gh->p.term_str[4]) + CTN(gh->p.term_str[5]);
break;
case 4: /* Process UTC TimeOfWeek */
gh->p.data.time.utc_tow = prv_parse_float_number(gh, NULL);
break;
case 5: /* Process UTC WeekNumber */
gh->p.data.time.utc_wk = prv_parse_number(gh, NULL);
break;
case 6: /* Process UTC leap seconds */
/*
* Accomodate a 2- or 3-digit leap second count
* a trailing 'D' means this is the firmware's default value.
*/
if (gh->p.term_str[2] == 'D' || gh->p.term_str[2] == '\0') {
gh->p.data.time.leap_sec = 10 * CTN(gh->p.term_str[0])
+ CTN(gh->p.term_str[1]);
} else {
gh->p.data.time.leap_sec = 100 * CTN(gh->p.term_str[0])
+ 10 * CTN(gh->p.term_str[1])
+ CTN(gh->p.term_str[2]);
}
break;
case 7: /* Process clock bias */
gh->p.data.time.clk_bias = prv_parse_number(gh, NULL);
break;
case 8: /* Process clock drift */
gh->p.data.time.clk_drift = prv_parse_float_number(gh, NULL);
break;
case 9: /* Process time pulse granularity */
gh->p.data.time.tp_gran = prv_parse_number(gh, NULL);
break;
default:
break;
}
#endif /* LWGPS_CFG_STATEMENT_PUBX_TIME */
#endif /* LWGPS_CFG_STATEMENT_PUBX */
}
return 1;
}
#if LWGPS_CFG_CRC
/**
* \brief Compare calculated CRC with received CRC
* \param[in] gh: GPS handle
* \return `1` on success, `0` otherwise
*/
static uint8_t
prv_check_crc(lwgps_t* gh) {
uint8_t crc;
crc = (uint8_t)((CHTN(gh->p.term_str[0]) & 0x0F) << 0x04) | (CHTN(gh->p.term_str[1]) & 0x0F); /* Convert received CRC from string (hex) to number */
return gh->p.crc_calc == crc; /* They must match! */
}
#else
#define prv_check_crc(_gh) (1)
#endif /* LWGPS_CFG_CRC */
/**
* \brief Copy temporary memory to user memory
* \param[in] gh: GPS handle
* \return `1` on success, `0` otherwise
*/
static uint8_t
prv_copy_from_tmp_memory(lwgps_t* gh) {
if (0) {
#if LWGPS_CFG_STATEMENT_GPGGA
} else if (gh->p.stat == STAT_GGA) {
gh->latitude = gh->p.data.gga.latitude;
gh->longitude = gh->p.data.gga.longitude;
gh->altitude = gh->p.data.gga.altitude;
gh->geo_sep = gh->p.data.gga.geo_sep;
gh->sats_in_use = gh->p.data.gga.sats_in_use;
gh->fix = gh->p.data.gga.fix;
gh->hours = gh->p.data.gga.hours;
gh->minutes = gh->p.data.gga.minutes;
gh->seconds = gh->p.data.gga.seconds;
#endif /* LWGPS_CFG_STATEMENT_GPGGA */
#if LWGPS_CFG_STATEMENT_GPGSA
} else if (gh->p.stat == STAT_GSA) {
gh->dop_h = gh->p.data.gsa.dop_h;
gh->dop_p = gh->p.data.gsa.dop_p;
gh->dop_v = gh->p.data.gsa.dop_v;
gh->fix_mode = gh->p.data.gsa.fix_mode;
memcpy(gh->satellites_ids, gh->p.data.gsa.satellites_ids, sizeof(gh->satellites_ids));
#endif /* LWGPS_CFG_STATEMENT_GPGSA */
#if LWGPS_CFG_STATEMENT_GPGSV
} else if (gh->p.stat == STAT_GSV) {
gh->sats_in_view = gh->p.data.gsv.sats_in_view;
#endif /* LWGPS_CFG_STATEMENT_GPGSV */
#if LWGPS_CFG_STATEMENT_GPRMC
} else if (gh->p.stat == STAT_RMC) {
gh->course = gh->p.data.rmc.course;
gh->is_valid = gh->p.data.rmc.is_valid;
gh->speed = gh->p.data.rmc.speed;
gh->variation = gh->p.data.rmc.variation;
gh->date = gh->p.data.rmc.date;
gh->month = gh->p.data.rmc.month;
gh->year = gh->p.data.rmc.year;
#endif /* LWGPS_CFG_STATEMENT_GPRMC */
#if LWGPS_CFG_STATEMENT_PUBX_TIME
} else if (gh->p.stat == STAT_UBX_TIME) {
gh->hours = gh->p.data.time.hours;
gh->minutes = gh->p.data.time.minutes;
gh->seconds = gh->p.data.time.seconds;
gh->date = gh->p.data.time.date;
gh->month = gh->p.data.time.month;
gh->year = gh->p.data.time.year;
gh->utc_tow = gh->p.data.time.utc_tow;
gh->utc_wk = gh->p.data.time.utc_wk;
gh->leap_sec = gh->p.data.time.leap_sec;
gh->clk_bias = gh->p.data.time.clk_bias;
gh->clk_drift = gh->p.data.time.clk_drift;
gh->tp_gran = gh->p.data.time.tp_gran;
#endif /* LWGPS_CFG_STATEMENT_PUBX_TIME */
}
return 1;
}
/**
* \brief Init GPS handle
* \param[in] gh: GPS handle structure
* \return `1` on success, `0` otherwise
*/
uint8_t
lwgps_init(lwgps_t* gh) {
memset(gh, 0x00, sizeof(*gh)); /* Reset structure */
return 1;
}
/**
* \brief Process NMEA data from GPS receiver
* \param[in] gh: GPS handle structure
* \param[in] data: Received data
* \param[in] len: Number of bytes to process
* \param[in] evt_fn: Event function to notify application layer.
* This parameter is available only if \ref LWGPS_CFG_STATUS is enabled
* \return `1` on success, `0` otherwise
*/
uint8_t
#if LWGPS_CFG_STATUS || __DOXYGEN__
lwgps_process(lwgps_t* gh, const void* data, size_t len, lwgps_process_fn evt_fn) {
#else /* LWGPS_CFG_STATUS */
lwgps_process(lwgps_t* gh, const void* data, size_t len) {
#endif /* !LWGPS_CFG_STATUS */
const uint8_t* d = data;
for (; len > 0; ++d, --len) { /* Process all bytes */
if (*d == '$') { /* Check for beginning of NMEA line */
memset(&gh->p, 0x00, sizeof(gh->p));/* Reset private memory */
TERM_ADD(gh, *d); /* Add character to term */
} else if (*d == ',') { /* Term separator character */
prv_parse_term(gh); /* Parse term we have currently in memory */
CRC_ADD(gh, *d); /* Add character to CRC computation */
TERM_NEXT(gh); /* Start with next term */
} else if (*d == '*') { /* Start indicates end of data for CRC computation */
prv_parse_term(gh); /* Parse term we have currently in memory */
gh->p.star = 1; /* STAR detected */
TERM_NEXT(gh); /* Start with next term */
} else if (*d == '\r') {
if (prv_check_crc(gh)) { /* Check for CRC result */
/* CRC is OK, in theory we can copy data from statements to user data */
prv_copy_from_tmp_memory(gh); /* Copy memory from temporary to user memory */
#if LWGPS_CFG_STATUS
if (evt_fn != NULL) {
evt_fn(gh->p.stat);
}
} else if (evt_fn != NULL) {
evt_fn(STAT_CHECKSUM_FAIL);
#endif /* LWGPS_CFG_STATUS */
}
} else {
if (!gh->p.star) { /* Add to CRC only if star not yet detected */
CRC_ADD(gh, *d); /* Add to CRC */
}
TERM_ADD(gh, *d); /* Add character to term */
}
}
return 1;
}
/**
* \brief Calculate distance and bearing between `2` latitude and longitude coordinates
* \param[in] las: Latitude start coordinate, in units of degrees
* \param[in] los: Longitude start coordinate, in units of degrees
* \param[in] lae: Latitude end coordinate, in units of degrees
* \param[in] loe: Longitude end coordinate, in units of degrees
* \param[out] d: Pointer to output distance in units of meters
* \param[out] b: Pointer to output bearing between start and end coordinate in relation to north in units of degrees
* \return `1` on success, `0` otherwise
*/
uint8_t
lwgps_distance_bearing(lwgps_float_t las, lwgps_float_t los, lwgps_float_t lae, lwgps_float_t loe, lwgps_float_t* d, lwgps_float_t* b) {
lwgps_float_t df, dfi, a;
if (d == NULL && b == NULL) {
return 0;
}
/* Convert degrees to radians */
df = D2R(lae - las);
dfi = D2R(loe - los);
las = D2R(las);
los = D2R(los);
lae = D2R(lae);
loe = D2R(loe);
/*
* Calculate distance
*
* Calculated distance is absolute value in meters between 2 points on earth.
*/
if (d != NULL) {
/*
* a = sin(df / 2)^2 + cos(las) * cos(lae) * sin(dfi / 2)^2
* *d = RADIUS * 2 * atan(sqrt(a) / sqrt(1 - a)) * 1000 (for meters)
*/
#if LWGPS_CFG_DOUBLE
a = FLT(sin(df * 0.5) * sin(df * 0.5) + sin(dfi * 0.5) * sin(dfi * 0.5) * cos(las) * cos(lae));
*d = FLT(EARTH_RADIUS * 2.0 * atan2(sqrt(a), sqrt(1.0 - a)) * 1000.0);
#else /* LWGPS_CFG_DOUBLE */
a = FLT(sinf(df * 0.5f) * sinf(df * 0.5f) + sinf(dfi * 0.5f) * sinf(dfi * 0.5f) * cosf(las) * cosf(lae));
*d = FLT(EARTH_RADIUS * 2.0f * atan2f(sqrtf(a), sqrtf(1.0f - a)) * 1000.0f);
#endif /* !LWGPS_CFG_DOUBLE */
}
/*
* Calculate bearing
*
* Bearing is calculated from point 1 to point 2.
* Result will tell us in which direction (according to north) we should move,
* to reach point 2.
*
* Example:
* Bearing is 0 => move to north
* Bearing is 90 => move to east
* Bearing is 180 => move to south
* Bearing is 270 => move to west
*/
if (b != NULL) {
#if LWGPS_CFG_DOUBLE
df = FLT(sin(loe - los) * cos(lae));
dfi = FLT(cos(las) * sin(lae) - sin(las) * cos(lae) * cos(loe - los));
*b = R2D(atan2(df, dfi)); /* Calculate bearing and convert to degrees */
#else /* LWGPS_CFG_DOUBLE */
df = FLT(sinf(loe - los) * cosf(lae));
dfi = FLT(cosf(las) * sinf(lae) - sinf(las) * cosf(lae) * cosf(loe - los));
*b = R2D(atan2f(df, dfi)); /* Calculate bearing and convert to degrees */
#endif /* !LWGPS_CFG_DOUBLE */
if (*b < 0) { /* Check for negative angle */
*b += FLT(360); /* Make bearing always positive */
}
}
return 1;
}
/**
* \brief Convert NMEA GPS speed (in knots = nautical mile per hour) to different speed format
* \param[in] sik: Speed in knots, received from GPS NMEA statement
* \param[in] ts: Target speed to convert to from knots
* \return Speed calculated from knots
*/
lwgps_float_t
lwgps_to_speed(lwgps_float_t sik, lwgps_speed_t ts) {
switch (ts) {
case lwgps_speed_kps:
return FLT(sik * FLT(0.000514));
case lwgps_speed_kph:
return FLT(sik * FLT(1.852));
case lwgps_speed_mps:
return FLT(sik * FLT(0.5144));
case lwgps_speed_mpm:
return FLT(sik * FLT(30.87));
case lwgps_speed_mips:
return FLT(sik * FLT(0.0003197));
case lwgps_speed_mph:
return FLT(sik * FLT(1.151));
case lwgps_speed_fps:
return FLT(sik * FLT(1.688));
case lwgps_speed_fpm:
return FLT(sik * FLT(101.3));
case lwgps_speed_mpk:
return FLT(sik * FLT(32.4));
case lwgps_speed_spk:
return FLT(sik * FLT(1944.0));
case lwgps_speed_sp100m:
return FLT(sik * FLT(194.4));
case lwgps_speed_mipm:
return FLT(sik * FLT(52.14));
case lwgps_speed_spm:
return FLT(sik * FLT(3128.0));
case lwgps_speed_sp100y:
return FLT(sik * FLT(177.7));
case lwgps_speed_smph:
return FLT(sik * FLT(1.0));
default:
return 0;
}
}

View File

@ -1,8 +0,0 @@
*.o
*~
*.diff
.*.swp
minmea
tests
example
*.exe

View File

@ -1,10 +0,0 @@
language: c
compiler:
- gcc
- clang
install: sudo apt-get install check
script: make test
notifications:
email:
on_success: change
on_failure: change

View File

@ -1,13 +0,0 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@ -1,31 +0,0 @@
# Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
# This program is free software. It comes without any warranty, to the extent
# permitted by applicable law. You can redistribute it and/or modify it under
# the terms of the Do What The Fuck You Want To Public License, Version 2, as
# published by Sam Hocevar. See the COPYING file for more details.
CFLAGS = -g -Wall -Wextra -Werror -std=c99
CFLAGS += -D_POSIX_C_SOURCE=199309L -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_DARWIN_C_SOURCE
CFLAGS += $(shell pkg-config --cflags check)
LDLIBS += $(shell pkg-config --libs check)
all: scan-build test example
@echo "+++ All good."""
test: tests
@echo "+++ Running Check test suite..."
./tests
scan-build: clean
@echo "+++ Running Clang Static Analyzer..."
scan-build $(MAKE) tests
clean:
$(RM) tests example *.o
tests: tests.o minmea.o
example: example.o minmea.o
tests.o: tests.c minmea.h
minmea.o: minmea.c minmea.h
.PHONY: all test scan-build clean

View File

@ -1,160 +0,0 @@
# minmea, a lightweight GPS NMEA 0183 parser library
[![Build Status](https://travis-ci.org/kosma/minmea.svg?branch=master)](https://travis-ci.org/kosma/minmea)
Minmea is a minimalistic GPS parser library written in pure C intended for
resource-constrained platforms, especially microcontrollers and other embedded
systems.
## Features
* Written in ISO C99.
* No dynamic memory allocation.
* No floating point usage in the core library.
* Supports both fixed and floating point values.
* One source file and one header - can't get any simpler.
* Tested under Linux, OS X, Windows and embedded ARM GCC.
* Easily extendable to support new sentences.
* Complete with a test suite and static analysis.
## Supported sentences
* ``RMC`` (Recommended Minimum: position, velocity, time)
* ``GGA`` (Fix Data)
* ``GSA`` (DOP and active satellites)
* ``GLL`` (Geographic Position: Latitude/Longitude)
* ``GST`` (Pseudorange Noise Statistics)
* ``GSV`` (Satellites in view)
* ``VTG`` (Track made good and Ground speed)
* ``ZDA`` (Time & Date - UTC, day, month, year and local time zone)
Adding support for more sentences is trivial; see ``minmea.c`` source. Good documentation
on NMEA is at https://gpsd.gitlab.io/gpsd/NMEA.html
## Compatibility
Minmea runs out-of-the-box under most Unix-compatible systems. Support for non-Unix systems
(including native Windows builds under MSVC) is provided via compatibility headers:
1. Define `MINMEA_INCLUDE_COMPAT` in the build environment.
2. Add appropriate compatibility header from under `compat/` directory as `minmea_compat.h`.
## Fractional number format
Internally, minmea stores fractional numbers as pairs of two integers: ``{value, scale}``.
For example, a value of ``"-123.456"`` would be parsed as ``{-123456, 1000}``. As this
format is quite unwieldy, minmea provides the following convenience functions for converting
to either fixed-point or floating-point format:
* ``minmea_rescale({-123456, 1000}, 10) => -1235``
* ``minmea_float({-123456, 1000}) => -123.456``
The compound type ``struct minmea_float`` uses ``int_least32_t`` internally. Therefore,
the coordinate precision is guaranteed to be at least ``[+-]DDDMM.MMMMM`` (five decimal digits)
or ±2cm LSB at the equator. Note that GPS modules commonly only provide four deciminal digits
(``[+-]DDDMM.MMMM``), which equates to ±20cm (0.0001 minute is 0.0001/60 degrees and one degree
is about 111km) at the equator.
## Coordinate format
NMEA uses the clunky ``DDMM.MMMM`` format which, honestly, is not good in the internet era.
Internally, minmea stores it as a fractional number (see above); for practical uses,
the value should be probably converted to the DD.DDDDD floating point format using the
following function:
* ``minmea_tocoord({-375165, 100}) => -37.860832``
The library doesn't perform this conversion automatically for the following reasons:
* The conversion is not reversible.
* It requires floating point support.
* The user might want to perform this conversion later on or retain the original values.
## Example
```c
char line[MINMEA_MAX_LENGTH];
while (fgets(line, sizeof(line), stdin) != NULL) {
switch (minmea_sentence_id(line, false)) {
case MINMEA_SENTENCE_RMC: {
struct minmea_sentence_rmc frame;
if (minmea_parse_rmc(&frame, line)) {
printf("$RMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n",
frame.latitude.value, frame.latitude.scale,
frame.longitude.value, frame.longitude.scale,
frame.speed.value, frame.speed.scale);
printf("$RMC fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n",
minmea_rescale(&frame.latitude, 1000),
minmea_rescale(&frame.longitude, 1000),
minmea_rescale(&frame.speed, 1000));
printf("$RMC floating point degree coordinates and speed: (%f,%f) %f\n",
minmea_tocoord(&frame.latitude),
minmea_tocoord(&frame.longitude),
minmea_tofloat(&frame.speed));
}
} break;
case MINMEA_SENTENCE_GGA: {
struct minmea_sentence_gga frame;
if (minmea_parse_gga(&frame, line)) {
printf("$GGA: fix quality: %d\n", frame.fix_quality);
}
} break;
case MINMEA_SENTENCE_GSV: {
struct minmea_sentence_gsv frame;
if (minmea_parse_gsv(&frame, line)) {
printf("$GSV: message %d of %d\n", frame.msg_nr, frame.total_msgs);
printf("$GSV: sattelites in view: %d\n", frame.total_sats);
for (int i = 0; i < 4; i++)
printf("$GSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n",
frame.sats[i].nr,
frame.sats[i].elevation,
frame.sats[i].azimuth,
frame.sats[i].snr);
}
} break;
}
}
```
## Integration with your project
Simply add ``minmea.[ch]`` to your project, ``#include "minmea.h"`` and you're
good to go.
## Running unit tests
Building and running the tests requires the following:
* Check Framework (https://libcheck.github.io/check/).
* Clang Static Analyzer (https://clang-analyzer.llvm.org/).
If you have both in your ``$PATH``, running the tests should be as simple as
typing ``make``.
## Limitations
* Only a handful of frames is supported right now.
* There's no support for omitting parts of the library from building. As
a workaround, use the ``-ffunction-sections -Wl,--gc-sections`` linker flags
(or equivalent) to remove the unused functions (parsers) from the final image.
* Some systems lack ``timegm``. On these systems, the recommended course of
action is to build with ``-Dtimegm=mktime`` which will work correctly as long
the system runs in the default ``UTC`` timezone.
## Bugs
There are plenty. Report them on GitHub, or - even better - open a pull request.
Please write unit tests for any new functions you add - it's fun!
## Licensing
Minmea is open source software; see ``COPYING`` for amusement. Email me if the
license bothers you and I'll happily re-license under anything else under the sun.
## Author
Minmea was written by Kosma Moczek &lt;kosma@kosma.pl&gt; and Patryk Szymczak
&lt;patryk.szymczak@gmail.com&gt; at Cloud Your Car, with bugs fixed by countless
good people.

View File

@ -1,18 +0,0 @@
/*
* Copyright © 2017 Kosma Moczek <kosma@cloudyourcar.com>
* This program is free software. It comes without any warranty, to the extent
* permitted by applicable law. You can redistribute it and/or modify it under
* the terms of the Do What The Fuck You Want To Public License, Version 2, as
* published by Sam Hocevar. See the COPYING file for more details.
*/
#if defined(__TI_ARM__)
// timespec definition
#include <ti/sysbios/posix/types.h>
#define timegm mktime
#endif
/* vim: set ts=4 sw=4 et: */

View File

@ -1,23 +0,0 @@
/*
* Copyright © 2017 Kosma Moczek <kosma@cloudyourcar.com>
* This program is free software. It comes without any warranty, to the extent
* permitted by applicable law. You can redistribute it and/or modify it under
* the terms of the Do What The Fuck You Want To Public License, Version 2, as
* published by Sam Hocevar. See the COPYING file for more details.
*/
#if defined(_MSC_VER)
#if !defined(HAVE_STRUCT_TIMESPEC)
struct timespec {
time_t tv_sec;
long tv_nsec;
};
#endif
#define inline __inline
#define timegm _mkgmtime
#endif
/* vim: set ts=4 sw=4 et: */

View File

@ -1,141 +0,0 @@
/*
* Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
* This program is free software. It comes without any warranty, to the extent
* permitted by applicable law. You can redistribute it and/or modify it under
* the terms of the Do What The Fuck You Want To Public License, Version 2, as
* published by Sam Hocevar. See the COPYING file for more details.
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "minmea.h"
#define INDENT_SPACES " "
int main(void)
{
char line[MINMEA_MAX_LENGTH];
while (fgets(line, sizeof(line), stdin) != NULL) {
printf("%s", line);
switch (minmea_sentence_id(line, false)) {
case MINMEA_SENTENCE_RMC: {
struct minmea_sentence_rmc frame;
if (minmea_parse_rmc(&frame, line)) {
printf(INDENT_SPACES "$xxRMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n",
frame.latitude.value, frame.latitude.scale,
frame.longitude.value, frame.longitude.scale,
frame.speed.value, frame.speed.scale);
printf(INDENT_SPACES "$xxRMC fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n",
minmea_rescale(&frame.latitude, 1000),
minmea_rescale(&frame.longitude, 1000),
minmea_rescale(&frame.speed, 1000));
printf(INDENT_SPACES "$xxRMC floating point degree coordinates and speed: (%f,%f) %f\n",
minmea_tocoord(&frame.latitude),
minmea_tocoord(&frame.longitude),
minmea_tofloat(&frame.speed));
}
else {
printf(INDENT_SPACES "$xxRMC sentence is not parsed\n");
}
} break;
case MINMEA_SENTENCE_GGA: {
struct minmea_sentence_gga frame;
if (minmea_parse_gga(&frame, line)) {
printf(INDENT_SPACES "$xxGGA: fix quality: %d\n", frame.fix_quality);
}
else {
printf(INDENT_SPACES "$xxGGA sentence is not parsed\n");
}
} break;
case MINMEA_SENTENCE_GST: {
struct minmea_sentence_gst frame;
if (minmea_parse_gst(&frame, line)) {
printf(INDENT_SPACES "$xxGST: raw latitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/%d)\n",
frame.latitude_error_deviation.value, frame.latitude_error_deviation.scale,
frame.longitude_error_deviation.value, frame.longitude_error_deviation.scale,
frame.altitude_error_deviation.value, frame.altitude_error_deviation.scale);
printf(INDENT_SPACES "$xxGST fixed point latitude,longitude and altitude error deviation"
" scaled to one decimal place: (%d,%d,%d)\n",
minmea_rescale(&frame.latitude_error_deviation, 10),
minmea_rescale(&frame.longitude_error_deviation, 10),
minmea_rescale(&frame.altitude_error_deviation, 10));
printf(INDENT_SPACES "$xxGST floating point degree latitude, longitude and altitude error deviation: (%f,%f,%f)",
minmea_tofloat(&frame.latitude_error_deviation),
minmea_tofloat(&frame.longitude_error_deviation),
minmea_tofloat(&frame.altitude_error_deviation));
}
else {
printf(INDENT_SPACES "$xxGST sentence is not parsed\n");
}
} break;
case MINMEA_SENTENCE_GSV: {
struct minmea_sentence_gsv frame;
if (minmea_parse_gsv(&frame, line)) {
printf(INDENT_SPACES "$xxGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs);
printf(INDENT_SPACES "$xxGSV: sattelites in view: %d\n", frame.total_sats);
for (int i = 0; i < 4; i++)
printf(INDENT_SPACES "$xxGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n",
frame.sats[i].nr,
frame.sats[i].elevation,
frame.sats[i].azimuth,
frame.sats[i].snr);
}
else {
printf(INDENT_SPACES "$xxGSV sentence is not parsed\n");
}
} break;
case MINMEA_SENTENCE_VTG: {
struct minmea_sentence_vtg frame;
if (minmea_parse_vtg(&frame, line)) {
printf(INDENT_SPACES "$xxVTG: true track degrees = %f\n",
minmea_tofloat(&frame.true_track_degrees));
printf(INDENT_SPACES " magnetic track degrees = %f\n",
minmea_tofloat(&frame.magnetic_track_degrees));
printf(INDENT_SPACES " speed knots = %f\n",
minmea_tofloat(&frame.speed_knots));
printf(INDENT_SPACES " speed kph = %f\n",
minmea_tofloat(&frame.speed_kph));
}
else {
printf(INDENT_SPACES "$xxVTG sentence is not parsed\n");
}
} break;
case MINMEA_SENTENCE_ZDA: {
struct minmea_sentence_zda frame;
if (minmea_parse_zda(&frame, line)) {
printf(INDENT_SPACES "$xxZDA: %d:%d:%d %02d.%02d.%d UTC%+03d:%02d\n",
frame.time.hours,
frame.time.minutes,
frame.time.seconds,
frame.date.day,
frame.date.month,
frame.date.year,
frame.hour_offset,
frame.minute_offset);
}
else {
printf(INDENT_SPACES "$xxZDA sentence is not parsed\n");
}
} break;
case MINMEA_INVALID: {
printf(INDENT_SPACES "$xxxxx sentence is not valid\n");
} break;
default: {
printf(INDENT_SPACES "$xxxxx sentence is not parsed\n");
} break;
}
}
return 0;
}
/* vim: set ts=4 sw=4 et: */

View File

@ -1,645 +0,0 @@
/*
* Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
* This program is free software. It comes without any warranty, to the extent
* permitted by applicable law. You can redistribute it and/or modify it under
* the terms of the Do What The Fuck You Want To Public License, Version 2, as
* published by Sam Hocevar. See the COPYING file for more details.
*/
#include "minmea.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
#define boolstr(s) ((s) ? "true" : "false")
static int hex2int(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return -1;
}
uint8_t minmea_checksum(const char *sentence)
{
// Support senteces with or without the starting dollar sign.
if (*sentence == '$')
sentence++;
uint8_t checksum = 0x00;
// The optional checksum is an XOR of all bytes between "$" and "*".
while (*sentence && *sentence != '*')
checksum ^= *sentence++;
return checksum;
}
bool minmea_check(const char *sentence, bool strict)
{
uint8_t checksum = 0x00;
// Sequence length is limited.
if (strlen(sentence) > MINMEA_MAX_LENGTH + 3)
return false;
// A valid sentence starts with "$".
if (*sentence++ != '$')
return false;
// The optional checksum is an XOR of all bytes between "$" and "*".
while (*sentence && *sentence != '*' && isprint((unsigned char) *sentence))
checksum ^= *sentence++;
// If checksum is present...
if (*sentence == '*') {
// Extract checksum.
sentence++;
int upper = hex2int(*sentence++);
if (upper == -1)
return false;
int lower = hex2int(*sentence++);
if (lower == -1)
return false;
int expected = upper << 4 | lower;
// Check for checksum mismatch.
if (checksum != expected)
return false;
} else if (strict) {
// Discard non-checksummed frames in strict mode.
return false;
}
// The only stuff allowed at this point is a newline.
if (*sentence && strcmp(sentence, "\n") && strcmp(sentence, "\r\n"))
return false;
return true;
}
static inline bool minmea_isfield(char c) {
return isprint((unsigned char) c) && c != ',' && c != '*';
}
bool minmea_scan(const char *sentence, const char *format, ...)
{
bool result = false;
bool optional = false;
va_list ap;
va_start(ap, format);
const char *field = sentence;
#define next_field() \
do { \
/* Progress to the next field. */ \
while (minmea_isfield(*sentence)) \
sentence++; \
/* Make sure there is a field there. */ \
if (*sentence == ',') { \
sentence++; \
field = sentence; \
} else { \
field = NULL; \
} \
} while (0)
while (*format) {
char type = *format++;
if (type == ';') {
// All further fields are optional.
optional = true;
continue;
}
if (!field && !optional) {
// Field requested but we ran out if input. Bail out.
goto parse_error;
}
switch (type) {
case 'c': { // Single character field (char).
char value = '\0';
if (field && minmea_isfield(*field))
value = *field;
*va_arg(ap, char *) = value;
} break;
case 'd': { // Single character direction field (int).
int value = 0;
if (field && minmea_isfield(*field)) {
switch (*field) {
case 'N':
case 'E':
value = 1;
break;
case 'S':
case 'W':
value = -1;
break;
default:
goto parse_error;
}
}
*va_arg(ap, int *) = value;
} break;
case 'f': { // Fractional value with scale (struct minmea_float).
int sign = 0;
int_least32_t value = -1;
int_least32_t scale = 0;
if (field) {
while (minmea_isfield(*field)) {
if (*field == '+' && !sign && value == -1) {
sign = 1;
} else if (*field == '-' && !sign && value == -1) {
sign = -1;
} else if (isdigit((unsigned char) *field)) {
int digit = *field - '0';
if (value == -1)
value = 0;
if (value > (INT_LEAST32_MAX-digit) / 10) {
/* we ran out of bits, what do we do? */
if (scale) {
/* truncate extra precision */
break;
} else {
/* integer overflow. bail out. */
goto parse_error;
}
}
value = (10 * value) + digit;
if (scale)
scale *= 10;
} else if (*field == '.' && scale == 0) {
scale = 1;
} else if (*field == ' ') {
/* Allow spaces at the start of the field. Not NMEA
* conformant, but some modules do this. */
if (sign != 0 || value != -1 || scale != 0)
goto parse_error;
} else {
goto parse_error;
}
field++;
}
}
if ((sign || scale) && value == -1)
goto parse_error;
if (value == -1) {
/* No digits were scanned. */
value = 0;
scale = 0;
} else if (scale == 0) {
/* No decimal point. */
scale = 1;
}
if (sign)
value *= sign;
*va_arg(ap, struct minmea_float *) = (struct minmea_float) {value, scale};
} break;
case 'i': { // Integer value, default 0 (int).
int value = 0;
if (field) {
char *endptr;
value = strtol(field, &endptr, 10);
if (minmea_isfield(*endptr))
goto parse_error;
}
*va_arg(ap, int *) = value;
} break;
case 's': { // String value (char *).
char *buf = va_arg(ap, char *);
if (field) {
while (minmea_isfield(*field))
*buf++ = *field++;
}
*buf = '\0';
} break;
case 't': { // NMEA talker+sentence identifier (char *).
// This field is always mandatory.
if (!field)
goto parse_error;
if (field[0] != '$')
goto parse_error;
for (int f=0; f<5; f++)
if (!minmea_isfield(field[1+f]))
goto parse_error;
char *buf = va_arg(ap, char *);
memcpy(buf, field+1, 5);
buf[5] = '\0';
} break;
case 'D': { // Date (int, int, int), -1 if empty.
struct minmea_date *date = va_arg(ap, struct minmea_date *);
int d = -1, m = -1, y = -1;
if (field && minmea_isfield(*field)) {
// Always six digits.
for (int f=0; f<6; f++)
if (!isdigit((unsigned char) field[f]))
goto parse_error;
char dArr[] = {field[0], field[1], '\0'};
char mArr[] = {field[2], field[3], '\0'};
char yArr[] = {field[4], field[5], '\0'};
d = strtol(dArr, NULL, 10);
m = strtol(mArr, NULL, 10);
y = strtol(yArr, NULL, 10);
}
date->day = d;
date->month = m;
date->year = y;
} break;
case 'T': { // Time (int, int, int, int), -1 if empty.
struct minmea_time *time_ = va_arg(ap, struct minmea_time *);
int h = -1, i = -1, s = -1, u = -1;
if (field && minmea_isfield(*field)) {
// Minimum required: integer time.
for (int f=0; f<6; f++)
if (!isdigit((unsigned char) field[f]))
goto parse_error;
char hArr[] = {field[0], field[1], '\0'};
char iArr[] = {field[2], field[3], '\0'};
char sArr[] = {field[4], field[5], '\0'};
h = strtol(hArr, NULL, 10);
i = strtol(iArr, NULL, 10);
s = strtol(sArr, NULL, 10);
field += 6;
// Extra: fractional time. Saved as microseconds.
if (*field++ == '.') {
uint32_t value = 0;
uint32_t scale = 1000000LU;
while (isdigit((unsigned char) *field) && scale > 1) {
value = (value * 10) + (*field++ - '0');
scale /= 10;
}
u = value * scale;
} else {
u = 0;
}
}
time_->hours = h;
time_->minutes = i;
time_->seconds = s;
time_->microseconds = u;
} break;
case '_': { // Ignore the field.
} break;
default: { // Unknown.
goto parse_error;
}
}
next_field();
}
result = true;
parse_error:
va_end(ap);
return result;
}
bool minmea_talker_id(char talker[3], const char *sentence)
{
char type[6];
if (!minmea_scan(sentence, "t", type))
return false;
talker[0] = type[0];
talker[1] = type[1];
talker[2] = '\0';
return true;
}
enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict)
{
if (!minmea_check(sentence, strict))
return MINMEA_INVALID;
char type[6];
if (!minmea_scan(sentence, "t", type))
return MINMEA_INVALID;
if (!strcmp(type+2, "RMC"))
return MINMEA_SENTENCE_RMC;
if (!strcmp(type+2, "GGA"))
return MINMEA_SENTENCE_GGA;
if (!strcmp(type+2, "GSA"))
return MINMEA_SENTENCE_GSA;
if (!strcmp(type+2, "GLL"))
return MINMEA_SENTENCE_GLL;
if (!strcmp(type+2, "GST"))
return MINMEA_SENTENCE_GST;
if (!strcmp(type+2, "GSV"))
return MINMEA_SENTENCE_GSV;
if (!strcmp(type+2, "VTG"))
return MINMEA_SENTENCE_VTG;
if (!strcmp(type+2, "ZDA"))
return MINMEA_SENTENCE_ZDA;
return MINMEA_UNKNOWN;
}
bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence)
{
// $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
char type[6];
char validity;
int latitude_direction;
int longitude_direction;
int variation_direction;
if (!minmea_scan(sentence, "tTcfdfdffDfd",
type,
&frame->time,
&validity,
&frame->latitude, &latitude_direction,
&frame->longitude, &longitude_direction,
&frame->speed,
&frame->course,
&frame->date,
&frame->variation, &variation_direction))
return false;
if (strcmp(type+2, "RMC"))
return false;
frame->valid = (validity == 'A');
frame->latitude.value *= latitude_direction;
frame->longitude.value *= longitude_direction;
frame->variation.value *= variation_direction;
return true;
}
bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence)
{
// $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
char type[6];
int latitude_direction;
int longitude_direction;
if (!minmea_scan(sentence, "tTfdfdiiffcfcf_",
type,
&frame->time,
&frame->latitude, &latitude_direction,
&frame->longitude, &longitude_direction,
&frame->fix_quality,
&frame->satellites_tracked,
&frame->hdop,
&frame->altitude, &frame->altitude_units,
&frame->height, &frame->height_units,
&frame->dgps_age))
return false;
if (strcmp(type+2, "GGA"))
return false;
frame->latitude.value *= latitude_direction;
frame->longitude.value *= longitude_direction;
return true;
}
bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence)
{
// $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39
char type[6];
if (!minmea_scan(sentence, "tciiiiiiiiiiiiifff",
type,
&frame->mode,
&frame->fix_type,
&frame->sats[0],
&frame->sats[1],
&frame->sats[2],
&frame->sats[3],
&frame->sats[4],
&frame->sats[5],
&frame->sats[6],
&frame->sats[7],
&frame->sats[8],
&frame->sats[9],
&frame->sats[10],
&frame->sats[11],
&frame->pdop,
&frame->hdop,
&frame->vdop))
return false;
if (strcmp(type+2, "GSA"))
return false;
return true;
}
bool minmea_parse_gll(struct minmea_sentence_gll *frame, const char *sentence)
{
// $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41$;
char type[6];
int latitude_direction;
int longitude_direction;
if (!minmea_scan(sentence, "tfdfdTc;c",
type,
&frame->latitude, &latitude_direction,
&frame->longitude, &longitude_direction,
&frame->time,
&frame->status,
&frame->mode))
return false;
if (strcmp(type+2, "GLL"))
return false;
frame->latitude.value *= latitude_direction;
frame->longitude.value *= longitude_direction;
return true;
}
bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence)
{
// $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58
char type[6];
if (!minmea_scan(sentence, "tTfffffff",
type,
&frame->time,
&frame->rms_deviation,
&frame->semi_major_deviation,
&frame->semi_minor_deviation,
&frame->semi_major_orientation,
&frame->latitude_error_deviation,
&frame->longitude_error_deviation,
&frame->altitude_error_deviation))
return false;
if (strcmp(type+2, "GST"))
return false;
return true;
}
bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence)
{
// $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74
// $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D
// $GPGSV,4,2,11,08,51,203,30,09,45,215,28*75
// $GPGSV,4,4,13,39,31,170,27*40
// $GPGSV,4,4,13*7B
char type[6];
if (!minmea_scan(sentence, "tiii;iiiiiiiiiiiiiiii",
type,
&frame->total_msgs,
&frame->msg_nr,
&frame->total_sats,
&frame->sats[0].nr,
&frame->sats[0].elevation,
&frame->sats[0].azimuth,
&frame->sats[0].snr,
&frame->sats[1].nr,
&frame->sats[1].elevation,
&frame->sats[1].azimuth,
&frame->sats[1].snr,
&frame->sats[2].nr,
&frame->sats[2].elevation,
&frame->sats[2].azimuth,
&frame->sats[2].snr,
&frame->sats[3].nr,
&frame->sats[3].elevation,
&frame->sats[3].azimuth,
&frame->sats[3].snr
)) {
return false;
}
if (strcmp(type+2, "GSV"))
return false;
return true;
}
bool minmea_parse_vtg(struct minmea_sentence_vtg *frame, const char *sentence)
{
// $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48
// $GPVTG,156.1,T,140.9,M,0.0,N,0.0,K*41
// $GPVTG,096.5,T,083.5,M,0.0,N,0.0,K,D*22
// $GPVTG,188.36,T,,M,0.820,N,1.519,K,A*3F
char type[6];
char c_true, c_magnetic, c_knots, c_kph, c_faa_mode;
if (!minmea_scan(sentence, "tfcfcfcfc;c",
type,
&frame->true_track_degrees,
&c_true,
&frame->magnetic_track_degrees,
&c_magnetic,
&frame->speed_knots,
&c_knots,
&frame->speed_kph,
&c_kph,
&c_faa_mode))
return false;
if (strcmp(type+2, "VTG"))
return false;
// check chars
if (c_true != 'T' ||
c_magnetic != 'M' ||
c_knots != 'N' ||
c_kph != 'K')
return false;
frame->faa_mode = (enum minmea_faa_mode)c_faa_mode;
return true;
}
bool minmea_parse_zda(struct minmea_sentence_zda *frame, const char *sentence)
{
// $GPZDA,201530.00,04,07,2002,00,00*60
char type[6];
if(!minmea_scan(sentence, "tTiiiii",
type,
&frame->time,
&frame->date.day,
&frame->date.month,
&frame->date.year,
&frame->hour_offset,
&frame->minute_offset))
return false;
if (strcmp(type+2, "ZDA"))
return false;
// check offsets
if (abs(frame->hour_offset) > 13 ||
frame->minute_offset > 59 ||
frame->minute_offset < 0)
return false;
return true;
}
int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_)
{
if (date->year == -1 || time_->hours == -1)
return -1;
struct tm tm;
memset(&tm, 0, sizeof(tm));
if (date->year < 80) {
tm.tm_year = 2000 + date->year - 1900; // 2000-2079
} else if (date->year >= 1900) {
tm.tm_year = date->year - 1900; // 4 digit year, use directly
} else {
tm.tm_year = date->year; // 1980-1999
}
tm.tm_mon = date->month - 1;
tm.tm_mday = date->day;
tm.tm_hour = time_->hours;
tm.tm_min = time_->minutes;
tm.tm_sec = time_->seconds;
time_t timestamp = timegm(&tm); /* See README.md if your system lacks timegm(). */
if (timestamp != (time_t)-1) {
ts->tv_sec = timestamp;
ts->tv_nsec = time_->microseconds * 1000;
return 0;
} else {
return -1;
}
}
/* vim: set ts=4 sw=4 et: */

View File

@ -1,261 +0,0 @@
/*
* Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
* This program is free software. It comes without any warranty, to the extent
* permitted by applicable law. You can redistribute it and/or modify it under
* the terms of the Do What The Fuck You Want To Public License, Version 2, as
* published by Sam Hocevar. See the COPYING file for more details.
*/
#ifndef MINMEA_H
#define MINMEA_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <time.h>
#include <math.h>
#ifdef MINMEA_INCLUDE_COMPAT
#include <minmea_compat.h>
#endif
#define MINMEA_MAX_LENGTH 80
enum minmea_sentence_id {
MINMEA_INVALID = -1,
MINMEA_UNKNOWN = 0,
MINMEA_SENTENCE_RMC,
MINMEA_SENTENCE_GGA,
MINMEA_SENTENCE_GSA,
MINMEA_SENTENCE_GLL,
MINMEA_SENTENCE_GST,
MINMEA_SENTENCE_GSV,
MINMEA_SENTENCE_VTG,
MINMEA_SENTENCE_ZDA,
};
struct minmea_float {
int_least32_t value;
int_least32_t scale;
};
struct minmea_date {
int day;
int month;
int year;
};
struct minmea_time {
int hours;
int minutes;
int seconds;
int microseconds;
};
struct minmea_sentence_rmc {
struct minmea_time time;
bool valid;
struct minmea_float latitude;
struct minmea_float longitude;
struct minmea_float speed;
struct minmea_float course;
struct minmea_date date;
struct minmea_float variation;
};
struct minmea_sentence_gga {
struct minmea_time time;
struct minmea_float latitude;
struct minmea_float longitude;
int fix_quality;
int satellites_tracked;
struct minmea_float hdop;
struct minmea_float altitude; char altitude_units;
struct minmea_float height; char height_units;
struct minmea_float dgps_age;
};
enum minmea_gll_status {
MINMEA_GLL_STATUS_DATA_VALID = 'A',
MINMEA_GLL_STATUS_DATA_NOT_VALID = 'V',
};
// FAA mode added to some fields in NMEA 2.3.
enum minmea_faa_mode {
MINMEA_FAA_MODE_AUTONOMOUS = 'A',
MINMEA_FAA_MODE_DIFFERENTIAL = 'D',
MINMEA_FAA_MODE_ESTIMATED = 'E',
MINMEA_FAA_MODE_MANUAL = 'M',
MINMEA_FAA_MODE_SIMULATED = 'S',
MINMEA_FAA_MODE_NOT_VALID = 'N',
MINMEA_FAA_MODE_PRECISE = 'P',
};
struct minmea_sentence_gll {
struct minmea_float latitude;
struct minmea_float longitude;
struct minmea_time time;
char status;
char mode;
};
struct minmea_sentence_gst {
struct minmea_time time;
struct minmea_float rms_deviation;
struct minmea_float semi_major_deviation;
struct minmea_float semi_minor_deviation;
struct minmea_float semi_major_orientation;
struct minmea_float latitude_error_deviation;
struct minmea_float longitude_error_deviation;
struct minmea_float altitude_error_deviation;
};
enum minmea_gsa_mode {
MINMEA_GPGSA_MODE_AUTO = 'A',
MINMEA_GPGSA_MODE_FORCED = 'M',
};
enum minmea_gsa_fix_type {
MINMEA_GPGSA_FIX_NONE = 1,
MINMEA_GPGSA_FIX_2D = 2,
MINMEA_GPGSA_FIX_3D = 3,
};
struct minmea_sentence_gsa {
char mode;
int fix_type;
int sats[12];
struct minmea_float pdop;
struct minmea_float hdop;
struct minmea_float vdop;
};
struct minmea_sat_info {
int nr;
int elevation;
int azimuth;
int snr;
};
struct minmea_sentence_gsv {
int total_msgs;
int msg_nr;
int total_sats;
struct minmea_sat_info sats[4];
};
struct minmea_sentence_vtg {
struct minmea_float true_track_degrees;
struct minmea_float magnetic_track_degrees;
struct minmea_float speed_knots;
struct minmea_float speed_kph;
enum minmea_faa_mode faa_mode;
};
struct minmea_sentence_zda {
struct minmea_time time;
struct minmea_date date;
int hour_offset;
int minute_offset;
};
/**
* Calculate raw sentence checksum. Does not check sentence integrity.
*/
uint8_t minmea_checksum(const char *sentence);
/**
* Check sentence validity and checksum. Returns true for valid sentences.
*/
bool minmea_check(const char *sentence, bool strict);
/**
* Determine talker identifier.
*/
bool minmea_talker_id(char talker[3], const char *sentence);
/**
* Determine sentence identifier.
*/
enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict);
/**
* Scanf-like processor for NMEA sentences. Supports the following formats:
* c - single character (char *)
* d - direction, returned as 1/-1, default 0 (int *)
* f - fractional, returned as value + scale (int *, int *)
* i - decimal, default zero (int *)
* s - string (char *)
* t - talker identifier and type (char *)
* T - date/time stamp (int *, int *, int *)
* Returns true on success. See library source code for details.
*/
bool minmea_scan(const char *sentence, const char *format, ...);
/*
* Parse a specific type of sentence. Return true on success.
*/
bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence);
bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence);
bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence);
bool minmea_parse_gll(struct minmea_sentence_gll *frame, const char *sentence);
bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence);
bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence);
bool minmea_parse_vtg(struct minmea_sentence_vtg *frame, const char *sentence);
bool minmea_parse_zda(struct minmea_sentence_zda *frame, const char *sentence);
/**
* Convert GPS UTC date/time representation to a UNIX timestamp.
*/
int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_);
/**
* Rescale a fixed-point value to a different scale. Rounds towards zero.
*/
static inline int_least32_t minmea_rescale(struct minmea_float *f, int_least32_t new_scale)
{
if (f->scale == 0)
return 0;
if (f->scale == new_scale)
return f->value;
if (f->scale > new_scale)
return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale/new_scale/2) / (f->scale/new_scale);
else
return f->value * (new_scale/f->scale);
}
/**
* Convert a fixed-point value to a floating-point value.
* Returns NaN for "unknown" values.
*/
static inline float minmea_tofloat(struct minmea_float *f)
{
if (f->scale == 0)
return NAN;
return (float) f->value / (float) f->scale;
}
/**
* Convert a raw coordinate to a floating point DD.DDD... value.
* Returns NaN for "unknown" values.
*/
static inline float minmea_tocoord(struct minmea_float *f)
{
if (f->scale == 0)
return NAN;
int_least32_t degrees = f->value / (f->scale * 100);
int_least32_t minutes = f->value % (f->scale * 100);
return (float) degrees + (float) minutes / (60 * f->scale);
}
#ifdef __cplusplus
}
#endif
#endif /* MINMEA_H */
/* vim: set ts=4 sw=4 et: */

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30804.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDR", "project\PDR.vcxproj", "{28D32532-309F-40EA-9E4A-2D162CC434D2}"
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
{28D32532-309F-40EA-9E4A-2D162CC434D2}.Debug|x64.ActiveCfg = Debug|x64
{28D32532-309F-40EA-9E4A-2D162CC434D2}.Debug|x64.Build.0 = Debug|x64
{28D32532-309F-40EA-9E4A-2D162CC434D2}.Debug|x86.ActiveCfg = Debug|Win32
{28D32532-309F-40EA-9E4A-2D162CC434D2}.Debug|x86.Build.0 = Debug|Win32
{28D32532-309F-40EA-9E4A-2D162CC434D2}.Release|x64.ActiveCfg = Release|x64
{28D32532-309F-40EA-9E4A-2D162CC434D2}.Release|x64.Build.0 = Release|x64
{28D32532-309F-40EA-9E4A-2D162CC434D2}.Release|x86.ActiveCfg = Release|Win32
{28D32532-309F-40EA-9E4A-2D162CC434D2}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B6378B77-5AB0-4A12-ACE8-2B45F292D7EE}
EndGlobalSection
EndGlobal

View File

@ -1,48 +0,0 @@
#ifndef _PDR_AHRS_H_
#define _PDR_AHRS_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Header File Including ------------------------------------------------------------------------ */
#include "pdr_sensor.h"
/* Macro Declaration ---------------------------------------------------------------------------- */
#define AHRS_SAMPLE_FREQ 100 // ARHS算法的传感器采样频率
#define IMU_SAMPLING_FREQUENCY 100
#define AHRS_KP 0.500
#define AHRS_TYPE_GYRO 4
#define AHRS_TYPE_ACCE 1
#define AHRS_TYPE_MAGN 2
#define AHRS_STATUS_RESET_PHASE_0 0x80
#define AHRS_STATUS_RESET_PHASE_1 0x40
#define AHRS_STATUS_RESET (AHRS_STATUS_RESET_PHASE_0|AHRS_STATUS_RESET_PHASE_1)
#define AHRS_STATUS_STABLE 0x20
/* Struct Declaration --------------------------------------------------------------------------- */
/* Function Declaration ------------------------------------------------------------------------- */
void AHRS_Init(void);
void ResetARHS(void);
/**---------------------------------------------------------------------
* Function : fv3Norm
* Description :
* Date : 2021/01/26 yuanlin@vivocom &&
*
*
*---------------------------------------------------------------------**/
void fv3Norm(float* vx, float* vy, float* vz);
int UpdateAHRS(IMU_t* imu);
#ifdef __cplusplus
}
#endif
#endif // for __AHRS_H

View File

@ -1,78 +0,0 @@
#ifndef _PDR_BUFFER_H_
#define _PDR_BUFFER_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Header File Including ------------------------------------------------------------------------ */
/* Macro Declaration ---------------------------------------------------------------------------- */
#define BUFFER_LONG_LENGTH 256
#define BUFFER_SHORT_LENGTH 10
#define BUFFER_TYPE_STACK 0
#define BUFFER_TYPE_QUEUE 1
#define BUFFER_NO_ERROR 0
#define BUFFER_WRONG_PARAMETER 1
#define BUFFER_OUT_OF_MEMORY 2
/* Structure Declaration ------------------------------------------------------------------------ */
#pragma pack (4)
typedef struct BUFFER {
char name[20];
int type;
int length;
int _bottom;
int _top;
int reserved;
double sum;
double mean;
float data[BUFFER_LONG_LENGTH + 1];
}Buffer_t;
typedef struct BUFFER_LONG {
char name[20];
int type;
int length;
int _bottom;
int _top;
int reserved;
double sum;
double mean;
float data[BUFFER_LONG_LENGTH + 1];
} BUFFER_LONG;
typedef struct BUFFER_SHORT {
char name[20];
int type;
int length;
int _bottom;
int _top;
int reserved;
double sum;
double mean;
float data[BUFFER_SHORT_LENGTH + 1];
} BUFFER_SHORT;
#pragma pack ()
/* Global Variable Declaration ------------------------------------------------------------------ */
/* Function Declaration ------------------------------------------------------------------------- */
int BufferInit(Buffer_t *buffer, const char *name, int type, int length);
int BufferClear(Buffer_t *buffer);
int BufferCount(Buffer_t *buffer, int *count);
int BufferGetTop(Buffer_t *buffer, float *value);
int BufferGetBottom(Buffer_t *buffer, float *value);
int BufferPop(Buffer_t *buffer, float *value);
int BufferPush(Buffer_t *buffer, float value);
int BufferGet(Buffer_t *buffer, float *value, int index);
int BufferMean(Buffer_t *buffer, float *mean);
int BufferVar(Buffer_t *buffer, float *var);
int BufferStd(Buffer_t *buffer, float *std);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,18 +0,0 @@
#ifndef __PDR_FFT_H__
#define __PDR_FFT_H__
/* Header File Including ------------------------------------------------------------------------ */
#include <stdint.h>
#include <math.h>
#include "Complex.h"
/* Macro Declaration ---------------------------------------------------------------------------- */
#define M_PI 3.14159265358979323846 /* pi */
/* Structure Declaration ------------------------------------------------------------------------ */
/* Function Declaration ------------------------------------------------------------------------- */
extern int FFT_Dft(Complex_t *fft, float *signal, int length);
extern int FFT_Fft(Complex_t *fft, float *signal, int length);
#endif

View File

@ -1,52 +0,0 @@
/******************** (C) COPYRIGHT 2022 Geek************************************
* File Name : Filter.c
* Current Version : V2.0
* Author : logzhan
* Date of Issued : 2022.10.15
* Comments : PDR
********************************************************************************/
/* Header File Including ------------------------------------------------------*/
#ifndef __FILTER_H__
#define __FILTER_H__
/* Macro Declaration ----------------------------------------------------------*/
#define FILTER_MAX_LENGTH 71
/* Structure Declaration ------------------------------------------------------*/
typedef struct Filter_t { // iir or fir filter
int reset; // reset flag
int order; // coefficient array length, not filter order
double H0; // dc signal gain
double b[FILTER_MAX_LENGTH]; // b coefficient
double a[FILTER_MAX_LENGTH]; // a coefficient
double yout[FILTER_MAX_LENGTH]; // yout buffer
double xin[FILTER_MAX_LENGTH]; // xin buffer
} Filter_t;
/* Function Declaration -------------------------------------------------------*/
/**---------------------------------------------------------------------
* Function : FilterReset
* Description :
* Date : 2022/10/15 logzhan
*---------------------------------------------------------------------**/
void FilterReset(Filter_t *f);
/**---------------------------------------------------------------------
* Function : FilterSetCoef
* Description :
* Input : order coefficient array length, not filter order
* *b b coefficient
* *a a coefficient
* H0 dc signal gain
* Date : 2022/10/15 logzhan
*---------------------------------------------------------------------**/
void FilterSetCoef(Filter_t *f, int order, double *b, double *a, double H0);
/**---------------------------------------------------------------------
* Function : FilterFilter
* Description :
* Date : 2022/10/15 logzhan
*---------------------------------------------------------------------**/
double FilterFilter(Filter_t *f, double x);
#endif

View File

@ -1,54 +0,0 @@
/******************** (C) COPYRIGHT 2020 Geek************************************
* File Name : pdr_kalman.h
* Department : Sensor Algorithm Team
* Current Version : V2.0(compare QCOM SAP 5.0)
* Author : logzhan
* Date of Issued : 2020.7.3
* Comments : PDR
********************************************************************************/
#ifndef _PDR_KALMAN_H_
#define _PDR_KALMAN_H_
#ifdef __cplusplus
extern "C" {
#endif
/**----------------------------------------------------------------------
* Function : EKF_Init
* Description :
* Date : 2022/09/19
*---------------------------------------------------------------------**/
void EKF_Init(void);
/**----------------------------------------------------------------------
* Function : EKFStatePredict
* Description : pdr
* Date : 2022/09/19 logzhan
*---------------------------------------------------------------------**/
void EKFStatePredict(EKFPara_t* kf, double step_length, PDR_t* g_pdr, int step);
/**----------------------------------------------------------------------
* Function : EKFCalQRMatrix
* Description :
* scane_type,
* nmea_dataNMEA
* g_pdrPDR
* sys
* kfEKF
* Date : 2022/09/19 logzhan
*---------------------------------------------------------------------**/
void EKFCalQRMatrix(Nmea_t* nmea_data, PDR_t* g_pdr, Classifer_t* sys, EKFPara_t* kf);
/**----------------------------------------------------------------------
* Function : pdr_ekfStateUpdate
* Description : pdr
* Date : 2020/7/22 logzhan
*---------------------------------------------------------------------**/
void EKFStateUpdate(EKFPara_t* kf, GNSS_t* pgnss, Classifer_t* sys, PDR_t* g_pdr,
int scene_type);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,57 +0,0 @@
/******************** (C) COPYRIGHT 2020 Geek************************************
* File Name : LapProcess.h
* Department : Sensor Algorithm Team
* Current Version : V1.0
* Author : logzhan
* Date of Issued : 2020.8.4
* Comments : ÅÜȦģʽÓÅ»¯
********************************************************************************/
#ifndef _LAPPROCESS_H_
#define _LAPPROCESS_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Header File Including ------------------------------------------------------------------------ */
#include <stdio.h>
#include <math.h>
#include <string.h>
#include "Utils.h"
#include "pdr_sensor.h"
#include "pdr_base.h"
/* Macro Definition ----------------------------------------------------------------------------- */
#define FIND_CIRCLE 1
#define NO_CIRCLE 0
#define ADJ_CIRCLE 1
#define NADJ_CIRCLE 0
#define DIST_THRESHOLD 25
/* Function Declaration ------------------------------------------------------------------------- */
void lapInit();
void lapProcess(GNSS_t pgnss, LctFs_t *result);
void pdr_cacheTrajs(GNSS_t pgnss, LctFs_t*result);
int pdr_adjustTraj(double trajs[][2], int count);
double pdr_getPointYaw(double point1[], double point2[]);
void pdr_pathMap(TrajSign* trajs, double point[2], double yaw, double projPoint[2]);
double pdr_optiFromTrajs(GNSS_t pgnss, LctFs_t*lct_fusion);
int pdr_findTun(TrajSign* refTrajs, double point[], double heading);
int pdr_fuseRefTraj(TrajSign* tunnelTrajs, TrajSign* refTrajs, double newRefLoc[][2]);
unsigned char pdr_point2line(double point[], double lineStart[], double lineEnd[], double projLoc[]);
void ProjPointOfLoop(double point1[], double yaw, double point2[], double result[]);
double lap_calDistance(double lla1[2], double lla2[2]);
#ifdef __cplusplus
}
#endif
#endif // !_LAPPROCESS_H_

View File

@ -1,66 +0,0 @@
/******************** (C) COPYRIGHT 2020 Geek************************************
* File Name : pdr_linearFit.h
* Department : Sensor Algorithm Team
* Current Version : V2.0(compare QCOM SAP 5.0)
* Author :
logzhan
* Date of Issued : 2020.7.4
* Comments : PDR 线
********************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _PDR_LINEAR_FIT_H_
#define _PDR_LINEAR_FIT_H_
/**----------------------------------------------------------------------
* Function : pdr_linearLeastSquaresFitting
* Description : 线 Y = c0 + c1 x
sum| nDataY - (a*nDataX+b)|^2
* Date : 2020/7/4 logzhan
*---------------------------------------------------------------------**/
int linearLeastSquaresFit(const double *nDataX, const double *nDataY,
const int nLength, double *a, double *b);
/**************************************************************************
* Description : 线ax+by+c=0, 线
* Input : x yXY
num,
* Output : para 线, para[0]:a, para[0]:b, para[0]:c
**************************************************************************/
int leastDistanceLinearFit(double x[], double y[], int num, double para[]);
/**----------------------------------------------------------------------
* Function : gsl_fit_linear
* Description : Fit the data (x_i, y_i) to the linear relationship
Y = c0 + c1 x
returning,
c0, c1 -- coefficients
cov00, cov01, cov11 -- variance-covariance matrix of
c0 and c1,
sumsq -- sum of squares of residuals
This fit can be used in the case where the errors for
the data are uknown, but assumed equal for all points.
The resulting variance-covariance matrix estimates the
error in the coefficientsfrom the observed variance of
the points around the best fit line.
* Date :
*---------------------------------------------------------------------**/
static int gslFitLinear(const double* x, const size_t xstride,
const double* y, const size_t ystride,
const size_t n,
double* c0, double* c1,
double* cov_00, double* cov_01, double* cov_11, double* sumsq);
#endif // !_LINEAR_FITTING_H_
#ifdef __cplusplus
}
#endif

View File

@ -1,137 +0,0 @@
#ifndef __PDR_LOCATION_H__
#define __PDR_LOCATION_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "pdr_base.h"
#define HIST_GPS_NUM 8
#define ACCURACY_THRES 0.6f // 精度阈值
#define YAW_THRES 10.0f // Yaw角范围阈值
/**---------------------------------------------------------------------
* Function : NavSys_Init
* Description : PDR
* Date : 2022/9/16
*---------------------------------------------------------------------**/
void PDR_Init(void);
/**---------------------------------------------------------------------
* Function : InsLocation
* Description : PDR
* Date : 2022/09/16 logzhan
*---------------------------------------------------------------------**/
int InsLocation(IMU_t *ss_data, EKFPara_t *kf);
/**----------------------------------------------------------------------
* Function : pdr_noGpsPredict
* Description : gpsGPS10
* Date : 2020/07/08 logzhan
*---------------------------------------------------------------------**/
void NoGnssInfoPredict(EKFPara_t* kf, LctFs_t* result, PDR_t* g_pdr);
/**----------------------------------------------------------------------
* Function : Nmea2Gnss
* Description : nmeagnss
* Date : 2020/07/08 logzhan
*---------------------------------------------------------------------**/
void Nmea2Gnss(Nmea_t* nmea_data, GNSS_t* pgnss);
/**----------------------------------------------------------------------
* Function : pdr_detectFixMode
* Description : PDR,
* GPS
* Date : 2020/07/08 logzhan
* 2020/02/08 logzhan : -1INVAILD_GPS_YAW
*
*---------------------------------------------------------------------**/
int DetectFixMode(GNSS_t* pgnss, EKFPara_t* kf, PDR_t* g_pdr, LctFs_t* result);
/**----------------------------------------------------------------------
* Function : GnssCalHeading
* Description : GPS(0 - 360°)
* Date : 2022/09/16 logzhan
*
*---------------------------------------------------------------------**/
double GnssCalHeading(GNSS_t* pgnss);
/**----------------------------------------------------------------------
* Function : calPdrHeadingOffset
* Description : GPS
* Date : 2020/07/08 logzhan
*---------------------------------------------------------------------**/
void CalPdrHeadingOffset(Nmea_t* nmea_data, PDR_t* p_pdr);
/**---------------------------------------------------------------------
* Function : pdr_resetSysStatus
* Description : PDS
* Date : 2020/2/1 logzhan
*---------------------------------------------------------------------**/
void ResetSystemStatus(EKFPara_t* kf);
/**----------------------------------------------------------------------
* Function : pdr_gnssInsLocation
* Description : PDR GPSINS
* Date : 2021/01/29 logzhan
*---------------------------------------------------------------------**/
int GnssInsFusionLocation(Nmea_t* nmea_data, EKFPara_t* kf, LctFs_t* result);
/**---------------------------------------------------------------------
* Function : pdr_initGnssInfo
* Description : PDS GNSS
* Date : 2020/2/1 logzhan
*---------------------------------------------------------------------**/
void InitGnssInfo(void);
/**----------------------------------------------------------------------
* Function : GnssUpdate
* Description : nmeagnss
* Date : 2020/07/08 logzhan
* 2020/02/09 logzhan : pdr_gpsUpdate
* GPSgnssnmea
*
*---------------------------------------------------------------------**/
void GnssUpdate(GNSS_t* gps, Nmea_t* nmea);
/**----------------------------------------------------------------------
* Function : OutputRawGnssPos
* Description : GPS
* Date : 2022/10/15 logzhan
*---------------------------------------------------------------------**/
void OutputRawGnssPos(GNSS_t* pgnss, LctFs_t* result);
/**----------------------------------------------------------------------
* Function : ResetOutputLoction
* Description : 1
* 2GPSresult
* Date : 2022/09/19 logzhan
*---------------------------------------------------------------------**/
int ResetOutputLoction(GNSS_t* pgnss, PDR_t* g_pdr, EKFPara_t* kf, LctFs_t* result);
/**----------------------------------------------------------------------
* Function : EkfGnssInsLocFusion
* Description : EKFGNSSINS
* Date : 2022/09/21 logzhan
*---------------------------------------------------------------------**/
void EkfGnssInsLocFusion(GNSS_t* pgnss, PDR_t* g_pdr, Classifer_t* sys, double yaw_bias,
EKFPara_t* kf, LctFs_t* res);
/**----------------------------------------------------------------------
* Function : PDRInitialAlignment
* Description : PDR
* Date : 2022/09/19 logzhan
*---------------------------------------------------------------------**/
int GnssInsInitialAlignment(GNSS_t* pgnss, EKFPara_t* kf, PDR_t* g_pdr, LctFs_t* result);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,43 +0,0 @@
#pragma once
#ifndef _LOCATION_TOOL_H_
#define _LOCATION_TOOL_H_
#include <stddef.h>
#ifndef PI
//#define PI 3.14159265358979323846
#endif // !PI
#ifndef EARTH_RADIUS
#define EARTH_RADIUS 6371008.8
#endif
typedef struct DoublePair {
double x;
double y;
} DoublePair;
typedef DoublePair PlaneCoordinate;
typedef struct LatLng{
double lat;
double lon;
} LatLng_t;
typedef struct DoublePairList{
double *x;
double *y;
size_t length;
} DoublePairList;
typedef struct LocationList{
double *lat;
double *lon;
size_t length;
} LocationList;
LatLng ProjPointOfLatLng(LatLng point, LatLng linePointA, LatLng linePointB);
DoublePair ProjPoint(DoublePair point, DoublePair linePointA, DoublePair linePointB);
//PlaneCoordinate WGS84_XYZ(const LatLng lla);
double CalculateDistance(LatLng pointA, LatLng pointB);
#endif // !_LOCATION_TOOL_H_

View File

@ -1,85 +0,0 @@
/******************** (C) COPYRIGHT 2020 Geek************************************
* File Name : pdr_main.h
* Department : Sensor Algorithm Team
* Current Version : V2.0
* Author :
& yuanlin@vivo.cm
* Date of Issued : 2020.7.18
* Comments : PDR
********************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _PDR_MAIN_H_
#define _PDR_MAIN_H_
#include <string>
using namespace std;
#define PATH_MAX 256
#define TRACK_MAX 100000
typedef struct LatLngd {
double lat;
double lon;
double heading;
double hdop;
double accuracy;
double vel;
double time;
int motionType;
} LatLngd;
typedef struct ResultTracks {
LatLngd gpsTrack[TRACK_MAX];
LatLngd pdrTrack[TRACK_MAX];
int gpsLen;
int pdrLen;
}ResultTracks;
/**----------------------------------------------------------------------
* Function : pdr_writeKml
* Description : pdrgpspdrkml
* path : kml
* name : kml
* postfix
* Date : 2020/11/1 logzhan
*---------------------------------------------------------------------**/
void KmlWrite(string path, string name, string postfix);
/**----------------------------------------------------------------------
* Function : getSimulateFileFp
* Description : 仿
* Date : 2021/01/25 logzhan
*---------------------------------------------------------------------**/
FILE* getSimulateFile(FILE* catalogFp, string path_file, string& fileHead);
/**----------------------------------------------------------------------
* Function : gpsYaw2GoogleYaw
* Description : kml0-360Yaw
* Yaw
* Date : 2021/01/25 logzhan
*---------------------------------------------------------------------**/
double gpsYaw2GoogleYaw(double heading);
/**----------------------------------------------------------------------
* Function : Motion2TypeStr
* Description :
* Date : 2022/9/16 logzhan
*---------------------------------------------------------------------**/
const char* Motion2TypeStr(int type);
/**----------------------------------------------------------------------
* Function : updateResTrack
* Description : GPSPDR
* Date : 2021/01/25 logzhan
*---------------------------------------------------------------------**/
void UpdateResTrack(ResultTracks& resTrack, LctFs_t& lctfs);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,70 +0,0 @@
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _PDR_MATRIX_H_
#define _PDR_MATRIX_H_
#define N 4
/**---------------------------------------------------------------------
* Function : MatrixTrans
* Description :
* Date : 2022/09/14 logzhan
*---------------------------------------------------------------------**/
void MatrixTrans(double a[N][N], double r[N][N]);
/**---------------------------------------------------------------------
* Function : VecMatMultiply
* Description : r = b * a
* Date : 2022/09/14 logzhan
*---------------------------------------------------------------------**/
void VecMatMultiply(double a[N], double b[N][N], double r[N]);
/**---------------------------------------------------------------------
* Function : MatrixMultiply
* Description : r = a * b
* Date : 2022/09/14 logzhan
*---------------------------------------------------------------------**/
void MatrixMultiply(double a[N][N], double b[N][N], double r[N][N]);
/**---------------------------------------------------------------------
* Function : MatrixAdd
* Description : r = a + b, a = a + b
* Date : 2022/09/14 logzhan
*---------------------------------------------------------------------**/
void MatrixAdd(double a[N][N], double b[N][N], double r[N][N]);
/**---------------------------------------------------------------------
* Function : VectorAdd
* Description : r = a + b, a = a + b
* Date : 2022/09/14 logzhan
*---------------------------------------------------------------------**/
void VectorAdd(double a[N], double b[N], double r[N]);
/**---------------------------------------------------------------------
* Function : MatrixSub
* Description : r = a - b, a = a - b
* Date : 2022/09/14 logzhan
*---------------------------------------------------------------------**/
void MatrixSub(double a[N][N], double b[N][N], double r[N][N]);
/**---------------------------------------------------------------------
* Function : VectorSub
* Description : r = a - b, a = a - b
* Date : 2022/09/14 logzhan
*---------------------------------------------------------------------**/
void VectorSub(double a[N], double b[N], double r[N]);
/**---------------------------------------------------------------------
* Function : MatrixInverse
* Description :
* Date : 2022/09/14 logzhan
*---------------------------------------------------------------------**/
void MatrixInverse(double(*a)[N], double(*a_inv)[N]);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,177 +0,0 @@
#pragma once
/******************** (C) COPYRIGHT 2017 Geek************************************
* File Name : Steps_Algorithm.h
* Department : Sensor Team
* Current Version : V1.0
* Author : Xiaoyong Li
* Date of Issued : 2017.05.8
* Comments: step counter algorithm added
********************************************************************************
* THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR REFERENCE OR
* EDUCATION. AS A RESULT, Geek SOFTWARE SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
********************************************************************************
* Release Log
*
* Comments: step counter algorithm released
* Version Number : V1.0
* Author : Xiaoyong Li
* Date of Issued : 2017.05.8
*******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _STEPS_ALGORITHM_H_
#define _STEPS_ALGORITHM_H_
#include "pdr_sensor.h"
#define ACC_SAMPLE_RATE_100HZ
//#define ACC_SAMPLE_RATE_25HZ
#ifdef ACC_SAMPLE_RATE_25HZ
#define DownResampleTimes 1
#define ACC_SAMPLE_TIME 40
#define SAMPLE_TIME_MIN 36
#else
#define DOWN_SAMPLE_TIMES 4
#define ACC_SAMPLE_TIME 10 //origin acc sampleFrequency is 100 hz
#define GYRO_SAMPLE_TIME 10
#define SAMPLE_TIME_MIN 9
#endif
#define SampleFrequency 25
#define SampleFrequencyFour 100
#define SampleFrequencydouble 50
#define SampleFrequencyOPF (37.5l)
#define SampleFrequencySix 150
#define AvergeFilterOrder 5
#define AxisNumber 4
#define FiltDifNumber 4
#define AxisNumberFiltOut 16 //(AxisNumber * FiltDifNumber)
#define AxisNumberStepSaveState 20 //(AxisNumberFiltOut + FiltDifNumber)
#define AccelerationNumber 39
#define ORI_ACC_SAMPLE_RATE 100 //origin acc sampleFrequency is 100 hz
#define WIN_LENGTH_AVE_DEVI 50 //
#define PI2A (180/3.1415926)
//#define NSTOS 1000000000
#define MS2S 1000
#define ACC_INSPECT_TIMEWIDTH 200 //origin acc sampleFrequency is 100 hz
#define WINDOW_LENGTH_MIN2MAX (ACC_INSPECT_TIMEWIDTH/2 - 1) //25hz , 1 second
//#define WINDOW_LENGTH_IN_VEHICLE
#define STARTSTEPS 8
/*#define PLATFORM_ANDROID_QCOM_AP*/
/*#define PLATFORM_ANDROID_QCOM_ADSP*/
#define PLATFORM_PC_WINDOWS
//#define PLATFORM_PC_LINUX
#if defined PLATFORM_ANDROID_QCOM_ADSP
#define STEPLIB_MEMSET SNS_OS_MEMSET
#if 1
#define SAM_STEP_LIB_NAME "steplib"
#define SAM_STEP_LIB_MSG_0(msg) UMSG(MSG_SSID_SNS,DBG_ERROR_PRIO, SAM_STEP_LIB_NAME" - "msg)
#define SAM_STEP_LIB_MSG_1(msg,p1) UMSG_1(MSG_SSID_SNS,DBG_ERROR_PRIO, SAM_STEP_LIB_NAME" - "msg,p1)
#define SAM_STEP_LIB_MSG_2(msg,p1,p2) UMSG_2(MSG_SSID_SNS,DBG_ERROR_PRIO, SAM_STEP_LIB_NAME" - "msg,p1,p2)
#define SAM_STEP_LIB_MSG_3(msg,p1,p2,p3) UMSG_3(MSG_SSID_SNS,DBG_ERROR_PRIO, SAM_STEP_LIB_NAME" - "msg,p1,p2,p3)
#define SAM_STEP_LIB_MSG_4(msg,p1,p2,p3,p4) UMSG_4(MSG_SSID_SNS,DBG_ERROR_PRIO, SAM_STEP_LIB_NAME" - "msg,p1,p2,p3,p4)
#else
#define SAM_STEP_LIB_MSG_0(msg)
#define SAM_STEP_LIB_MSG_1(msg,p1)
#define SAM_STEP_LIB_MSG_2(msg,p1,p2)
#define SAM_STEP_LIB_MSG_3(msg,p1,p2,p3)
#define SAM_STEP_LIB_MSG_4(msg,p1,p2,p3,p4)
#endif
#elif defined PLATFORM_ANDROID_QCOM_AP || defined PLATFORM_PC_WINDOWS || defined PLATFORM_PC_LINUX || defined PLATFORM_ANDROID_MTK || defined PLATFORM_ANDROID_QCOM_SLPI845
#define STEPLIB_MEMSET memset
#endif
#if defined PLATFORM_ANDROID_MTK
#define ABS_INT abs_value
#else
#define ABS_INT abs
#endif
typedef struct {
unsigned long walkSteps;
}stepsRe;
typedef struct {
double walk;
double walkRealTime;
double nD;
}stepsDfine;
typedef enum {
nonSteps = 0,
walk,
}enumState;
typedef struct {
unsigned char sign;
enumState state;
unsigned long long stateStartTime;
unsigned long long stepsStartTime;
}stateNowDfine;
typedef struct {
enumState state;
unsigned long long startTimeStamp;
}stateDfine;
typedef struct {
int flag;
double ax;
double ay;
double az;
}downSample;
/**---------------------------------------------------------------------
* Function : getPedometerLibVersion
* Description :
* Date : 2020/2/20
*---------------------------------------------------------------------**/
const char* getPedometerLibVersion(void);
/**---------------------------------------------------------------------
* Function : initPedometer
* Description : PDR
* Date : 2020/2/20 logzhan
*---------------------------------------------------------------------**/
void Pedometer_Init(void);
/**---------------------------------------------------------------------
* Function : reinitPedometer
* Description : PDR
* Date : 2020/2/20 logzhan
*---------------------------------------------------------------------**/
void reinitPedometer(void);
/**---------------------------------------------------------------------
* Function : updatePedometer
* Description : PDR ,
* Date : 2020/2/1 logzhan
*---------------------------------------------------------------------**/
void PedometerUpdate(IMU_t* ss_data, unsigned long* step);
void Steps_State_Detect(enumState* state);
/**---------------------------------------------------------------------
* Function : detVehicleMode
* Description : PDR ,
* Date : 2020/2/1 logzhan
*---------------------------------------------------------------------**/
void detVehicleMode(double ax, double ay, double az);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -1,141 +0,0 @@
#ifndef _PDR_UTIL_H_
#define _PDR_UTIL_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#define TRAJ_POINT_COUNT 1000
#define MIN_POINT_COUNT 15
#define OPT_TRAJ_TIMEOUT 1000
#define FALIED_LINE 0
#define IN_LINE 1
#define CLOSE_START 2
#define CLOSE_END 3
typedef struct LatLng{
double lat;
double lon;
} LatLng_t;
typedef struct TrajSign
{
int count;
double lastTime;
double weight;
double yaw[TRAJ_POINT_COUNT];
double error[TRAJ_POINT_COUNT];
double points[TRAJ_POINT_COUNT][2];
}TrajSign;
/**地球相关参数*******************************************************************/
typedef struct
{
double rmh; /* 子午圈曲率半径 */
double rnh; /* 卯酉圈曲率半径 */
double grav; /* 当地重力加速度 */
double lat; /* 当地纬度rad*/
double wnie[3]; /* 地理系相对惯性系的角速度在导航系分量 */
} EarthData_t;
/**----------------------------------------------------------------------
* Function : mean
* Description : double
* Review :
*
* Date : 2020/7/4 logzhan
*---------------------------------------------------------------------**/
double mean(double *x, int n);
float fmean(float *data, int len);
float stdf(float* data, int len);
double meanAngle(double* angle, int len);
void modAngle(double * angle, double min, double max);
double pow_i(double num, long long n);
double pow_f(double num, double m);
double vivopow(double x, double y);
void centralization(double *x, double *x_out, int n);
void getCovMatrix(double *x, double *y, double cov[2][2], int n);
int Jacobi(double a[][2], double p[][2], int n, double eps, int T);
LatLng_t ProjPointOfLatLng(LatLng_t point, LatLng_t linePointA, LatLng_t linePointB);
double CalDistance(LatLng_t pointA, LatLng_t pointB);
void ProjPointOfLatLng_cir(double point1[], double yaw, double point2[], double result[]);
/**----------------------------------------------------------------------
* Function : pdr_invSqrt
* Description : 1/sqrt(x)
* Date : 2020/6/30 logzhan
*---------------------------------------------------------------------**/
float InvSqrt(float x);
/**---------------------------------------------------------------------
* Function : pdr_v3Norm
* Description :
* Date : 2021/01/26 yuanlin@vivocom &&
*
*
*---------------------------------------------------------------------**/
void pdr_v3Norm(float* vx, float* vy, float* vz);
/**----------------------------------------------------------------------
* Function : pdr_writeCsvStr
* Description : fprintf
* Date : 2020/7/8 logzhan
*---------------------------------------------------------------------**/
void WriteCsvStr(FILE* fp_write, char* str);
/**----------------------------------------------------------------------
* Function : pdr_utc2hms
* Description : UTC
* Date : 2020/7/8 logzhan
*---------------------------------------------------------------------**/
void pdr_utc2hms(double utc, double* h, double* m, double* s);
/**----------------------------------------------------------------------
* Function : qnb2att
* Description :
* Date : 2020/7/4 logzhan
*---------------------------------------------------------------------**/
void Qnb2Att(float* q, double* attitude);
double CalRadianDifferent(double s_dir, double d_dir);
/**----------------------------------------------------------------------
* Function : pdr_earthParameter
* Description :
* Date : 2020/7/8 logzhan
*---------------------------------------------------------------------**/
EarthData_t CalEarthParameter(double oriLat);
/**----------------------------------------------------------------------
* Function : Motion2TypeStr
* Description :
* Date : 2022/9/16 logzhan
*---------------------------------------------------------------------**/
const char* Motion2TypeStr(int type);
int pdr_min(int param_a, int param_b);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,15 +0,0 @@
#ifndef _PDR_GPS_H_
#define _PDR_GPS_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,110 +0,0 @@
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _PARSEDATA_H_
#define _PARSEDATA_H_
#include "pdr_sensor.h"
#define NAEA_LAST_TIME 500
#define IMU_LAST_COUNT 10
#define FLOAT_TO_INT 1000000
/**************************************************************************
* Description : NMEA
* : NMEARMC,GGA,GSA,GSV,
**************************************************************************/
char * strtok_ct(char * s, const char * delim);
/**************************************************************************
* Description :
* Input : pt
ts ms
sstp
* Output : imu_p
**************************************************************************/
void ParseIMU(char* pt, IMU_t *imu_p, double ts, int sstp);
/**************************************************************************
* Description : NMEA
* Input : pt NMEA
ts ms
* Output : ln NMEA
**************************************************************************/
void parseNMEA(char* pt, Nmea_t *ln, double ts);
/**************************************************************************
* Description : GGA
* Input : pt NMEA
ts ms
* Output : ln NMEA
**************************************************************************/
void ParseGGA(char* pt, Nmea_t *ln, double ts);
/**************************************************************************
* Description : RMC
* Input : pt NMEA
ts ms
* Output : ln NMEA
**************************************************************************/
void ParseRMC(char* pt, Nmea_t *ln, double ts);
/**************************************************************************
* Description : GSV
* Input : pt NMEA
ts ms
* Output : ln NMEA
**************************************************************************/
void parseGSV(char* pt, Nmea_t *ln, double ts);
/**************************************************************************
* Description : GSA
* Input : pt NMEA
ts ms
* Output : ln NMEA
**************************************************************************/
void ParseGSA(char* pt, Nmea_t *ln, double ts);
/**----------------------------------------------------------------------
* Function : parseLocAccuracy
* Description : GPSAccuracy
* Date : 2020/7/9 yuanlin@vivo.com &logzhan
*---------------------------------------------------------------------**/
void parseLocAccuracy(char* pt, Nmea_t *ln, double ts);
/**************************************************************************
* Description : NMEA
* Input : ln NMEA
**************************************************************************/
void preprocessNMEA(Nmea_t *ln);
int ParseLineStr(char* line, IMU_t* imu_p, Nmea_t* ln);
/**----------------------------------------------------------------------
* Function : HexToDec
* Description :
* Date : 2022-09-15 logzhan
*---------------------------------------------------------------------**/
long HexToDec(char *source);
/**----------------------------------------------------------------------
* Function : pdr_int2Hex
* Description :
* Date : 2020/7/9 yuanlin@vivo.com & logzhan
*---------------------------------------------------------------------**/
char * Int2Hex(int a, char *buffer);
/**----------------------------------------------------------------------
* Function : pdr_getIndexOfSigns
* Description : chsign
* Date : 2020/7/9 yuanlin@vivo.com &logzhan
*---------------------------------------------------------------------**/
int pdr_getIndexOfSigns(char ch);
#endif
#ifdef __cplusplus
}
#endif

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