1.air部署,细节待修改

This commit is contained in:
2022-04-15 13:51:42 +08:00
parent 3b74dfc608
commit 8c6be81f94
440 changed files with 7 additions and 883 deletions

251
Source/GPS/BD357Ctrl.cpp Normal file
View File

@ -0,0 +1,251 @@
#include "BD357Ctrl.h"
#include "MainGrabber.h"
#include "lwgps.h"
#include "lwrb.h"
#include "time.h"
BD357Controller::BD357Controller(QObject* parent /*= nullptr*/)
{
m_iFlagIsGPSValid = 0;
m_iFlagIsContinuousUpdating = 0;
m_pSerialPort = NULL;
//connect(m_pSerialPort, &QSerialPort::readyRead, this, &BD357Controller::HandleReadyRead);
connect(this, &BD357Controller::Signal_InitSelf, this, &BD357Controller::Slot_InitSelf);
//connect(m_pMainGrabber,&CMainGrabber::Signal_InitFinished,this, &BD357Controller::Slot_SyncDateOnce);
}
BD357Controller::~BD357Controller()
{
if (m_pSerialPort != NULL)
{
m_pSerialPort->close();
delete m_pSerialPort;
}
}
int BD357Controller::SetContext(CMainGrabber& pMainGrabber)
{
m_pMainGrabber = &pMainGrabber;
}
int BD357Controller::SetupMessagePipeline()
{
connect(m_pMainGrabber, &CMainGrabber::Signal_InitFinished, this, &BD357Controller::Slot_SyncDateOnce);
return 0;
}
int BD357Controller::SyncDateOnce()
{
using namespace ZZ_MISCDEF;
bool bFlagFinish = false;
size_t uiCount = 0;
ZZ_U8 u8Rx,u8Tx;
while (!bFlagFinish)
{
m_qbReadData.clear();
while (m_qbReadData.size()< 1000)
{
m_pSerialPort->waitForReadyRead(1000);
m_qbReadData.append(m_pSerialPort->readAll());
}
qDebug() << m_qbReadData.size() << endl;
lwgps_process(&m_hLWGPS, m_qbReadData, m_qbReadData.size());
m_qbReadData.clear();
if (uiCount<2)
{
uiCount++;
continue;
}
else
{
/*if (m_hLWGPS.is_valid)
{
bFlagFinish = 1;
}*/
}
//need to disable
//qDebug() << "one status :"<< m_hLWGPS.is_valid << m_hLWGPS .sats_in_use<< m_hLWGPS .year<<"-" << m_hLWGPS .month<<"-" << m_hLWGPS.date << "-" << m_hLWGPS.hours << ":" << m_hLWGPS.minutes << ":" << m_hLWGPS.seconds << ":" << endl;
int iRes = ZZ_FormatTime(m_hLWGPS.year, m_hLWGPS.month, m_hLWGPS.date, m_hLWGPS.hours, m_hLWGPS.minutes, m_hLWGPS.seconds);
if (iRes != 0)
{
continue;
}
else
{
bFlagFinish = 1;
time_t stdtime = m_qdtDateTime.toTime_t();
time_t stdtimeUTC = m_qdtDateTimeUTC.toTime_t();
stime(&stdtimeUTC);
system("gpio write 4 1");
qDebug() << "system(gpio write 4 1)";
//
//m_qdtDateTime.currentMSecsSinceEpoch
//m_qdtDateTime.currentMSecsSinceEpoch();
}
//bFlagFinish = 1;
}
emit Signal_StartCapture();
return 0;
}
int BD357Controller::StartContinuousUpdating()
{
m_iFlagIsContinuousUpdating = 1;
while (m_iFlagIsContinuousUpdating)
{
m_qbReadData.clear();
while (m_qbReadData.size() < 1000)
{
m_pSerialPort->waitForReadyRead(1000);
m_qbReadData.append(m_pSerialPort->readAll());
}
//qDebug() << m_qbReadData.size() << endl;
lwgps_process(&m_hLWGPS, m_qbReadData, m_qbReadData.size());
if (m_hLWGPS.is_valid&&(!m_iFlagIsGPSValid))
{
emit Signal_UpdateStatus(1);
}
if ((!m_hLWGPS.is_valid)&& m_iFlagIsGPSValid)
{
emit Signal_UpdateStatus(0);
}
m_qbReadData.clear();
}
return 0;
}
int BD357Controller::Initialize(GPSInfo &struGPSI)
{
m_struGPSInfo = struGPSI;
emit Signal_InitSelf();
}
int BD357Controller::ZZ_FormatTime(ZZ_U8 u8Year, ZZ_U8 u8Month, ZZ_U8 u8Day, ZZ_U8 u8Hour, ZZ_U8 u8Minute, ZZ_U8 u8Second)
{
int iYear = u8Year + 2000;
QDateTime qdtTestDate(QDate(iYear, u8Month, u8Day), QTime(u8Hour, u8Minute, u8Second));
if (qdtTestDate.isValid())
{
qDebug() << qdtTestDate;
m_qdtDateTime = qdtTestDate;
m_qdtDateTimeUTC = qdtTestDate;
//UTC+0 to UTC+8
m_qdtDateTime.addSecs(3600 * 8);
return 0;
}
else
{
return -1;
}
return -2;
//bool b1 = startDate.isValid();
//QString end = startDate.toString("yyyy.MM.dd hh:mm:ss");
//qDebug() << end;
//QString qstrSyncDate = QString("%1-%2-%3-%4-%5-%6").arg(iYear, 4, 10, QLatin1Char('0')).arg(u8Month, 2, 10, QLatin1Char('0'))
// .arg(u8Day, 2, 10, QLatin1Char('0')).arg(u8Hour, 2, 10, QLatin1Char('0'))
// .arg(u8Minute, 2, 10, QLatin1Char('0')).arg(u8Second, 2, 10, QLatin1Char('0'));
//
//m_qdtDateTime.fromString(qstrSyncDate, "yyyy-MM-dd-hh-mm-ss");
//qDebug() << qstrSyncDate;
//bool b = m_qdtDateTime.isValid();
//QString begin = m_qdtDateTime.toString("yyyy.MM.dd hh:mm:ss");
//qDebug() << begin;
/*qstrSyncDate = "20" + QString::number(m_hLWGPS.year) + "-"
+ QString::number(m_hLWGPS.month) + "-"
+ QString::number(m_hLWGPS.date) + "-"
+ QString::number(m_hLWGPS.hours) + "-"
+ QString::number(m_hLWGPS.minutes) + "-"
+ QString::number(m_hLWGPS.seconds);
qdtSync.fromString(qstrSyncDate, "yyyy-MM-dd-hh-mm-ss");
qDebug() << qstrSyncDate;
bool b = qdtSync.isValid();
QString begin = qdtSync.toString("yyyy.MM.dd hh:mm:ss");
std::string strtime = begin.toStdString();
qDebug() << begin;*/
}
int BD357Controller::Slot_InitSelf()
{
m_qbReadData.clear();
//QString qstrPortName = struGPSI.qstrInterfaceName;
if (m_pSerialPort != NULL)
{
m_pSerialPort->close();
delete m_pSerialPort;
}
m_pSerialPort = new QSerialPort;
m_pSerialPort->setPortName(m_struGPSInfo.qstrInterfaceName);
//m_pSerialPort->setReadBufferSize(512); in this project 0 seems a better param.
bool bRes = m_pSerialPort->setBaudRate(m_struGPSInfo.iBaud);
if (!bRes)
{
qDebug() << "Err:setBaudRate Failed.Exit Code:1";
//std::cout << "Err.setBaudRate Failed" << std::endl;
return 1;
}
bRes = m_pSerialPort->open(QIODevice::ReadWrite);
if (!bRes)
{
qDebug() << "Err:open Failed.Exit Code:2";
//std::cout << "Err.open Failed" << std::endl;
return 2;
}
/////
lwgps_init(&m_hLWGPS);
lwrb_init(&m_hlwrbBuffer, m_u8GPSDataBuffer, sizeof(m_u8GPSDataBuffer));
}
int BD357Controller::Slot_SyncDateOnce()
{
SyncDateOnce();
return 0;
}
//
// int BD357Controller::HandleReadyRead()
// {
// size_t uiSize = 0;
// m_qbReadData.append(m_pSerialPort->readAll());
// if (m_qbReadData.size()<2048)
// {
// lwrb_write(&m_hlwrbBuffer, &m_qbReadData, m_qbReadData.size());
// m_qbReadData.clear();
// }
// else
// {
// m_qbReadData.clear();
// }
//
// return 0;
// }

50
Source/GPS/BD357Ctrl.h Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include "ZZ_Types.h"
#include "pch.h"
#include "lwgps.h"
#include "lwrb.h"
#include <atomic>
using namespace ZZ_MISCDEF;
using namespace ZZ_MISCDEF::MISC_DETECTOR;
class CMainGrabber;
class BD357Controller :public QObject
{
Q_OBJECT
public:
BD357Controller(QObject* parent = nullptr);
~BD357Controller();
private:
std::atomic<int> m_iFlagIsGPSValid;
std::atomic<int> m_iFlagIsContinuousUpdating;
std::atomic<int> m_iFlagIsConnected;
public:
int SetContext(CMainGrabber &pMainGrabber);
int SetupMessagePipeline();
int SyncDateOnce();
int StartContinuousUpdating();
int Initialize(GPSInfo &struGPSI);
private:
int ZZ_FormatTime(ZZ_U8 u8Year,ZZ_U8 u8Month, ZZ_U8 u8Day, ZZ_U8 u8Hour, ZZ_U8 u8Minute, ZZ_U8 u8Second);
public slots:
int Slot_InitSelf();
int Slot_SyncDateOnce();
private slots:
//int HandleReadyRead();
signals:
void Signal_InitSelf();
void Signal_StartCapture();
void Signal_UpdateStatus(int iValid);
void Signal_UpdateDate(QTime qDate);
private:
QDateTime m_qdtDateTime, m_qdtDateTimeUTC;
GPSInfo m_struGPSInfo;
ZZ_U8 m_u8GPSDataBuffer[10];
lwgps_t m_hLWGPS;
lwrb_t m_hlwrbBuffer;
int m_iBaudRate;
QSerialPort* m_pSerialPort;
QByteArray m_qbReadData;
CMainGrabber* m_pMainGrabber;
};

606
Source/GPS/lwgps.cpp Normal file
View File

@ -0,0 +1,606 @@
/**
* \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.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 */
#define CRC_ADD(_gh, ch) (_gh)->p.crc_calc ^= (uint8_t)(ch)
#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;
}
/**
* \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! */
}
/**
* \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 =(uint8_t*) 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;
}
}

289
Source/GPS/lwgps.h Normal file
View File

@ -0,0 +1,289 @@
/**
* \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_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 */
uint8_t crc_calc; /*!< Calculated CRC string */
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 */

171
Source/GPS/lwgps_opt.h Normal file
View File

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

44
Source/GPS/lwgps_opts.h Normal file
View File

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

495
Source/GPS/lwrb.cpp Normal file
View File

@ -0,0 +1,495 @@
/**
* \file lwrb.c
* \brief Lightweight ring buffer
*/
/*
* 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 LwRB - Lightweight ring buffer library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v2.0.3
*/
#include "lwrb.h"
/* Memory set and copy functions */
#define BUF_MEMSET memset
#define BUF_MEMCPY memcpy
#define BUF_MAGIC1 (0xDEADBEEF)
#define BUF_MAGIC2 (~0xDEADBEEF)
#if LWRB_USE_MAGIC
#define BUF_IS_VALID(b) ((b) != NULL && (b)->magic1 == BUF_MAGIC1 && (b)->magic2 == BUF_MAGIC2 && (b)->buff != NULL && (b)->size > 0)
#else
#define BUF_IS_VALID(b) ((b) != NULL && (b)->buff != NULL && (b)->size > 0)
#endif /* LWRB_USE_MAGIC */
#define BUF_MIN(x, y) ((x) < (y) ? (x) : (y))
#define BUF_MAX(x, y) ((x) > (y) ? (x) : (y))
#define BUF_SEND_EVT(b, type, bp) do { if ((b)->evt_fn != NULL) { (b)->evt_fn((b), (type), (bp)); } } while (0)
/**
* \brief Initialize buffer handle to default values with size and buffer data array
* \param[in] buff: Buffer handle
* \param[in] buffdata: Pointer to memory to use as buffer data
* \param[in] size: Size of `buffdata` in units of bytes
* Maximum number of bytes buffer can hold is `size - 1`
* \return `1` on success, `0` otherwise
*/
uint8_t
lwrb_init(lwrb_t* buff, void* buffdata, size_t size) {
if (buff == NULL || buffdata == NULL || size == 0) {
return 0;
}
BUF_MEMSET((void*)buff, 0x00, sizeof(*buff));
buff->size = size;
buff->buff = (uint8_t*)buffdata;
#if LWRB_USE_MAGIC
buff->magic1 = BUF_MAGIC1;
buff->magic2 = BUF_MAGIC2;
#endif /* LWRB_USE_MAGIC */
return 1;
}
/**
* \brief Check if buff is initialized and ready to use
* \param[in] buff: Buffer handle
* \return `1` if ready, `0` otherwise
*/
uint8_t
lwrb_is_ready(lwrb_t* buff) {
return BUF_IS_VALID(buff);
}
/**
* \brief Free buffer memory
* \note Since implementation does not use dynamic allocation,
* it just sets buffer handle to `NULL`
* \param[in] buff: Buffer handle
*/
void
lwrb_free(lwrb_t* buff) {
if (BUF_IS_VALID(buff)) {
buff->buff = NULL;
}
}
/**
* \brief Set event function callback for different buffer operations
* \param[in] buff: Buffer handle
* \param[in] evt_fn: Callback function
*/
void
lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn evt_fn) {
if (BUF_IS_VALID(buff)) {
buff->evt_fn = evt_fn;
}
}
/**
* \brief Write data to buffer.
* Copies data from `data` array to buffer and marks buffer as full for maximum `btw` number of bytes
*
* \param[in] buff: Buffer handle
* \param[in] data: Pointer to data to write into buffer
* \param[in] btw: Number of bytes to write
* \return Number of bytes written to buffer.
* When returned value is less than `btw`, there was no enough memory available
* to copy full data array
*/
size_t
lwrb_write(lwrb_t* buff, const void* data, size_t btw) {
size_t tocopy, free;
volatile size_t buff_w_ptr;
const uint8_t* d = (uint8_t*)data;
if (!BUF_IS_VALID(buff) || data == NULL || btw == 0) {
return 0;
}
/* Calculate maximum number of bytes available to write */
free = lwrb_get_free(buff);
btw = BUF_MIN(free, btw);
if (btw == 0) {
return 0;
}
/* Step 1: Write data to linear part of buffer */
buff_w_ptr = buff->w;
tocopy = BUF_MIN(buff->size - buff_w_ptr, btw);
BUF_MEMCPY(&buff->buff[buff_w_ptr], d, tocopy);
buff_w_ptr += tocopy;
btw -= tocopy;
/* Step 2: Write data to beginning of buffer (overflow part) */
if (btw > 0) {
BUF_MEMCPY(buff->buff, &d[tocopy], btw);
buff_w_ptr = btw;
}
/* Step 3: Check end of buffer */
if (buff_w_ptr >= buff->size) {
buff_w_ptr = 0;
}
/*
* Write final value to the actual running variable.
* This is to ensure no read operation can access intermediate data
*/
buff->w = buff_w_ptr;
BUF_SEND_EVT(buff, LWRB_EVT_WRITE, tocopy + btw);
return tocopy + btw;
}
/**
* \brief Read data from buffer.
* Copies data from buffer to `data` array and marks buffer as free for maximum `btr` number of bytes
*
* \param[in] buff: Buffer handle
* \param[out] data: Pointer to output memory to copy buffer data to
* \param[in] btr: Number of bytes to read
* \return Number of bytes read and copied to data array
*/
size_t
lwrb_read(lwrb_t* buff, void* data, size_t btr) {
size_t tocopy, full;
volatile size_t buff_r_ptr;
uint8_t* d = (uint8_t*)data;
if (!BUF_IS_VALID(buff) || data == NULL || btr == 0) {
return 0;
}
/* Calculate maximum number of bytes available to read */
full = lwrb_get_full(buff);
btr = BUF_MIN(full, btr);
if (btr == 0) {
return 0;
}
/* Step 1: Read data from linear part of buffer */
buff_r_ptr = buff->r;
tocopy = BUF_MIN(buff->size - buff_r_ptr, btr);
BUF_MEMCPY(d, &buff->buff[buff_r_ptr], tocopy);
buff_r_ptr += tocopy;
btr -= tocopy;
/* Step 2: Read data from beginning of buffer (overflow part) */
if (btr > 0) {
BUF_MEMCPY(&d[tocopy], buff->buff, btr);
buff_r_ptr = btr;
}
/* Step 3: Check end of buffer */
if (buff_r_ptr >= buff->size) {
buff_r_ptr = 0;
}
/*
* Write final value to the actual running variable.
* This is to ensure no write operation can access intermediate data
*/
buff->r = buff_r_ptr;
BUF_SEND_EVT(buff, LWRB_EVT_READ, tocopy + btr);
return tocopy + btr;
}
/**
* \brief Read from buffer without changing read pointer (peek only)
* \param[in] buff: Buffer handle
* \param[in] skip_count: Number of bytes to skip before reading data
* \param[out] data: Pointer to output memory to copy buffer data to
* \param[in] btp: Number of bytes to peek
* \return Number of bytes peeked and written to output array
*/
size_t
lwrb_peek(lwrb_t* buff, size_t skip_count, void* data, size_t btp) {
size_t full, tocopy;
volatile size_t r;
uint8_t* d = (uint8_t*)data;
if (!BUF_IS_VALID(buff) || data == NULL || btp == 0) {
return 0;
}
r = buff->r;
/* Calculate maximum number of bytes available to read */
full = lwrb_get_full(buff);
/* Skip beginning of buffer */
if (skip_count >= full) {
return 0;
}
r += skip_count;
full -= skip_count;
if (r >= buff->size) {
r -= buff->size;
}
/* Check maximum number of bytes available to read after skip */
btp = BUF_MIN(full, btp);
if (btp == 0) {
return 0;
}
/* Step 1: Read data from linear part of buffer */
tocopy = BUF_MIN(buff->size - r, btp);
BUF_MEMCPY(d, &buff->buff[r], tocopy);
btp -= tocopy;
/* Step 2: Read data from beginning of buffer (overflow part) */
if (btp > 0) {
BUF_MEMCPY(&d[tocopy], buff->buff, btp);
}
return tocopy + btp;
}
/**
* \brief Get available size in buffer for write operation
* \param[in] buff: Buffer handle
* \return Number of free bytes in memory
*/
size_t
lwrb_get_free(lwrb_t* buff) {
size_t size;
volatile size_t w, r;
if (!BUF_IS_VALID(buff)) {
return 0;
}
/* Use temporary values in case they are changed during operations */
w = buff->w;
r = buff->r;
if (w == r) {
size = buff->size;
} else if (r > w) {
size = r - w;
} else {
size = buff->size - (w - r);
}
/* Buffer free size is always 1 less than actual size */
return size - 1;
}
/**
* \brief Get number of bytes currently available in buffer
* \param[in] buff: Buffer handle
* \return Number of bytes ready to be read
*/
size_t
lwrb_get_full(lwrb_t* buff) {
size_t size;
volatile size_t w, r;
if (!BUF_IS_VALID(buff)) {
return 0;
}
/* Use temporary values in case they are changed during operations */
w = buff->w;
r = buff->r;
if (w == r) {
size = 0;
} else if (w > r) {
size = w - r;
} else {
size = buff->size - (r - w);
}
return size;
}
/**
* \brief Resets buffer to default values. Buffer size is not modified
* \note This function is not thread safe.
* When used, application must ensure there is no active read/write operation
* \param[in] buff: Buffer handle
*/
void
lwrb_reset(lwrb_t* buff) {
if (BUF_IS_VALID(buff)) {
buff->w = 0;
buff->r = 0;
BUF_SEND_EVT(buff, LWRB_EVT_RESET, 0);
}
}
/**
* \brief Get linear address for buffer for fast read
* \param[in] buff: Buffer handle
* \return Linear buffer start address
*/
void*
lwrb_get_linear_block_read_address(lwrb_t* buff) {
if (!BUF_IS_VALID(buff)) {
return NULL;
}
return &buff->buff[buff->r];
}
/**
* \brief Get length of linear block address before it overflows for read operation
* \param[in] buff: Buffer handle
* \return Linear buffer size in units of bytes for read operation
*/
size_t
lwrb_get_linear_block_read_length(lwrb_t* buff) {
size_t len;
volatile size_t w, r;
if (!BUF_IS_VALID(buff)) {
return 0;
}
/* Use temporary values in case they are changed during operations */
w = buff->w;
r = buff->r;
if (w > r) {
len = w - r;
} else if (r > w) {
len = buff->size - r;
} else {
len = 0;
}
return len;
}
/**
* \brief Skip (ignore; advance read pointer) buffer data
* Marks data as read in the buffer and increases free memory for up to `len` bytes
*
* \note Useful at the end of streaming transfer such as DMA
* \param[in] buff: Buffer handle
* \param[in] len: Number of bytes to skip and mark as read
* \return Number of bytes skipped
*/
size_t
lwrb_skip(lwrb_t* buff, size_t len) {
size_t full;
volatile size_t r;
if (!BUF_IS_VALID(buff) || len == 0) {
return 0;
}
full = lwrb_get_full(buff);
len = BUF_MIN(len, full);
r = buff->r + len;
if (r >= buff->size) {
r -= buff->size;
}
buff->r = r;
BUF_SEND_EVT(buff, LWRB_EVT_READ, len);
return len;
}
/**
* \brief Get linear address for buffer for fast read
* \param[in] buff: Buffer handle
* \return Linear buffer start address
*/
void*
lwrb_get_linear_block_write_address(lwrb_t* buff) {
if (!BUF_IS_VALID(buff)) {
return NULL;
}
return &buff->buff[buff->w];
}
/**
* \brief Get length of linear block address before it overflows for write operation
* \param[in] buff: Buffer handle
* \return Linear buffer size in units of bytes for write operation
*/
size_t
lwrb_get_linear_block_write_length(lwrb_t* buff) {
size_t len;
volatile size_t w, r;
if (!BUF_IS_VALID(buff)) {
return 0;
}
/* Use temporary values in case they are changed during operations */
w = buff->w;
r = buff->r;
if (w >= r) {
len = buff->size - w;
/*
* When read pointer is 0,
* maximal length is one less as if too many bytes
* are written, buffer would be considered empty again (r == w)
*/
if (r == 0) {
/*
* Cannot overflow:
* - If r is not 0, statement does not get called
* - buff->size cannot be 0 and if r is 0, len is greater 0
*/
--len;
}
} else {
len = r - w - 1;
}
return len;
}
/**
* \brief Advance write pointer in the buffer.
* Similar to skip function but modifies write pointer instead of read
*
* \note Useful when hardware is writing to buffer and application needs to increase number
* of bytes written to buffer by hardware
* \param[in] buff: Buffer handle
* \param[in] len: Number of bytes to advance
* \return Number of bytes advanced for write operation
*/
size_t
lwrb_advance(lwrb_t* buff, size_t len) {
size_t free;
volatile size_t w;
if (!BUF_IS_VALID(buff) || len == 0) {
return 0;
}
/* Use local variables before writing back to main structure */
free = lwrb_get_free(buff);
len = BUF_MIN(len, free);
w = buff->w + len;
if (w >= buff->size) {
w -= buff->size;
}
buff->w = w;
BUF_SEND_EVT(buff, LWRB_EVT_WRITE, len);
return len;
}

139
Source/GPS/lwrb.h Normal file
View File

@ -0,0 +1,139 @@
/**
* \file lwrb.h
* \brief LwRB - Lightweight ring buffer
*/
/*
* 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 LwRB - Lightweight ring buffer library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v2.0.3
*/
#ifndef LWRB_HDR_H
#define LWRB_HDR_H
#include <string.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* \defgroup LWRB Lightweight ring buffer manager
* \brief Lightweight ring buffer manager
* \{
*/
/**
* \brief Enable buffer structure pointer parameters as volatile
* To use this feature, uncomment keyword below, or define in global compiler settings
*/
#ifndef LWRB_VOLATILE
#define LWRB_VOLATILE /* volatile */
#endif
/**
* \brief Adds 2 magic words to make sure if memory is corrupted
* application can detect wrong data pointer and maximum size
*/
#ifndef LWRB_USE_MAGIC
#define LWRB_USE_MAGIC 1
#endif
/**
* \brief Event type for buffer operations
*/
typedef enum {
LWRB_EVT_READ, /*!< Read event */
LWRB_EVT_WRITE, /*!< Write event */
LWRB_EVT_RESET, /*!< Reset event */
} lwrb_evt_type_t;
/**
* \brief Buffer structure forward declaration
*/
struct lwrb;
/**
* \brief Event callback function type
* \param[in] buff: Buffer handle for event
* \param[in] evt: Event type
* \param[in] bp: Number of bytes written or read (when used), depends on event type
*/
typedef void (*lwrb_evt_fn)(struct lwrb* buff, lwrb_evt_type_t evt, size_t bp);
/**
* \brief Buffer structure
*/
typedef struct lwrb {
#if LWRB_USE_MAGIC
uint32_t magic1; /*!< Magic 1 word */
#endif /* LWRB_USE_MAGIC */
uint8_t* buff; /*!< Pointer to buffer data.
Buffer is considered initialized when `buff != NULL` and `size > 0` */
LWRB_VOLATILE size_t size; /*!< Size of buffer data. Size of actual buffer is `1` byte less than value holds */
LWRB_VOLATILE size_t r; /*!< Next read pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
LWRB_VOLATILE size_t w; /*!< Next write pointer. Buffer is considered empty when `r == w` and full when `w == r - 1` */
lwrb_evt_fn evt_fn; /*!< Pointer to event callback function */
#if LWRB_USE_MAGIC
uint32_t magic2; /*!< Magic 2 word */
#endif /* LWRB_USE_MAGIC */
} lwrb_t;
uint8_t lwrb_init(lwrb_t* buff, void* buffdata, size_t size);
uint8_t lwrb_is_ready(lwrb_t* buff);
void lwrb_free(lwrb_t* buff);
void lwrb_reset(lwrb_t* buff);
void lwrb_set_evt_fn(lwrb_t* buff, lwrb_evt_fn fn);
/* Read/Write functions */
size_t lwrb_write(lwrb_t* buff, const void* data, size_t btw);
size_t lwrb_read(lwrb_t* buff, void* data, size_t btr);
size_t lwrb_peek(lwrb_t* buff, size_t skip_count, void* data, size_t btp);
/* Buffer size information */
size_t lwrb_get_free(lwrb_t* buff);
size_t lwrb_get_full(lwrb_t* buff);
/* Read data block management */
void* lwrb_get_linear_block_read_address(lwrb_t* buff);
size_t lwrb_get_linear_block_read_length(lwrb_t* buff);
size_t lwrb_skip(lwrb_t* buff, size_t len);
/* Write data block management */
void* lwrb_get_linear_block_write_address(lwrb_t* buff);
size_t lwrb_get_linear_block_write_length(lwrb_t* buff);
size_t lwrb_advance(lwrb_t* buff, size_t len);
/**
* \}
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LWRB_HDR_H */