mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-11-30 22:51:16 +01:00
Added support for NMEA files
This commit is contained in:
parent
e9e7660beb
commit
535361dada
@ -69,7 +69,8 @@ HEADERS += src/config.h \
|
||||
src/assert.h \
|
||||
src/cadencegraph.h \
|
||||
src/powergraph.h \
|
||||
src/igcparser.h
|
||||
src/igcparser.h \
|
||||
src/nmeaparser.h
|
||||
SOURCES += src/main.cpp \
|
||||
src/gui.cpp \
|
||||
src/poi.cpp \
|
||||
@ -119,7 +120,8 @@ SOURCES += src/main.cpp \
|
||||
src/cadencegraph.cpp \
|
||||
src/powergraph.cpp \
|
||||
src/igcparser.cpp \
|
||||
src/path.cpp
|
||||
src/path.cpp \
|
||||
src/nmeaparser.cpp
|
||||
RESOURCES += gpxsee.qrc
|
||||
TRANSLATIONS = lang/gpxsee_cs.ts
|
||||
macx {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "kmlparser.h"
|
||||
#include "fitparser.h"
|
||||
#include "igcparser.h"
|
||||
#include "nmeaparser.h"
|
||||
#include "data.h"
|
||||
|
||||
|
||||
@ -24,6 +25,8 @@ Data::Data() : _errorLine(0)
|
||||
_waypointData));
|
||||
_parsers.insert("igc", new IGCParser(_trackData, _routeData,
|
||||
_waypointData));
|
||||
_parsers.insert("nmea", new NMEAParser(_trackData, _routeData,
|
||||
_waypointData));
|
||||
}
|
||||
|
||||
Data::~Data()
|
||||
|
11
src/gui.cpp
11
src/gui.cpp
@ -108,17 +108,18 @@ GUI::~GUI()
|
||||
|
||||
const QString GUI::fileFormats() const
|
||||
{
|
||||
return tr("Supported files (*.csv *.fit *.gpx *.igc *.kml *.tcx)") + ";;"
|
||||
+ tr("CSV files (*.csv)") + ";;" + tr("FIT files (*.fit)") + ";;"
|
||||
return tr("Supported files (*.csv *.fit *.gpx *.igc *.kml *.nmea *.tcx)")
|
||||
+ ";;" + tr("CSV files (*.csv)") + ";;" + tr("FIT files (*.fit)") + ";;"
|
||||
+ tr("GPX files (*.gpx)") + ";;" + tr("IGC files (*.igc)") + ";;"
|
||||
+ tr("KML files (*.kml)") + ";;" + tr("TCX files (*.tcx)") + ";;"
|
||||
+ tr("All files (*)");
|
||||
+ tr("KML files (*.kml)") + ";;" + tr("NMEA files (*.nmea)") + ";;"
|
||||
+ tr("TCX files (*.tcx)") + ";;" + tr("All files (*)");
|
||||
}
|
||||
|
||||
void GUI::createBrowser()
|
||||
{
|
||||
QStringList filter;
|
||||
filter << "*.gpx" << "*.tcx" << "*.kml" << "*.fit" << "*.csv" << "*.igc";
|
||||
filter << "*.gpx" << "*.tcx" << "*.kml" << "*.fit" << "*.csv" << "*.igc"
|
||||
<< "*.nmea";
|
||||
_browser = new FileBrowser(this);
|
||||
_browser->setFilter(filter);
|
||||
}
|
||||
|
@ -1,22 +1,8 @@
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include "misc.h"
|
||||
#include "igcparser.h"
|
||||
|
||||
|
||||
static int str2int(const char *str, size_t len)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (const char *sp = str; sp < str + len; sp++) {
|
||||
if (::isdigit(*sp))
|
||||
res = res * 10 + *sp - '0';
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool readLat(const char *data, qreal &lat)
|
||||
{
|
||||
int d = str2int(data, 2);
|
||||
@ -93,6 +79,8 @@ static bool readTimestamp(const char *data, QTime &time)
|
||||
return false;
|
||||
|
||||
time = QTime(h, m, s);
|
||||
if (!time.isValid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -108,7 +96,7 @@ static bool readARecord(const char *line, qint64 len)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IGCParser::readHRecord(const char *line, qint64 len)
|
||||
bool IGCParser::readHRecord(const char *line, int len)
|
||||
{
|
||||
if (len < 10 || ::strncmp(line, "HFDTE", 5))
|
||||
return true;
|
||||
@ -131,7 +119,7 @@ bool IGCParser::readHRecord(const char *line, qint64 len)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IGCParser::readBRecord(const char *line, qint64 len)
|
||||
bool IGCParser::readBRecord(const char *line, int len)
|
||||
{
|
||||
qreal lat, lon, ele;
|
||||
QTime time;
|
||||
@ -172,7 +160,7 @@ bool IGCParser::readBRecord(const char *line, qint64 len)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IGCParser::readCRecord(const char *line, qint64 len)
|
||||
bool IGCParser::readCRecord(const char *line, int len)
|
||||
{
|
||||
qreal lat, lon;
|
||||
|
||||
|
@ -19,9 +19,9 @@ public:
|
||||
int errorLine() const {return _errorLine;}
|
||||
|
||||
private:
|
||||
bool readHRecord(const char *line, qint64 len);
|
||||
bool readBRecord(const char *line, qint64 len);
|
||||
bool readCRecord(const char *line, qint64 len);
|
||||
bool readHRecord(const char *line, int len);
|
||||
bool readBRecord(const char *line, int len);
|
||||
bool readCRecord(const char *line, int len);
|
||||
|
||||
int _errorLine;
|
||||
QString _errorString;
|
||||
|
15
src/misc.cpp
15
src/misc.cpp
@ -1,4 +1,5 @@
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
#include "misc.h"
|
||||
|
||||
|
||||
@ -33,3 +34,17 @@ double niceNum(double x, int round)
|
||||
|
||||
return nf * pow(10.0, expv);
|
||||
}
|
||||
|
||||
int str2int(const char *str, int len)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (const char *sp = str; sp < str + len; sp++) {
|
||||
if (::isdigit(*sp))
|
||||
res = res * 10 + *sp - '0';
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -2,5 +2,6 @@
|
||||
#define MISC_H
|
||||
|
||||
double niceNum(double x, int round);
|
||||
int str2int(const char *str, int len);
|
||||
|
||||
#endif // MISC_H
|
||||
|
420
src/nmeaparser.cpp
Normal file
420
src/nmeaparser.cpp
Normal file
@ -0,0 +1,420 @@
|
||||
#include <cstring>
|
||||
#include "misc.h"
|
||||
#include "nmeaparser.h"
|
||||
|
||||
|
||||
static bool readTime(const char *data, int len, QTime &time)
|
||||
{
|
||||
int h, m, s ,ms;
|
||||
|
||||
if (len < 9)
|
||||
return false;
|
||||
|
||||
h = str2int(data, 2);
|
||||
m = str2int(data + 2, 2);
|
||||
s = str2int(data + 4, 2);
|
||||
ms = str2int(data + 7, len - 7);
|
||||
if (h < 0 || m < 0 || s < 0 || ms < 0 || data[6] != '.')
|
||||
return false;
|
||||
|
||||
time = QTime(h, m, s, ms);
|
||||
if (!time.isValid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readDate(const char *data, int len, QDate &date)
|
||||
{
|
||||
int y, m, d;
|
||||
|
||||
if (len != 6)
|
||||
return false;
|
||||
|
||||
d = str2int(data, 2);
|
||||
m = str2int(data + 2, 2);
|
||||
y = str2int(data + 4, 2);
|
||||
if (d < 0 || m < 0 || y < 0)
|
||||
return false;
|
||||
|
||||
date = QDate(2000 + y, m, d);
|
||||
if (!date.isValid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readLat(const char *data, int len, qreal &lat)
|
||||
{
|
||||
int d, mi;
|
||||
qreal mf;
|
||||
bool ok;
|
||||
|
||||
if (len < 7 || data[4] != '.')
|
||||
return false;
|
||||
|
||||
d = str2int(data, 2);
|
||||
mi = str2int(data + 2, 2);
|
||||
mf = QString(QByteArray::fromRawData(data + 4, len - 4)).toFloat(&ok);
|
||||
if (d < 0 || mi < 0 || !ok)
|
||||
return false;
|
||||
|
||||
lat = d + (((qreal)mi + mf) / 60.0);
|
||||
if (lat > 90)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readLon(const char *data, int len, qreal &lon)
|
||||
{
|
||||
int d, mi;
|
||||
qreal mf;
|
||||
bool ok;
|
||||
|
||||
if (len < 8 || data[5] != '.')
|
||||
return false;
|
||||
|
||||
d = str2int(data, 3);
|
||||
mi = str2int(data + 3, 2);
|
||||
mf = QString(QByteArray::fromRawData(data + 5, len - 5)).toFloat(&ok);
|
||||
if (d < 0 || mi < 0 || !ok)
|
||||
return false;
|
||||
|
||||
lon = d + (((qreal)mi + mf) / 60.0);
|
||||
if (lon > 180)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readFloat(const char *data, int len, qreal &f)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
f = QString(QByteArray::fromRawData(data, len)).toFloat(&ok);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool NMEAParser::readRMC(const char *line, int len)
|
||||
{
|
||||
int col = 1;
|
||||
const char *vp = line;
|
||||
qreal lat, lon;
|
||||
QTime time;
|
||||
bool valid = true;
|
||||
|
||||
for (const char *lp = line; lp < line + len; lp++) {
|
||||
if (*lp == ',' || *lp == '*') {
|
||||
switch (col) {
|
||||
case 1:
|
||||
if (!readTime(vp, lp - vp, time)) {
|
||||
_errorString = "Invalid time";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (*vp != 'A')
|
||||
valid = false;
|
||||
break;
|
||||
case 3:
|
||||
if (!readLat(vp, lp - vp, lat)) {
|
||||
_errorString = "Invalid latitude";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (!(*vp == 'N' || *vp == 'S')) {
|
||||
_errorString = "Invalid latitude N|S";
|
||||
return false;
|
||||
}
|
||||
if (*lp == 'S')
|
||||
lat = -lat;
|
||||
break;
|
||||
case 5:
|
||||
if (!readLon(vp, lp - vp, lon)) {
|
||||
_errorString = "Invalid longitude";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (!(*vp == 'E' || *vp == 'W')) {
|
||||
_errorString = "Invalid latitude E|W";
|
||||
return false;
|
||||
}
|
||||
if (*vp == 'W')
|
||||
lon = -lon;
|
||||
break;
|
||||
case 9:
|
||||
if (!readDate(vp, lp - vp, _date)) {
|
||||
_errorString = "Invalid date";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
col++;
|
||||
vp = lp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (col < 9) {
|
||||
_errorString = "Invalid RMC sentence";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (valid && !_GGA) {
|
||||
Trackpoint t(Coordinates(lon, lat));
|
||||
t.setTimestamp(QDateTime(_date, time, Qt::UTC));
|
||||
_tracks.last().append(t);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NMEAParser::readGGA(const char *line, int len)
|
||||
{
|
||||
int col = 1;
|
||||
const char *vp = line;
|
||||
qreal lat, lon, ele, gh;
|
||||
QTime time;
|
||||
|
||||
for (const char *lp = line; lp < line + len; lp++) {
|
||||
if (*lp == ',' || *lp == '*') {
|
||||
switch (col) {
|
||||
case 1:
|
||||
if (!readTime(vp, lp - vp, time)) {
|
||||
_errorString = "Invalid time";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!readLat(vp, lp - vp, lat)) {
|
||||
_errorString = "Invalid latitude";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!(*vp == 'N' || *vp == 'S')) {
|
||||
_errorString = "Invalid latitude N|S";
|
||||
return false;
|
||||
}
|
||||
if (*vp == 'S')
|
||||
lat = -lat;
|
||||
break;
|
||||
case 4:
|
||||
if (!readLon(vp, lp - vp, lon)) {
|
||||
_errorString = "Invalid longitude";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (!(*vp == 'E' || *vp == 'W')) {
|
||||
_errorString = "Invalid latitude E|W";
|
||||
return false;
|
||||
}
|
||||
if (*vp == 'W')
|
||||
lon = -lon;
|
||||
break;
|
||||
case 9:
|
||||
if ((lp - vp) && !readFloat(vp, lp - vp, ele)) {
|
||||
_errorString = "Invalid altitude";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
if ((lp - vp) && *vp != 'M') {
|
||||
_errorString = "Invalid altitude units";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
if ((lp - vp) && !readFloat(vp, lp - vp, gh)) {
|
||||
_errorString = "Invalid geoid height";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
if ((lp - vp) && *vp != 'M') {
|
||||
_errorString = "Invalid geoid height units";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
col++;
|
||||
vp = lp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (col < 12) {
|
||||
_errorString = "Invalid GGA sentence";
|
||||
return false;
|
||||
}
|
||||
|
||||
_GGA = true;
|
||||
|
||||
Trackpoint t(Coordinates(lon, lat));
|
||||
t.setTimestamp(QDateTime(_date, time, Qt::UTC));
|
||||
t.setElevation(ele - gh);
|
||||
_tracks.last().append(t);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NMEAParser::readWPL(const char *line, int len)
|
||||
{
|
||||
int col = 1;
|
||||
const char *vp = line;
|
||||
qreal lat, lon;
|
||||
QString name;
|
||||
|
||||
for (const char *lp = line; lp < line + len; lp++) {
|
||||
if (*lp == ',' || *lp == '*') {
|
||||
switch (col) {
|
||||
case 1:
|
||||
if (!readLat(vp, lp - vp, lat)) {
|
||||
_errorString = "Invalid latitude";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!(*vp == 'N' || *vp == 'S')) {
|
||||
_errorString = "Invalid latitude N|S";
|
||||
return false;
|
||||
}
|
||||
if (*vp == 'S')
|
||||
lat = -lat;
|
||||
break;
|
||||
case 3:
|
||||
if (!readLon(vp, lp - vp, lon)) {
|
||||
_errorString = "Invalid longitude";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (!(*vp == 'E' || *vp == 'W')) {
|
||||
_errorString = "Invalid latitude E|W";
|
||||
return false;
|
||||
}
|
||||
if (*vp == 'W')
|
||||
lon = -lon;
|
||||
break;
|
||||
case 5:
|
||||
name = QString(QByteArray(vp, lp - vp));
|
||||
break;
|
||||
}
|
||||
|
||||
col++;
|
||||
vp = lp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (col < 4) {
|
||||
_errorString = "Invalid WPL sentence";
|
||||
return false;
|
||||
}
|
||||
|
||||
Waypoint w(Coordinates(lon, lat));
|
||||
w.setName(name);
|
||||
_waypoints.append(w);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NMEAParser::readZDA(const char *line, int len)
|
||||
{
|
||||
int col = 1;
|
||||
const char *vp = line;
|
||||
int d, m, y;
|
||||
|
||||
for (const char *lp = line; lp < line + len; lp++) {
|
||||
if (*lp == ',' || *lp == '*') {
|
||||
switch (col) {
|
||||
case 2:
|
||||
if ((d = str2int(vp, lp - vp)) < 0) {
|
||||
_errorString = "Invalid day";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if ((m = str2int(vp, lp - vp)) < 0) {
|
||||
_errorString = "Invalid month";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if ((y = str2int(vp, lp - vp)) < 0) {
|
||||
_errorString = "Invalid year";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
col++;
|
||||
vp = lp + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (col < 4) {
|
||||
_errorString = "Invalid ZDA sentence";
|
||||
return false;
|
||||
}
|
||||
|
||||
_date = QDate(y, m, d);
|
||||
if (!_date.isValid()) {
|
||||
_errorString = "Invalid date";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NMEAParser::loadFile(QFile *file)
|
||||
{
|
||||
qint64 len;
|
||||
char line[80 + 2 + 1 + 1];
|
||||
|
||||
|
||||
_errorLine = 1;
|
||||
_errorString.clear();
|
||||
|
||||
_GGA = false;
|
||||
_tracks.append(TrackData());
|
||||
|
||||
while (!file->atEnd()) {
|
||||
len = file->readLine(line, sizeof(line));
|
||||
|
||||
if (len < 0) {
|
||||
_errorString = "I/O error";
|
||||
return false;
|
||||
} else if (len > (qint64)sizeof(line) - 1) {
|
||||
_errorString = "Line limit exceeded";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (len < 7 || line[0] != '$') {
|
||||
_errorString = "Format error";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!memcmp(line + 3, "RMC,", 4)) {
|
||||
if (!readRMC(line + 7, len))
|
||||
return false;
|
||||
} else if (!memcmp(line + 3, "GGA,", 4)) {
|
||||
if (!readGGA(line + 7, len))
|
||||
return false;
|
||||
} else if (!memcmp(line + 3, "WPL,", 4)) {
|
||||
if (!readWPL(line + 7, len))
|
||||
return false;
|
||||
} else if (!memcmp(line + 3, "ZDA,", 4)) {
|
||||
if (!readZDA(line + 7, len))
|
||||
return false;
|
||||
}
|
||||
|
||||
_errorLine++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
33
src/nmeaparser.h
Normal file
33
src/nmeaparser.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef NMEAPARSER_H
|
||||
#define NMEAPARSER_H
|
||||
|
||||
#include <QDate>
|
||||
#include "parser.h"
|
||||
|
||||
|
||||
class NMEAParser : public Parser
|
||||
{
|
||||
public:
|
||||
NMEAParser(QList<TrackData> &tracks, QList<RouteData> &routes,
|
||||
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints)
|
||||
{_errorLine = 0; _GGA = false;}
|
||||
~NMEAParser() {}
|
||||
|
||||
bool loadFile(QFile *file);
|
||||
QString errorString() const {return _errorString;}
|
||||
int errorLine() const {return _errorLine;}
|
||||
|
||||
private:
|
||||
bool readRMC(const char *line, int len);
|
||||
bool readGGA(const char *line, int len);
|
||||
bool readWPL(const char *line, int len);
|
||||
bool readZDA(const char *line, int len);
|
||||
|
||||
int _errorLine;
|
||||
QString _errorString;
|
||||
|
||||
QDate _date;
|
||||
bool _GGA;
|
||||
};
|
||||
|
||||
#endif // NMEAPARSER_H
|
Loading…
Reference in New Issue
Block a user