This commit is contained in:
Степанов Станислав 2025-03-20 17:57:02 +03:00
commit f2cec055b8
9 changed files with 636 additions and 0 deletions

112
Inc/Nmea0183Parser.h Normal file
View File

@ -0,0 +1,112 @@
//
// Created by xemon on 01.09.22.
//
#ifndef NMEA_0183_PARSER_NMEA_0183_PARSER_H
#define NMEA_0183_PARSER_NMEA_0183_PARSER_H
#include "stdint.h"
#include "stddef.h"
#include "stdbool.h"
typedef struct {
uint8_t hour;
uint8_t minute;
uint8_t second;
uint16_t millisecond;
} tNmeaTimeRmc;
typedef struct {
uint8_t day;
uint8_t month;
uint8_t year;
} tNmeaDateRmc;
typedef struct {
double latitude;
char nsIndicator;
double longitude;
double horizontDiluitPrecis;
double altitude;
char ewIndicator;
} tNmeaLocationRmc;
typedef struct {
double declination;
double nsat;
double mode;
} tNmeaMagneticRmc;
typedef struct {
char status;
tNmeaDateRmc date;
tNmeaTimeRmc time;
tNmeaLocationRmc location;
tNmeaMagneticRmc magnetic;
double knotVelocity;
double kmhVelocity;
double headingAngle;
} tNmeaRmc;
typedef struct {
double lat;
double lon;
} tLocationPointInDegDouble;
typedef struct {
float lat;
float lon;
} tLocationPointInDeg;
typedef struct {
double latitude;
char nsIndicator;
double longitude;
char ewIndicator;
} tNmeaLocationGga;
typedef enum {
NO_SOLUTIONS = 0,
AUTO = 1,
DIFF = 2,
RTK_FIX = 4,
RTK_DIFF = 5,
EXTRAPOLATION = 6,
FIX_LOCATION = 7,
SIMULATE = 8,
SBAS = 9,
} eNmeaGgaGpsQual;
typedef struct {
tNmeaTimeRmc time;
tNmeaLocationGga location;
eNmeaGgaGpsQual qual;
uint8_t sats;
float hdop;
double altitude;
char altitudeUnit;
double undulation;
char undulationUnit;
uint8_t ageSec;
uint16_t stnID;
} tNmeaGga;
bool bNmea0183ParseGGA(char *ggaString, size_t len, tNmeaGga *result);
uint32_t iNmea0183TimestampFromRmc(tNmeaRmc *env);
tLocationPointInDegDouble iNmea0183_DegLocationDoubleFromRmc(tNmeaRmc *env);
tLocationPointInDeg iNmea0183_DegLocationFromRmc(tNmeaRmc *env);
void vNmea0183Sign(char *str, size_t *strLen);
bool bNmea0183ParseRMC(char *rmcString, size_t len, tNmeaRmc *result);
bool bNmea0183IsRmcString(char *nmeaString, size_t len);
bool bNmea0183IsValidString(char *nmeaString, size_t len);
#endif //NMEA_0183_PARSER_NMEA_0183_PARSER__H

View File

@ -0,0 +1,23 @@
//
// Created by villuton on 20.03.25.
//
#ifndef NMEA0183PARSER_GENERAL_H
#define NMEA0183PARSER_GENERAL_H
#include <stdint-gcc.h>
typedef struct {
uint8_t hour;
uint8_t minute;
uint8_t second;
uint16_t millisecond;
} tNmeaTime;
typedef struct {
uint8_t day;
uint8_t month;
uint8_t year;
} tNmeaDate;
#endif //NMEA0183PARSER_GENERAL_H

View File

@ -0,0 +1,22 @@
//
// Created by villuton on 20.03.25.
//
#ifndef NMEA0183PARSER_PRIVATE_H
#define NMEA0183PARSER_PRIVATE_H
#include "AsciiStringParsingUtils.h"
#define Nmea0183ParseShortCharsDecimalNumber(STR, LEN) (uint16_t) iAsciiStringParseUnsignedLongDecimalNumber(STR,((STR)+(LEN)))
#define Nmea0183ParseDouble(STR, LEN) dAsciiStringParseDouble(STR,((STR)+(LEN)))
#define Nmea0183SkipSpace(STR, STR_END) xAsciiStringSkipSpace(STR,STR_END)
#define Nmea0183SkipToChar(STR, STR_END, SYMBOL) xAsciiStringSeekChar(STR,STR_END,SYMBOL)
#define Nmea0183ParseNextPortion(BEGIN, DIV, STR_END) iAsciiStringMoveToNextParsingBlock(BEGIN,DIV,STR_END,',')
#define Nmea0183ParseHexByte(STR) iAsciiStringParseHexByte(STR)
#endif //NMEA0183PARSER_PRIVATE_H

329
Src/Nmea0183Parser.c Normal file
View File

@ -0,0 +1,329 @@
//
// Created by xemon on 01.09.22.
//
#include "Nmea0183Parser.h"
#include "AsciiStringAssmeblingUtils.h"
#include "AsciiStringParsingUtils.h"
#include "time.h"
#include <string.h>
#define Nmea0183ParseShortCharsDecimalNumber(STR, LEN) (uint16_t) iAsciiStringParseUnsignedLongDecimalNumber(STR,((STR)+(LEN)))
#define Nmea0183ParseDouble(STR, LEN) dAsciiStringParseDouble(STR,((STR)+(LEN)))
#define Nmea0183SkipSpace(STR, STR_END) xAsciiStringSkipSpace(STR,STR_END)
#define Nmea0183SkipToChar(STR, STR_END, SYMBOL) xAsciiStringSeekChar(STR,STR_END,SYMBOL)
#define Nmea0183ParseNextPortion(BEGIN, DIV, STR_END) iAsciiStringMoveToNextParsingBlock(BEGIN,DIV,STR_END,',')
#define Nmea0183ParseHexByte(STR) iAsciiStringParseHexByte(STR)
bool Nmea0183ParseTime(char *utcString, char const *utcStringEnd, tNmeaTimeRmc *time) {
uint32_t len = utcStringEnd - utcString;
uint8_t charsLeft = len - 7;
if (len > 10 || len < 9) {
return false;
}
if (utcString[6] != '.') {
return false;
}
time->hour = Nmea0183ParseShortCharsDecimalNumber(utcString + 0, 2);
time->minute = Nmea0183ParseShortCharsDecimalNumber(utcString + 2, 2);
time->second = Nmea0183ParseShortCharsDecimalNumber(utcString + 4, 2);
time->millisecond = Nmea0183ParseShortCharsDecimalNumber(utcString + 7, charsLeft);
charsLeft = 3 - charsLeft;
while (charsLeft) {
time->millisecond *= 10;
--charsLeft;
}
return true;
}
bool Nmea0183ParseDate(char *utcString, char const *utcStringEnd, tNmeaDateRmc *date) {
uint32_t len = utcStringEnd - utcString;
if (len != 6) {
return false;
}
date->day = Nmea0183ParseShortCharsDecimalNumber(utcString + 0, 2);
date->month = Nmea0183ParseShortCharsDecimalNumber(utcString + 2, 2);
date->year = Nmea0183ParseShortCharsDecimalNumber(utcString + 4, 2);
return true;
}
uint8_t uNmea0183Checksum(const char *str, size_t len) {
char *end = (char *) (str + len);
unsigned char result;
result = 0;
str++;
while ((str != end) && (*str != '*') && (*str != '\0'))
result ^= *str++;
return result;
}
void vNmea0183Sign(char *str, size_t *strLen) {
uint8_t crc = uNmea0183Checksum(str, *strLen);
vAsciiStringAddChar(str, strLen, '*');
vAsciiStringAddByteAsHex(str, strLen, crc);
}
bool bNmea0183IsRmcString(char *nmeaString, size_t len) {
if (nmeaString[0] != '$') {
return false;
}
return strnstr(nmeaString, "RMC", len);
}
uint32_t iNmea0183TimestampFromRmc(tNmeaRmc *env) {
struct tm time;
time.tm_hour = env->time.hour;
time.tm_min = env->time.minute;
time.tm_sec = env->time.second;
time.tm_mday = env->date.day;
time.tm_mon = env->date.month - 1;
if(env->date.year>60){
time.tm_year = (1900 - 1900) + env->date.year;
}else{
time.tm_year = (2000 - 1900) + env->date.year;
}
return mktime(&time);
}
static double nmeaLocationToDeg(double dec) {
double _dec = dec;
int deg = (int) (_dec / 100);
int min = (int) (_dec) - (deg * 100);
double sec = (double) (_dec - min - 100 * deg) * 60.0;
return deg + min / 60.0 + sec / 3600.0;
}
tLocationPointInDegDouble iNmea0183_DegLocationDoubleFromRmc(tNmeaRmc *env) {
tLocationPointInDegDouble result;
result.lat = nmeaLocationToDeg(env->location.latitude);
result.lon = nmeaLocationToDeg(env->location.longitude);
if (env->location.nsIndicator == 'S' || env->location.nsIndicator == 's') {
result.lat *= -1.0;
}
if (env->location.ewIndicator == 'W' || env->location.ewIndicator == 'w') {
result.lon *= -1.0;
}
return result;
}
tLocationPointInDeg iNmea0183_DegLocationFromRmc(tNmeaRmc *env) {
tLocationPointInDeg result;
result.lat = (float) nmeaLocationToDeg(env->location.latitude);
result.lon = (float) nmeaLocationToDeg(env->location.longitude);
if (env->location.nsIndicator == 'S' || env->location.nsIndicator == 's') {
result.lat *= -1.0f;
}
if (env->location.ewIndicator == 'W' || env->location.ewIndicator == 'w') {
result.lon *= -1.0f;
}
return result;
}
bool bNmea0183IsValidString(char *nmeaString, size_t len) {
char *dataEndPos = strnstr((char *) nmeaString, "*", len);
uint8_t checksumCalculated = uNmea0183Checksum(nmeaString, dataEndPos - nmeaString);
++dataEndPos;
uint8_t checksumInString = Nmea0183ParseHexByte(dataEndPos);
return checksumCalculated == checksumInString;
}
bool bNmea0183ParseRMC(char *rmcString, size_t len, tNmeaRmc *result) {
char *end = rmcString + len;
char *front = rmcString;
char *nextDivider = front - 1;
result->status = 'V';
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
if (!Nmea0183ParseTime(front, nextDivider, &result->time)) {
return false;
}
} else {
result->time.second = 0;
result->time.minute = 0;
result->time.hour = 0;
result->time.millisecond = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->status = *front;
} else {
result->status = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->location.latitude = Nmea0183ParseDouble(front, nextDivider - front);
} else {
result->location.latitude = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->location.nsIndicator = *front;
} else {
result->location.nsIndicator = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->location.longitude = Nmea0183ParseDouble(front, nextDivider - front);
} else {
result->location.longitude = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->location.ewIndicator = *front;
} else {
result->location.ewIndicator = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->knotVelocity = Nmea0183ParseDouble(front, nextDivider - front);
} else {
result->knotVelocity = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->headingAngle = Nmea0183ParseDouble(front, nextDivider - front);
} else {
result->headingAngle = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
if (!Nmea0183ParseDate(front, nextDivider, &result->date)) {
return false;
}
} else {
result->date.year = 0;
result->date.month = 0;
result->date.day = 0;
}
return true;
}
bool bNmea0183IsGgaString(char *nmeaString, size_t len) {
if (nmeaString[0] != '$') {
return false;
}
return strnstr(nmeaString, "GGA", len);
}
bool bNmea0183ParseGGA(char *ggaString, size_t len, tNmeaGga *result){
char *end = ggaString + len;
char *front = ggaString;
char *nextDivider = front-1;
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
if (!Nmea0183ParseTime(front, nextDivider, &result->time)) {
return false;
}
} else {
result->time.second = 0;
result->time.minute = 0;
result->time.hour = 0;
result->time.millisecond = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->location.latitude = Nmea0183ParseDouble(front, nextDivider - front);
} else {
result->location.latitude = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->location.nsIndicator = *front;
} else {
result->location.nsIndicator = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->location.longitude = Nmea0183ParseDouble(front, nextDivider - front);
} else {
result->location.longitude = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->location.ewIndicator = *front;
} else {
result->location.ewIndicator = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->qual = Nmea0183ParseShortCharsDecimalNumber(front,nextDivider - front);
} else {
result->qual = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->sats = Nmea0183ParseShortCharsDecimalNumber(front,nextDivider - front);
} else {
result->sats = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->hdop = Nmea0183ParseDouble(front,nextDivider - front);
} else {
result->hdop = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->altitude = Nmea0183ParseDouble(front,nextDivider - front);
} else {
result->altitude = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->altitudeUnit = *front;
} else {
result->altitude = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->undulation = Nmea0183ParseDouble(front,nextDivider - front);
} else {
result->undulation = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->undulationUnit = *front;
} else {
result->undulationUnit = 0;
}
if(result->qual == DIFF){
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->ageSec = Nmea0183ParseShortCharsDecimalNumber(front,nextDivider - front);
} else {
result->ageSec = 0;
}
if (Nmea0183ParseNextPortion(&front, &nextDivider, end) > 0) {
result->stnID = Nmea0183ParseShortCharsDecimalNumber(front,nextDivider - front);
} else {
result->stnID = 0;
}
} else{
result->ageSec = 0;
result->stnID = 0;
}
return true;
}

View File

@ -0,0 +1,35 @@
//
// Created by villuton on 20.03.25.
//
#include <stdbool.h>
#include "Nmea0183Parser_General.h"
#include "Nmea0183Parser_Private.h"
bool Nmea0183ParseTime(char *utcString, char const *utcStringEnd, tNmeaTime *time) {
uint32_t len = utcStringEnd - utcString;
uint8_t charsLeft = len - 7;
if (len > 10 || len < 9) {
return false;
}
if (utcString[6] != '.') {
return false;
}
time->hour = Nmea0183ParseShortCharsDecimalNumber(utcString + 0, 2);
time->minute = Nmea0183ParseShortCharsDecimalNumber(utcString + 2, 2);
time->second = Nmea0183ParseShortCharsDecimalNumber(utcString + 4, 2);
time->millisecond = Nmea0183ParseShortCharsDecimalNumber(utcString + 7, charsLeft);
charsLeft = 3 - charsLeft;
while (charsLeft) {
time->millisecond *= 10;
--charsLeft;
}
return true;
}

8
Tst/CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.17)
PROJECT(nmea_parser_test)
set(CMAKE_CXX_STANDARD 17)
include(modular.cmake)
add_executable(test ${SOURCES})

69
Tst/Src/test.c Normal file
View File

@ -0,0 +1,69 @@
//
// Created by xemon on 02.09.22.
//
#include "printf.h"
#include "Nmea0183Parser.h"
#include "stdio.h"
void testRmc(char *rmcStr, size_t rmcStrLen) {
printf("string %*s\n", (int) rmcStrLen, rmcStr);
if (bNmea0183IsValidString(rmcStr, rmcStrLen)) {
printf("it's valid nmea string\n");
if (bNmea0183IsRmcString(rmcStr, rmcStrLen)) {
printf("it's rmc string\n");
tNmeaRmc rmc;
if (bNmea0183ParseRMC(rmcStr + 7, rmcStrLen - 7, &rmc)) {
printf("RMC string parsed OK!\n");
if (rmc.location.ewIndicator) {
printf("lat %c %f\n", rmc.location.ewIndicator, rmc.location.latitude);
} else {
printf("no lat\n");
}
if (rmc.location.nsIndicator) {
printf("lon %c %f\n", rmc.location.nsIndicator, rmc.location.longitude);
} else {
printf("no lon\n");
}
printf("time %02i:%02i:%02i [%03i]\n",
rmc.time.hour,
rmc.time.minute,
rmc.time.second,
rmc.time.millisecond
);
printf("date %i %i 20%02i\n",
rmc.date.day,
rmc.date.month,
rmc.date.year
);
} else {
printf("Can't parse rmc string!\n");
}
} else {
printf("it's not rmc string\n");
}
} else {
printf("it's invalid nmea string\n");
}
}
int main() {
char gprmc0[] = "$GPSACP: 115235.000,5244.8321N,04126.7108E,0.6,201.7,3,0.0,0.0,0.0,121022,05,08";
char gprmc1[] = "$GPRMC,125504.049,A,5542.2389,N,03741.6063,E,0.06,25.82,200906,,,*17";
printf("\n");
// testAcp(gprmc0, sizeof(gprmc0) - 1);
printf("\n");
testRmc(gprmc1, sizeof(gprmc1) - 1);
return 0;
}

16
Tst/modular.json Normal file
View File

@ -0,0 +1,16 @@
{
"dep": [
{
"type": "local",
"dir": "../"
}
],
"cmake": {
"inc_dirs": [
"Inc"
],
"srcs": [
"Src/**.c"
]
}
}

22
modular.json Normal file
View File

@ -0,0 +1,22 @@
{
"dep": [
{
"type": "git",
"provider": "Villuton",
"repo": "AsciiStringAssemblingUtils"
},
{
"type": "git",
"provider": "Villuton",
"repo": "AsciiStringParsingUtils"
}
],
"cmake": {
"inc_dirs": [
"Inc"
],
"srcs": [
"Src/**.c"
]
}
}