1.air部署,细节待修改
This commit is contained in:
251
Source/GPS/BD357Ctrl.cpp
Normal file
251
Source/GPS/BD357Ctrl.cpp
Normal 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
50
Source/GPS/BD357Ctrl.h
Normal 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
606
Source/GPS/lwgps.cpp
Normal 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
289
Source/GPS/lwgps.h
Normal 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
171
Source/GPS/lwgps_opt.h
Normal 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
44
Source/GPS/lwgps_opts.h
Normal 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
495
Source/GPS/lwrb.cpp
Normal 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
139
Source/GPS/lwrb.h
Normal 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 */
|
||||
Reference in New Issue
Block a user