diff --git a/gpxsee.pro b/gpxsee.pro index 21c253e8..8fd7a08e 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -95,6 +95,7 @@ HEADERS += src/common/config.h \ src/GUI/areaitem.h \ src/data/itnparser.h \ src/data/link.h \ + src/data/omdparser.h \ src/data/ov2parser.h \ src/map/IMG/bitmapline.h \ src/map/IMG/bitstream.h \ @@ -289,6 +290,7 @@ SOURCES += src/main.cpp \ src/GUI/areaitem.cpp \ src/data/address.cpp \ src/data/itnparser.cpp \ + src/data/omdparser.cpp \ src/data/ov2parser.cpp \ src/data/waypoint.cpp \ src/map/IMG/bitmapline.cpp \ diff --git a/src/data/data.cpp b/src/data/data.cpp index 8613499e..49b2da02 100644 --- a/src/data/data.cpp +++ b/src/data/data.cpp @@ -19,6 +19,7 @@ #include "smlparser.h" #include "ov2parser.h" #include "itnparser.h" +#include "omdparser.h" #include "data.h" @@ -41,6 +42,7 @@ static GPIParser gpi; static SMLParser sml; static OV2Parser ov2; static ITNParser itn; +static OMDParser omd; static QMap parsers() { @@ -67,6 +69,7 @@ static QMap parsers() map.insert("sml", &sml); map.insert("ov2", &ov2); map.insert("itn", &itn); + map.insert("omd", &omd); return map; } @@ -144,6 +147,7 @@ QString Data::formats() + qApp->translate("Data", "KML files") + " (*.kml);;" + qApp->translate("Data", "LOC files") + " (*.loc);;" + qApp->translate("Data", "NMEA files") + " (*.nmea);;" + + qApp->translate("Data", "OMD files") + " (*.omd);;" + qApp->translate("Data", "OV2 files") + " (*.ov2);;" + qApp->translate("Data", "OziExplorer files") + " (*.plt *.rte *.wpt);;" + qApp->translate("Data", "SLF files") + " (*.slf);;" diff --git a/src/data/omdparser.cpp b/src/data/omdparser.cpp new file mode 100644 index 00000000..b7ad90ea --- /dev/null +++ b/src/data/omdparser.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include "omdparser.h" + + +static quint16 u16(const char *buffer) +{ + return qFromLittleEndian(buffer); +} + +static qint16 s16(const char *buffer) +{ + return qFromLittleEndian(buffer); +} + +static qint32 s32(const char *buffer) +{ + return qFromLittleEndian(buffer); +} + + +bool OMDParser::readHeaderFile(const QString &omdPath, Header &hdr) +{ + QFileInfo fi(omdPath); + QString path = fi.absoluteDir().filePath(fi.baseName() + ".OMH"); + QFile file(path); + char buffer[60]; + + if (!file.open(QIODevice::ReadOnly)) { + qWarning("%s: %s", qPrintable(path), qPrintable(file.errorString())); + return false; + } + if (file.read(buffer, sizeof(buffer)) != sizeof(buffer)) { + qWarning("%s: invalid OMH file", qPrintable(path)); + return false; + } + + quint8 Y = buffer[14]; + quint8 M = buffer[15]; + quint8 D = buffer[16]; + quint8 h = buffer[17]; + quint8 m = buffer[18]; + quint16 ascent = u16(buffer + 22); + quint16 descent = u16(buffer + 24); + quint8 avgHr = buffer[12]; + quint8 maxHr = buffer[13]; + + QDateTime date(QDate(Y + 2000, M, D), QTime(h, m), Qt::UTC); + if (!date.isValid()) { + qWarning("%s: invalid date", qPrintable(path)); + return false; + } + + hdr.date = date; + hdr.hr = avgHr || maxHr; + hdr.elevation = ascent || descent; + + return true; +} + +bool OMDParser::readF1(const char *chunk, const Header &hdr, Sequence &seq, + SegmentData &segment) +{ + if (seq.cnt > 1) { + _errorString = "invalid chunk sequence"; + return false; + } + + qint32 lat = s32(chunk); + qint32 lon = s32(chunk + 4); + quint16 sec = u16(chunk + 12); + quint8 fia = chunk[14]; + qint16 alt = s16(chunk + 15); + + if (fia == 3) { + Trackpoint t(Coordinates(lon / 1000000.0, lat / 1000000.0)); + if (!t.coordinates().isValid()) { + _errorString = "invalid coordinates"; + return false; + } + t.setTimestamp(QDateTime(hdr.date.date(), + hdr.date.time().addSecs(sec), Qt::UTC)); + if (hdr.elevation) + t.setElevation(alt); + + seq.idx[seq.cnt] = segment.size(); + segment.append(t); + } else + seq.idx[seq.cnt] = -1; + + seq.cnt++; + + return true; +} + +bool OMDParser::readF2(const char *chunk, const Header &hdr, Sequence &seq, + SegmentData &segment) +{ + if (!seq.cnt) { + _errorString = "invalid chunk sequence"; + return false; + } + + quint16 speed1 = u16(chunk + 2); + quint8 hr1 = chunk[6]; + quint16 speed2 = u16(chunk + 12); + quint8 hr2 = chunk[16]; + + if (seq.idx[0] >= 0) { + Trackpoint &p0 = segment[seq.idx[0]]; + if (hdr.hr) + p0.setHeartRate(hr1); + p0.setSpeed(speed1 / 360.0); + } + if (seq.idx[1] >= 0) { + Trackpoint &p1 = segment[seq.idx[1]]; + if (hdr.hr) + p1.setHeartRate(hr2); + p1.setSpeed(speed2 / 360.0); + } + + seq.idx[0] = -1; + seq.idx[1] = -1; + seq.cnt = 0; + + return true; +} + +bool OMDParser::parse(QFile *file, QList &tracks, + QList &routes, QList &polygons, QVector &waypoints) +{ + Q_UNUSED(routes); + Q_UNUSED(waypoints); + Q_UNUSED(polygons); + SegmentData segment; + Header hdr; + Sequence seq; + char chunk[20]; + qint64 size; + + // If no header file is found or it is invalid, continue with the default + // header values. The track will have a fictional date and possibly some + // zero-graphs, but it will be still usable. + readHeaderFile(file->fileName(), hdr); + + while ((size = file->read(chunk, sizeof(chunk))) == sizeof(chunk)) { + switch ((quint8)chunk[19]) { + case 0xF1: + if (!readF1(chunk, hdr, seq, segment)) + return false; + break; + case 0xF2: + if (!readF2(chunk, hdr, seq, segment)) + return false; + break; + default: + _errorString = "invalid chunk type"; + return false; + } + } + + if (size < 0) { + _errorString = "I/O error"; + return false; + } else if (size) { + _errorString = "unexpected end of file"; + return false; + } + + tracks.append(TrackData()); + tracks.last().append(segment); + + return true; +} diff --git a/src/data/omdparser.h b/src/data/omdparser.h new file mode 100644 index 00000000..d7c93a4e --- /dev/null +++ b/src/data/omdparser.h @@ -0,0 +1,42 @@ +#ifndef OMDPARSER_H +#define OMDPARSER_H + +#include "parser.h" + +class OMDParser : public Parser +{ +public: + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); + QString errorString() const {return _errorString;} + int errorLine() const {return 0;} + +private: + struct Header + { + Header() : date(QDateTime(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC)), + elevation(true), hr(true) {} + + QDateTime date; + bool elevation; + bool hr; + }; + + struct Sequence + { + Sequence() : cnt(0), idx{-1, -1} {} + + unsigned cnt; + int idx[2]; + }; + + bool readHeaderFile(const QString &omdPath, Header &hdr); + bool readF1(const char *chunk, const Header &hdr, Sequence &seq, + SegmentData &segment); + bool readF2(const char *chunk, const Header &hdr, Sequence &seq, + SegmentData &segment); + + QString _errorString; +}; + +#endif // OMDPARSER_H