The logging period is apparently an int, not a byte in GHP files
[GPXSee.git] / src / data / onmoveparsers.cpp
blob39132300326e580d9ce603dc2898e8e013777b94
1 #include <QFileInfo>
2 #include <QDir>
3 #include <QtEndian>
4 #include "onmoveparsers.h"
7 static inline quint16 u16(const char *buffer)
9 return qFromLittleEndian<quint16>(buffer);
12 static inline qint16 s16(const char *buffer)
14 return qFromLittleEndian<qint16>(buffer);
17 static inline qint32 s32(const char *buffer)
19 return qFromLittleEndian<qint32>(buffer);
23 bool OMDParser::readHeaderFile(const QString &omdPath, Header &hdr)
25 QFileInfo fi(omdPath);
26 QString path = fi.absoluteDir().filePath(fi.baseName() + ".OMH");
27 QFile file(path);
28 char buffer[60];
30 if (!file.open(QIODevice::ReadOnly)) {
31 qWarning("%s: %s", qPrintable(path), qPrintable(file.errorString()));
32 return false;
34 if (file.read(buffer, sizeof(buffer)) != sizeof(buffer)) {
35 qWarning("%s: invalid OMH file", qPrintable(path));
36 return false;
39 quint8 Y = buffer[14];
40 quint8 M = buffer[15];
41 quint8 D = buffer[16];
42 quint8 h = buffer[17];
43 quint8 m = buffer[18];
44 quint16 ascent = u16(buffer + 22);
45 quint16 descent = u16(buffer + 24);
46 quint8 avgHr = buffer[12];
47 quint8 maxHr = buffer[13];
49 QDateTime date(QDate(Y + 2000, M, D), QTime(h, m), Qt::UTC);
50 if (!date.isValid()) {
51 qWarning("%s: invalid date", qPrintable(path));
52 return false;
55 hdr.date = date;
56 hdr.hr = avgHr || maxHr;
57 hdr.elevation = ascent || descent;
59 return true;
62 bool OMDParser::readF1(const char *chunk, const Header &hdr, Sequence &seq,
63 SegmentData &segment)
65 if (seq.cnt > 1) {
66 _errorString = "invalid chunk sequence";
67 return false;
70 qint32 lat = s32(chunk);
71 qint32 lon = s32(chunk + 4);
72 quint16 sec = u16(chunk + 12);
73 quint8 fia = chunk[14];
74 qint16 alt = s16(chunk + 15);
76 if (fia == 3) {
77 Trackpoint t(Coordinates(lon / 1000000.0, lat / 1000000.0));
78 if (!t.coordinates().isValid()) {
79 _errorString = "invalid coordinates";
80 return false;
82 t.setTimestamp(QDateTime(hdr.date.date(),
83 hdr.date.time().addSecs(sec), Qt::UTC));
84 if (hdr.elevation)
85 t.setElevation(alt);
87 seq.idx[seq.cnt] = segment.size();
88 segment.append(t);
89 } else
90 seq.idx[seq.cnt] = -1;
92 seq.cnt++;
94 return true;
97 bool OMDParser::readF2(const char *chunk, const Header &hdr, Sequence &seq,
98 SegmentData &segment)
100 if (!seq.cnt) {
101 _errorString = "invalid chunk sequence";
102 return false;
105 quint16 speed1 = u16(chunk + 2);
106 quint8 hr1 = chunk[6];
107 quint16 speed2 = u16(chunk + 12);
108 quint8 hr2 = chunk[16];
110 if (seq.idx[0] >= 0) {
111 Trackpoint &p0 = segment[seq.idx[0]];
112 if (hdr.hr)
113 p0.setHeartRate(hr1);
114 p0.setSpeed(speed1 / 360.0);
116 if (seq.idx[1] >= 0) {
117 Trackpoint &p1 = segment[seq.idx[1]];
118 if (hdr.hr)
119 p1.setHeartRate(hr2);
120 p1.setSpeed(speed2 / 360.0);
123 seq.idx[0] = -1;
124 seq.idx[1] = -1;
125 seq.cnt = 0;
127 return true;
130 bool OMDParser::parse(QFile *file, QList<TrackData> &tracks,
131 QList<RouteData> &routes, QList<Area> &polygons, QVector<Waypoint> &waypoints)
133 Q_UNUSED(routes);
134 Q_UNUSED(waypoints);
135 Q_UNUSED(polygons);
136 SegmentData segment;
137 Header hdr;
138 Sequence seq;
139 char chunk[20];
140 qint64 size;
142 // If no header file is found or it is invalid, continue with the default
143 // header values. The track will have a fictional date and possibly some
144 // zero-graphs, but it will be still usable.
145 readHeaderFile(file->fileName(), hdr);
147 while ((size = file->read(chunk, sizeof(chunk))) == sizeof(chunk)) {
148 switch ((quint8)chunk[19]) {
149 case 0xF1:
150 if (!readF1(chunk, hdr, seq, segment))
151 return false;
152 break;
153 case 0xF2:
154 if (!readF2(chunk, hdr, seq, segment))
155 return false;
156 break;
157 default:
158 _errorString = "invalid chunk type";
159 return false;
163 if (size < 0) {
164 _errorString = "I/O error";
165 return false;
166 } else if (size) {
167 _errorString = "unexpected end of file";
168 return false;
171 tracks.append(TrackData());
172 tracks.last().append(segment);
174 return true;
178 bool GHPParser::readHeaderFile(const QString &ghpPath, Header &hdr)
180 QFileInfo fi(ghpPath);
181 QString path = fi.absoluteDir().filePath(fi.baseName() + ".GHT");
182 QFile file(path);
183 char buffer[96];
185 if (!file.open(QIODevice::ReadOnly)) {
186 qWarning("%s: %s", qPrintable(path), qPrintable(file.errorString()));
187 return false;
189 if (file.read(buffer, sizeof(buffer)) != sizeof(buffer)) {
190 qWarning("%s: invalid GHT file", qPrintable(path));
191 return false;
194 quint8 Y = buffer[0];
195 quint8 M = buffer[1];
196 quint8 D = buffer[2];
197 quint8 h = buffer[3];
198 quint8 m = buffer[4];
199 quint8 s = buffer[5];
200 quint8 avgHr = buffer[61];
201 quint8 maxHr = buffer[60];
203 QDateTime date(QDate(Y + 2000, M, D), QTime(h, m, s), Qt::UTC);
204 if (!date.isValid()) {
205 qWarning("%s: invalid date", qPrintable(path));
206 return false;
209 hdr.date = date;
210 hdr.hr = avgHr || maxHr;
212 return true;
215 bool GHPParser::readF0(const char *chunk, const Header &hdr, int &time,
216 SegmentData &segment)
218 qint32 lat = s32(chunk);
219 qint32 lon = s32(chunk + 4);
220 qint16 alt = s16(chunk + 8);
221 quint16 speed = u16(chunk + 10);
222 quint8 hr = chunk[12];
223 quint8 fia = chunk[13];
224 qint32 ms = s32(chunk + 16);
226 if (fia == 3) {
227 Trackpoint t(Coordinates(lon / 1000000.0, lat / 1000000.0));
228 if (!t.coordinates().isValid()) {
229 _errorString = "invalid coordinates";
230 return false;
232 t.setTimestamp(QDateTime(hdr.date.date(),
233 hdr.date.time().addMSecs(time * 100), Qt::UTC));
234 t.setSpeed(speed / 360.0);
235 t.setElevation(alt);
236 if (hdr.hr)
237 t.setHeartRate(hr);
239 segment.append(t);
242 time += ms;
244 return true;
247 bool GHPParser::parse(QFile *file, QList<TrackData> &tracks,
248 QList<RouteData> &routes, QList<Area> &polygons, QVector<Waypoint> &waypoints)
250 Q_UNUSED(routes);
251 Q_UNUSED(waypoints);
252 Q_UNUSED(polygons);
253 SegmentData segment;
254 Header hdr;
255 int time = 0;
256 char chunk[20];
257 qint64 size;
259 // see OMD
260 readHeaderFile(file->fileName(), hdr);
262 while ((size = file->read(chunk, sizeof(chunk))) == sizeof(chunk))
263 if (!readF0(chunk, hdr, time, segment))
264 return false;
266 if (size < 0) {
267 _errorString = "I/O error";
268 return false;
269 } else if (size) {
270 _errorString = "unexpected end of file";
271 return false;
273 if (!segment.size()) {
274 _errorString = "No usable data found";
275 return false;
278 tracks.append(TrackData());
279 tracks.last().append(segment);
281 return true;