1 /*
2
3 Support for "GeoGrid Viewer" binary tracklogs (*.log)
4
5 Copyright (C) 2007 Olaf Klein, o.b.klein@gpsbabel.org
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21 */
22
23 #include <cmath> // for fabs, floor, lround
24 #include <cstdio> // for sscanf
25 #include <cstring> // for memset, strncmp
26 #include <cstdint> // for int16_t
27 #include <ctime> // for gmtime
28
29 #include <QtCore/QString> // for QString
30 #include <QtCore/QTime> // for QTime
31 #include <QtCore/QtGlobal> // for foreach
32
33 #include "defs.h"
34 #include "gbfile.h" // for gbfputint16, gbfclose, gbfopen, gbfputflt, gbfgetc, gbfputcstr, gbfputdbl, gbfread, gbfile
35 #include "grtcirc.h" // for heading_true_degrees
36 #include "src/core/datetime.h" // for DateTime
37
38
39
40 #define MYNAME "ggv_log"
41
42 static gbfile* fin, *fout;
43 static int ggv_log_ver;
44
45 static
46 QVector<arglist_t> ggv_log_args = {
47 };
48
49 /*******************************************************************************
50 * %%% global callbacks called by gpsbabel main process %%% *
51 *******************************************************************************/
52
53 static void
ggv_log_rd_init(const QString & fname)54 ggv_log_rd_init(const QString& fname)
55 {
56 static char magic[32];
57 int len = 0;
58
59 fin = gbfopen(fname, "rb", MYNAME);
60
61 for (;;) {
62 int cin = gbfgetc(fin);
63 if (cin < 0) {
64 break;
65 }
66
67 magic[len++] = cin;
68
69 if (cin == '\0') {
70 double ver = 0;
71 if (strncmp(magic, "DOMGVGPS Logfile V", 18) != 0) {
72 break;
73 }
74
75 char* sver = &magic[18];
76 sscanf(sver, "%lf:", &ver);
77 ggv_log_ver = ver * 10;
78 if ((ggv_log_ver == 10) || (ggv_log_ver == 25)) {
79 return; /* header accepted */
80 }
81
82 fatal(MYNAME ": Sorry, unsupported version (%s)!\n", sver);
83 } else if (len == sizeof(magic)) {
84 break;
85 }
86 }
87 fatal(MYNAME ": Invalid header. Probably no " MYNAME " file!\n");
88 }
89
90 static void
ggv_log_rd_deinit()91 ggv_log_rd_deinit()
92 {
93 gbfclose(fin);
94 }
95
96 static void
ggv_log_read()97 ggv_log_read()
98 {
99 int bufsz = 0, len;
100 route_head* trk = nullptr;
101
102 switch (ggv_log_ver) {
103 case 10:
104 bufsz = 0x2A;
105 break;
106 case 25:
107 bufsz = 0x6F;
108 break;
109 }
110
111 auto* buf = (signed char*) xmalloc(bufsz);
112
113 while ((len = gbfread(buf, 1, bufsz, fin))) {
114 struct tm tm;
115
116 if (len != bufsz) {
117 break;
118 }
119
120 if (trk == nullptr) {
121 trk = new route_head;
122 track_add_head(trk);
123 }
124
125 memset(&tm, 0, sizeof(tm));
126
127 auto* wpt = new Waypoint;
128
129 int deg = (int16_t) le_read16(&buf[0]);
130 int min = le_read16(&buf[2]);
131 float sec = le_read_float(&buf[4]);
132 double xlat = (double)deg + ((double)min / 60.0) + (sec / 3600.0);
133 wpt->latitude = xlat;
134
135 deg = (int16_t) le_read16(&buf[8]);
136 min = le_read16(&buf[10]);
137 sec = le_read_float(&buf[12]);
138 double xlon = (double)deg + ((double)min / 60.0) + (sec / 3600.0);
139 wpt->longitude = xlon;
140
141 WAYPT_SET(wpt, course, le_read16(&buf[16 + 0]));
142 int milliseconds = 0;
143 if (ggv_log_ver == 10) {
144 wpt->altitude = le_read16(&buf[16 + 2]);
145 WAYPT_SET(wpt, speed, le_read16(&buf[16 + 4]));
146 tm.tm_year = le_read16(&buf[16 + 8]);
147 tm.tm_mon = le_read16(&buf[16 + 10]);
148 tm.tm_mday = le_read16(&buf[16 + 12]);
149 tm.tm_hour = le_read16(&buf[16 + 14]);
150 tm.tm_min = le_read16(&buf[16 + 16]);
151 double secs = le_read_double(&buf[16 + 18]);
152 tm.tm_sec = (int)secs;
153 milliseconds = lround((secs - tm.tm_sec) * 1000.0);
154 } else {
155 wpt->altitude = le_read16(&buf[16 + 4]);
156 wpt->sat = (unsigned char)buf[16 + 14];
157
158 /* other probably valid double values at offset:
159
160 22: 0.0 - 20.0
161 43: 0.0 - 59.0
162 51: -1.0
163 61: -1.0
164 79: .. - 20.0 ? speed over ground ? (++)
165 87: ? course ?
166 95: 0.0 - 3.1 (++)
167 103: -1
168
169 */
170 }
171
172 if (wpt->altitude == 0) {
173 wpt->altitude = unknown_alt;
174 }
175
176 if (tm.tm_year >= 1900) {
177 tm.tm_year -= 1900;
178 if (tm.tm_mon > 0) {
179 tm.tm_mon--;
180 wpt->SetCreationTime(mkgmtime(&tm), milliseconds);
181 }
182 }
183
184 track_add_wpt(trk, wpt);
185 }
186 xfree(buf);
187 }
188
189 static void
ggv_log_wr_init(const QString & fname)190 ggv_log_wr_init(const QString& fname)
191 {
192 fout = gbfopen(fname, "wb", MYNAME);
193
194 gbfputcstr("DOMGVGPS Logfile V1.0:", fout);
195 }
196
197 static void
ggv_log_wr_deinit()198 ggv_log_wr_deinit()
199 {
200 gbfclose(fout);
201 }
202
203 static void
ggv_log_track_head_cb(const route_head * trk)204 ggv_log_track_head_cb(const route_head* trk)
205 {
206 const Waypoint* prev = nullptr;
207
208 foreach (const Waypoint* wpt, trk->waypoint_list) {
209 double course = 0, speed = 0;
210 struct tm tm;
211 double secs = 0;
212
213 int latint = wpt->latitude;
214 int lonint = wpt->longitude;
215 double latmin = 60.0 * (fabs(wpt->latitude) - latint);
216 double lonmin = 60.0 * (fabs(wpt->longitude) - lonint);
217 double latsec = 60.0 * (latmin - floor(latmin));
218 double lonsec = 60.0 * (lonmin - floor(lonmin));
219
220 if (wpt->creation_time.isValid()) {
221 time_t t = wpt->GetCreationTime().toTime_t();
222 tm = *gmtime(&t);
223 tm.tm_mon += 1;
224 tm.tm_year += 1900;
225 } else {
226 memset(&tm, 0, sizeof(tm));
227 }
228
229 if (prev != nullptr) {
230 course = heading_true_degrees(
231 prev->latitude, prev->longitude,
232 wpt->latitude, wpt->longitude);
233 speed = waypt_speed(prev, wpt);
234 }
235 if (wpt->creation_time.isValid()) {
236 secs = (double)tm.tm_sec + wpt->GetCreationTime().time().msec() / 1000.0;
237 }
238
239 gbfputint16((int16_t) latint, fout);
240 gbfputint16((int16_t) latmin, fout);
241 gbfputflt(latsec, fout);
242 gbfputint16((int16_t) lonint, fout);
243 gbfputint16((int16_t) lonmin, fout);
244 gbfputflt(lonsec, fout);
245 gbfputint16((int16_t) course, fout);
246 gbfputint16((int16_t)(wpt->altitude != unknown_alt) ? wpt->altitude : 0, fout);
247 gbfputint16((int16_t) speed, fout);
248 gbfputint16(0, fout);
249 gbfputint16(tm.tm_year, fout);
250 gbfputint16(tm.tm_mon, fout);
251 gbfputint16(tm.tm_mday, fout);
252 gbfputint16(tm.tm_hour, fout);
253 gbfputint16(tm.tm_min, fout);
254 gbfputdbl(secs, fout);
255
256 prev = wpt;
257 }
258 }
259
260 static void
ggv_log_write()261 ggv_log_write()
262 {
263 track_disp_all(ggv_log_track_head_cb, nullptr, nullptr);
264 }
265
266 /**************************************************************************/
267
268 ff_vecs_t ggv_log_vecs = {
269 ff_type_file,
270 {
271 ff_cap_none, /* waypoints */
272 (ff_cap)(ff_cap_read | ff_cap_write), /* tracks */
273 ff_cap_none /* routes */
274 },
275 ggv_log_rd_init,
276 ggv_log_wr_init,
277 ggv_log_rd_deinit,
278 ggv_log_wr_deinit,
279 ggv_log_read,
280 ggv_log_write,
281 nullptr,
282 &ggv_log_args,
283 CET_CHARSET_ASCII, 1
284 , NULL_POS_OPS,
285 nullptr
286 };
287 /**************************************************************************/
288