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