You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gopal.cc 13KB

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