1 /*
2
3
4 Copyright (C) 2008 Dr. J�rgen Neumann, Juergen.Neumann@online.de
5 Copyright (C) 2005 Robert Lipe, robertlipe@usa.net (based on nmea.c)
6 Copyright (C) 20XX probably many others from the gpsbabel development team ;-)
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
21
22 =====================================================================================
23
24 This file allows gpsbabel to read and write the internal track log format used by
25 GoPal navigation systems. They produce a simple line-oriented format with one point per
26 second. Unfortunately the the data does not contain a valid date, only some kind of timetick,
27 together with each point (perhaps by mistake ??). So we have to parse the filename for a valid starting
28 date and add the timeoffset. Second problem (at least to me) was that irregularly stupid errors were
29 in the data, i.e. only one data point shows a totally wrong longitude or latitude. Everything else in
30 the dataset seems ok, so I needed a way to sort out these errors. My solution is to calculate the speed
31 between successive points and drop points not between minspeed and maxspeed. This way I can sort out most
32 of this annoying bugs, a side effect is that if a minimum speed > 0 is set points with the same coodinates are also
33 dropped.
34
35 Fileformat GoPal
36 TICK; TIME UTC; LONG; LAT; HEIGHT; SPEED km/h; FIX; HDOP; SAT
37 3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3
38 Filenames:
39 trackYYYYMMDD_HHMMSS.trk
40 A_YYYYMMDD_HHMMSS.trk
41 with HHMMSS local time (not UTC)
42
43 History
44 2008-07-18 initial release of Version 0.1
45 2008-07-26 bugfix: filenamehandling linux, format specification in write statement
46
47 ToDo:
48 - check for midnight & adjust
49 */
50
51 #include "defs.h"
52 #include <ctype.h>
53 #include "csv_util.h"
54 #include <time.h>
55 #include "strptime.h"
56 #include "jeeps/gpsmath.h"
57 #include "grtcirc.h"
58 #define MYNAME "gopal"
59
60 static gbfile* fin, *fout;
61
62 static struct tm tm,filenamedate, trackdate;
63 time_t tx;
64 char tmp[64];
65 char routename[64];
66 static char* optdate=NULL;
67 static char* optmaxspeed=NULL;
68 static char* optminspeed=NULL;
69 static char* optclean= NULL;
70 static double minspeed,maxspeed;
71 static struct tm opt_tm; /* converted "date" parameter */
72 static
73 arglist_t gopal_args[] = {
74 {"date", &optdate, "Complete date-free tracks with given date (YYYYMMDD).", NULL, ARGTYPE_INT, ARG_NOMINMAX },
75 {"maxspeed", &optmaxspeed, "The maximum speed (km/h) traveling from waypoint to waypoint.", "200", ARGTYPE_INT, "1", "1000" },
76 {"minspeed", &optminspeed, "The minimum speed (km/h) traveling from waypoint to waypoint. Set >0 to remove duplicate waypoints", "0", ARGTYPE_INT, "0", "999" },
77 {"clean", &optclean, "Cleanup common errors in trackdata", "1", ARGTYPE_BOOL, ARG_NOMINMAX },
78 ARG_TERMINATOR
79 };
80
81 #define CHECK_BOOL(a) if (a && (*a == '0')) a = NULL
82
gopal_check_line(char * line)83 int gopal_check_line(char* line)
84 {
85 char* c = line;
86 int i = 0;
87 while ((c = strchr(c, ','))) {
88 c++;
89 i++;
90 }
91 return i;
92 }
93
94
95
96 /*******************************************************************************
97 * %%% global callbacks called by gpsbabel main process %%% *
98 *******************************************************************************/
99
100 static void
gopal_rd_init(const char * fname)101 gopal_rd_init(const char* fname)
102 {
103 char buff[32];
104 char* ck;
105 char* filename;
106 CHECK_BOOL(optclean);
107 if (optminspeed) {
108 minspeed=atof(optminspeed);
109 if (global_opts.debug_level > 1) {
110 fprintf(stderr,"options from command line : gopal minspeed = %s\n",optminspeed);
111 }
112 } else {
113 minspeed=0;
114 }
115 if (optmaxspeed) {
116 maxspeed=atof(optmaxspeed);
117 if (global_opts.debug_level > 1) {
118 fprintf(stderr,"options from command line : gopal maxspeed = %s\n",optmaxspeed);
119 }
120 } else {
121 maxspeed=200;
122 }
123 if (global_opts.debug_level > 1) {
124 fprintf(stderr,"setting minspeed to %5.1lf km/h and maxspeed to %5.1lf km/h\n",minspeed,maxspeed);
125 }
126
127 fin = gbfopen(fname, "r", MYNAME);
128
129 memset(buff,0,sizeof(buff));
130 if (optdate) {
131 memset(&opt_tm, 0, sizeof(opt_tm));
132
133 ck = (char*)strptime(optdate, "%Y%m%d", &opt_tm);
134 if ((ck == NULL) || (*ck != '\0') || (strlen(optdate) != 8)) {
135 fatal(MYNAME ": Invalid date \"%s\"!\n", optdate);
136 } else if (opt_tm.tm_year < 70) {
137 fatal(MYNAME ": Date \"%s\" is out of range (have to be 19700101 or later)!\n", optdate);
138 }
139 tx = mkgmtime(&opt_tm);
140
141 } else {
142 /* remove path */
143 filename = get_filename(fname);
144
145 if ((strncmp(filename,"track",5)==0)&&(strlen(filename)>13)) { // we need at least 13 letters: trackYYYYMMDD...
146 strncpy(&buff[0],&filename[5],8);
147 } else if ((strncmp(filename,"A_",2)==0)&&(strlen(filename)>10)) { // here we expect at least 10 letters: A_YYYYMMDD...
148 strncpy(&buff[0],&filename[2],8);
149 }
150 // in buff we should now have something wich looks like a valid date starting with YYYYMMDD
151 ck = (char*)strptime(buff, "%Y%m%d", &filenamedate);
152 // if (((ck == NULL) || (*ck != '\0') )&&!(optdate))
153 // fatal(MYNAME ": Invalid date in filename \"%s\", try to set manually using \"date\" switch!\n", buff);
154 // /* else */ if (filenamedate.tm_year < 70)
155 // fatal(MYNAME ": Date \"%s\" is out of range (have to be 19700101 or later)!\n", buff);
156 // tx= mkgmtime(&filenamedate);
157 }
158 }
159
160 static void
gopal_rd_deinit(void)161 gopal_rd_deinit(void)
162 {
163 gbfclose(fin);
164 }
165
166 static void
gopal_read(void)167 gopal_read(void)
168 {
169
170 char* buff;
171 char* str, *c;
172 int column;
173 long line;
174 double hmsd,speed;
175 int fix, hms;
176 route_head* route;
177 waypoint* wpt, *lastwpt=NULL;
178 double long_old,lat_old;
179 char tbuffer[64];
180 struct tm tm2;
181 long_old=0;
182 lat_old=0;
183 strftime(routename,sizeof(routename),"Tracklog %c",gmtime(&tx));
184
185 route = route_head_alloc();
186 route->rte_name=xstrdup(routename);
187 route_add_head(route);
188
189 line=0;
190 while ((buff = gbfgetstr(fin))) {
191 int nfields;
192 unsigned long microsecs;
193 if ((line == 0) && fin->unicode) {
194 cet_convert_init(CET_CHARSET_UTF8, 1);
195 }
196
197 str = buff = lrtrim(buff);
198 if (*buff == '\0') {
199 continue;
200 }
201 nfields = gopal_check_line(buff);
202 if ((nfields != 8) && (nfields != 11)) {
203 continue;
204 }
205 // Old format. Hassle for date.
206 if ((nfields == 8) && (tx == 0)) {
207 // fatal(MYNAME ": Invalid date in filename \"%s\", try to set manually using \"date\" switch!\n", buff);
208 }
209 wpt = waypt_new();
210
211 column = -1;
212 // the format of gopal is quite simple. Unfortunately the developers forgot the date as the first element...
213 //TICK; TIME; LONG; LAT; HEIGHT; SPEED; Fix; HDOP; SAT
214 //3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3
215 c = csv_lineparse(str, ",", "", column++);
216 while (c != NULL) {
217 switch (column) {
218 case 0: /* "-" */ /* unknown fields for the moment */
219 sscanf(c, "%lu", µsecs);
220 wpt->microseconds += microsecs % 1000000;
221 wpt->creation_time += microsecs / 1000000;
222 break;
223 case 1: /* Time UTC */
224 sscanf(c,"%lf",&hmsd);
225 hms = (int) hmsd;
226 tm.tm_sec = hms % 100;
227 hms = hms / 100;
228 tm.tm_min = hms % 100;
229 hms = hms / 100;
230 tm.tm_hour = hms % 100;
231 tm.tm_year=trackdate.tm_year;
232 tm.tm_mon=trackdate.tm_mon;
233 tm.tm_mday=trackdate.tm_mday;
234 wpt->creation_time = tx+((((time_t)tm.tm_hour * 60) + tm.tm_min) * 60) + tm.tm_sec;
235 if (global_opts.debug_level > 1) {
236 strftime(tbuffer, sizeof(tbuffer), "%c", gmtime(&wpt->creation_time));
237 printf("parsed timestamp: %s\n",tbuffer);
238 }
239 break;
240
241 case 2: /* longitude */
242 sscanf(c, "%lf", &wpt->longitude);
243 break;
244
245 case 3: /* latitude */
246 sscanf(c, "%lf", &wpt->latitude);
247 break;
248 case 4: /* altitude */
249 sscanf(c, "%lf", &wpt->altitude);
250 break;
251 case 5: /* speed */
252 //sscanf(c, "%lf", &wpt->speed);
253 wpt->speed=atof(c);
254 if (global_opts.debug_level > 1) {
255 printf("parsed speed: %8.5f\n",wpt->speed);
256 }
257 break;
258 case 6: /* type of fix */
259 sscanf(c, "%d", &fix);
260 //my device shows only 0 or 2
261 //should i guess from no of sats if 2d or 3d?
262 switch (fix) {
263 case 0:
264 wpt->fix = fix_none;
265 break;
266 case 2:
267 wpt->fix = fix_2d;
268 break;
269 //case 3: wpt->fix = fix_3d;break;
270 //case 4: wpt->fix = fix_dgps;break; /* 2D_diff */
271 //case 5: wpt->fix = fix_dgps;break; /* 3D_diff */
272 default:
273 wpt->fix = fix_unknown;
274 break;
275 }
276 break;
277 case 7: /* hdop */
278 wpt->hdop = atof(c);
279 //sscanf(c, "%lf", &wpt->hdop); does not work ???
280 //wpt->vdop=0;wpt->hdop=0;
281 break;
282 case 8: /* number of sats */
283 sscanf(c, "%d", &wpt->sat);
284 break;
285 // Somewhere around mid/late 2009, these files started
286 // seeing 11 fields.
287 case 9:
288 memset(&tm2, 0, sizeof(tm2));
289 if (!strptime(c, "%Y%m%d", &tm2)) {
290 fatal("Bad date '%s'.\n", c);
291 }
292 wpt->creation_time += mkgmtime(&tm2);
293 break;
294 case 10: // Unknown. Ignored.
295 case 11: // Bearing. Ignored.
296 break;
297 }
298 c = csv_lineparse(NULL, ",", "", column++);
299 }
300 line++;
301
302 if ((wpt->fix != fix_none)&&(lat_old==0)) { //first-time init
303 lat_old=wpt->latitude;
304 long_old=wpt->longitude;
305 //route_add_wpt(route, wpt);
306 lastwpt=wpt;
307 }
308 //calculate the speed to reach this waypoint from the last. This way I try to sort out invalid waypoints
309 speed=0;
310 if (lastwpt !=NULL) {
311 speed=3.6*radtometers(gcdist(RAD(lastwpt->latitude), RAD(lastwpt->longitude), RAD(wpt->latitude), RAD(wpt->longitude))) / abs(wpt->creation_time - lastwpt->creation_time);
312 //printf("speed line %d %lf \n",line,speed);
313 }
314 /* Error handling: in the tracklog of my device sometimes "jump" waypoints ;-) */
315 if ((optclean) &&
316 (((wpt->longitude==0.0)|| (wpt->latitude==0.0)||(abs(wpt->latitude)>90)||(abs(wpt->longitude)>180))||
317 ((speed>maxspeed)||(speed<minspeed)))
318 ) {
319 if (global_opts.debug_level > 1) {
320 fprintf(stderr,"Problem in or around line %5lu: \"%s\" %lf km/h\n",line,buff,speed);
321 }
322 } else {
323 if (global_opts.debug_level > 1) {
324 fprintf(stderr,"valid line %5lu: \"%s\" %lf km/h\n",line,buff,speed);
325 }
326 lastwpt=wpt;
327 long_old=wpt->longitude;
328 lat_old=wpt->latitude;
329 route_add_wpt(route,wpt);
330 waypt_add(waypt_dupe(wpt));
331 }
332 }
333 }
334
335 static void
gopal_route_hdr(const route_head * route)336 gopal_route_hdr(const route_head* route)
337 {
338
339 }
340
341 static void
gopal_route_tlr(const route_head * rte)342 gopal_route_tlr(const route_head* rte)
343 {
344 }
345
346 static void
gopal_write_waypt(const waypoint * wpt)347 gopal_write_waypt(const waypoint* wpt)
348 {
349 char tbuffer[64];
350 unsigned long timestamp;
351 int fix=fix_unknown;
352 //TICK; TIME; LONG; LAT; HEIGHT; SPEED; UN; HDOP; SAT
353 //3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3
354 strftime(tbuffer, sizeof(tbuffer), "%H%M%S", gmtime(&wpt->creation_time));
355 if (wpt->fix!=fix_unknown) {
356 switch (wpt->fix) {
357 case fix_none:
358 fix = 0;
359 break;
360 case fix_2d:
361 fix = 2;
362 break;
363 default:
364 fix = 0;
365 break;
366 }
367 }
368 //MSVC handles time_t as int64, gcc and mac only int32, so convert it:
369 timestamp=(unsigned long)wpt->creation_time;
370 gbfprintf(fout, "%lu, %s, %lf, %lf, %5.1lf, %8.5lf, %d, %lf, %d\n",timestamp,tbuffer, wpt->longitude, wpt->latitude,wpt->altitude,
371 wpt->speed,fix,wpt->hdop,wpt->sat);
372 }
373
374
375 static void
gopal_wr_init(const char * fname)376 gopal_wr_init(const char* fname)
377 {
378 fout = gbfopen(fname, "w", MYNAME);
379 }
380
381 static void
gopal_wr_deinit(void)382 gopal_wr_deinit(void)
383 {
384 gbfclose(fout);
385 }
386
387 static void
gopal_write(void)388 gopal_write(void)
389 {
390 route_disp_all(gopal_route_hdr, gopal_route_tlr, gopal_write_waypt);
391 }
392
393 static void
gopal_exit(void)394 gopal_exit(void) /* optional */
395 {
396 }
397
398 /**************************************************************************/
399
400 // capabilities below means: we can only read and write waypoints
401 //
402
403 ff_vecs_t gopal_vecs = {
404 ff_type_file,
405 {
406 ff_cap_none /* waypoints */,
407 (ff_cap)(ff_cap_read | ff_cap_write) /* tracks */,
408 ff_cap_none /* routes */
409 },
410 gopal_rd_init,
411 gopal_wr_init,
412 gopal_rd_deinit,
413 gopal_wr_deinit,
414 gopal_read,
415 gopal_write,
416 gopal_exit,
417 gopal_args,
418 CET_CHARSET_ASCII, 0 /* ascii is the expected character set */
419 /* not fixed, can be changed through command line parameter */
420 };
421 /**************************************************************************/
422