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.

1326 lines
33KB

  1. /*
  2. Jeeps wrapper for Garmin serial protocol.
  3. Copyright (C) 2002, 2003, 2004, 2005, 2006 Robert Lipe, robertlipe+source@gpsbabel.org
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  15. */
  16. #include <ctype.h>
  17. #include <limits.h>
  18. #include <stdlib.h>
  19. #include <math.h>
  20. #include "defs.h"
  21. #include "cet_util.h"
  22. #include "grtcirc.h"
  23. #include "jeeps/gps.h"
  24. #include "jeeps/gpsserial.h"
  25. #include "garmin_tables.h"
  26. #include "garmin_fs.h"
  27. #include "garmin_device_xml.h"
  28. #define MYNAME "GARMIN"
  29. static const char* portname;
  30. static short_handle mkshort_handle;
  31. static GPS_PWay* tx_waylist;
  32. static GPS_PWay* tx_routelist;
  33. static GPS_PWay* cur_tx_routelist_entry;
  34. static GPS_PTrack* tx_tracklist;
  35. static GPS_PTrack* cur_tx_tracklist_entry;
  36. static int my_track_count = 0;
  37. static char* getposn = NULL;
  38. static char* poweroff = NULL;
  39. static char* eraset = NULL;
  40. static char* resettime = NULL;
  41. static char* snlen = NULL;
  42. static char* snwhiteopt = NULL;
  43. static char* deficon = NULL;
  44. static char* category = NULL;
  45. static char* categorybitsopt = NULL;
  46. static char* baudopt = NULL;
  47. static int baud = 0;
  48. static int categorybits;
  49. static int receiver_must_upper = 1;
  50. static ff_vecs_t* gpx_vec;
  51. #define MILITANT_VALID_WAYPT_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  52. /* Technically, even this is a little loose as spaces arent allowed */
  53. static const char* valid_waypt_chars = MILITANT_VALID_WAYPT_CHARS " ";
  54. static
  55. arglist_t garmin_args[] = {
  56. {
  57. "snlen", &snlen, "Length of generated shortnames", NULL,
  58. ARGTYPE_INT, "1", NULL
  59. },
  60. {
  61. "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames",
  62. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  63. },
  64. { "deficon", &deficon, "Default icon name", NULL, ARGTYPE_STRING, ARG_NOMINMAX },
  65. {
  66. "get_posn", &getposn, "Return current position as a waypoint",
  67. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  68. },
  69. {
  70. "power_off", &poweroff, "Command unit to power itself down",
  71. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  72. },
  73. {
  74. "erase_t", &eraset, "Erase existing courses when writing new ones",
  75. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  76. },
  77. {
  78. "resettime", &resettime, "Sync GPS time to computer time",
  79. NULL, ARGTYPE_BOOL, ARG_NOMINMAX
  80. },
  81. {
  82. "category", &category, "Category number to use for written waypoints",
  83. NULL, ARGTYPE_INT, "1", "16"
  84. },
  85. {
  86. "bitscategory", &categorybitsopt, "Bitmap of categories",
  87. NULL, ARGTYPE_INT, "1", "65535"
  88. },
  89. {
  90. "baud", &baudopt, "Speed in bits per second of serial port (baud=9600)",
  91. NULL, ARGTYPE_INT, ARG_NOMINMAX },
  92. ARG_TERMINATOR
  93. };
  94. static const char* d103_symbol_from_icon_number(unsigned int n);
  95. static int d103_icon_number_from_symbol(const QString& s);
  96. static void
  97. rw_init(const QString& fname)
  98. {
  99. int receiver_short_length;
  100. int receiver_must_upper = 1;
  101. const char* receiver_charset = NULL;
  102. if (!mkshort_handle) {
  103. mkshort_handle = mkshort_new_handle();
  104. }
  105. if (global_opts.debug_level > 0) {
  106. GPS_Enable_Warning();
  107. GPS_Enable_User();
  108. }
  109. if (global_opts.debug_level > 1) {
  110. GPS_Enable_Diagnose();
  111. }
  112. GPS_Enable_Error();
  113. if (poweroff) {
  114. GPS_Command_Off(qPrintable(fname));
  115. return;
  116. }
  117. /*
  118. * THis is Gross. The B&W Vista sometimes sets its time decades into
  119. * the future with no way to reset it. This apparently can "cure"
  120. * an affected unit.
  121. */
  122. if (resettime) {
  123. GPS_Command_Send_Time(qPrintable(fname), current_time().toTime_t());
  124. return;
  125. }
  126. if (categorybitsopt) {
  127. categorybits = strtol(categorybitsopt, NULL, 0);
  128. }
  129. if (baudopt) {
  130. baud = strtol(baudopt, NULL, 0);
  131. switch (baud) {
  132. case 9600:
  133. case 19200:
  134. case 38400:
  135. case 57600:
  136. case 115200:
  137. break;
  138. default:
  139. fatal("Baud rate %d is not supported\n", baud);
  140. }
  141. }
  142. if (GPS_Init(qPrintable(fname)) < 0) {
  143. fatal(MYNAME ":Can't init %s\n", qPrintable(fname));
  144. }
  145. portname = xstrdup(qPrintable(fname));
  146. if (baud && baud != DEFAULT_BAUD) {
  147. if (0 == GPS_Set_Baud_Rate(portname, baud)) {
  148. gps_baud_rate = baud;
  149. }
  150. }
  151. /*
  152. * Grope the unit we're talking to to set setshort_length to
  153. * 20 for the V,
  154. * 10 for Street Pilot, (old) Rhino, 76
  155. * 6 for the III, 12, emap, and etrex
  156. * Fortunately, getting this "wrong" only results in ugly names
  157. * when we're using the synthesize_shortname path.
  158. */
  159. receiver_short_length = 10;
  160. switch (gps_waypt_type) { /* waypoint type as defined by jeeps */
  161. case 0:
  162. fatal("Garmin unit %d does not support waypoint xfer.",
  163. gps_save_id);
  164. break;
  165. case 100: /* The GARMIN GPS Interface Specification, */
  166. case 101: /* says these waypoint types use an ident */
  167. case 102: /* length of 6. Waypoint types 106, 108 */
  168. case 103: /* and 109 are all variable length */
  169. case 104:
  170. case 105:
  171. case 107:
  172. case 150:
  173. case 151:
  174. case 152:
  175. case 154:
  176. case 155:
  177. receiver_short_length = 6;
  178. break;
  179. case 106: /* Waypoint types with variable ident length */
  180. case 108: /* Need GPSr id to know the actual length */
  181. case 109:
  182. case 110:
  183. switch (gps_save_id) {
  184. case 130: /* Garmin Etrex (yellow) */
  185. receiver_short_length = 6;
  186. break;
  187. case 295: /* eTrex (yellow, fw v. 3.30) */
  188. case 696: /* eTrex HC */
  189. case 574: /* Geko 201 */
  190. receiver_short_length = 6;
  191. valid_waypt_chars =
  192. MILITANT_VALID_WAYPT_CHARS " +-";
  193. setshort_badchars(mkshort_handle, "\"$.,'!");
  194. break;
  195. case 155: /* Garmin V */
  196. case 404: /* SP2720 */
  197. case 520: /* SP2820 */
  198. receiver_short_length = 20;
  199. break;
  200. case 382: /* C320 */
  201. receiver_short_length = 30;
  202. receiver_must_upper = 0;
  203. break;
  204. case 292: /* (60|76)C[S]x series */
  205. case 421: /* Vista|Legend Cx */
  206. case 694: /* Legend HCx */
  207. case 695: /* Vista HC */
  208. case 786: /* HC model */
  209. case 957: /* Legend HC */
  210. receiver_short_length = 14;
  211. snwhiteopt = xstrdup("1");
  212. receiver_must_upper = 0;
  213. /* This might be 8859-1 */
  214. receiver_charset = CET_CHARSET_MS_ANSI;
  215. break;
  216. case 291: /* GPSMAP 60CS, probably others */
  217. case 1095: /* GPS 72H */
  218. receiver_short_length = 10;
  219. valid_waypt_chars = MILITANT_VALID_WAYPT_CHARS " +-";
  220. setshort_badchars(mkshort_handle, "\"$.,'!");
  221. break;
  222. case 231: /* Quest */
  223. case 463: /* Quest 2 */
  224. receiver_must_upper = 0;
  225. receiver_short_length = 30;
  226. receiver_charset = CET_CHARSET_MS_ANSI;
  227. break;
  228. case 577: // Rino 530HCx Version 2.50
  229. receiver_must_upper = 0;
  230. receiver_short_length = 14;
  231. break;
  232. case 429: // Streetpilot i3
  233. receiver_must_upper = 0;
  234. receiver_charset = CET_CHARSET_MS_ANSI;
  235. receiver_short_length = 30;
  236. break;
  237. case 484: // Forerunner 305
  238. receiver_short_length = 8;
  239. break;
  240. case 260: /* GPSMap 296 */
  241. default:
  242. break;
  243. }
  244. break;
  245. default:
  246. break;
  247. }
  248. // If a user has specified a non-default character set, we'll trust
  249. // them to sort our the wreckage of violating the Garmin protocol and
  250. // ship characters to the device in that character set.
  251. if (global_opts.charset != &cet_cs_vec_utf8) {
  252. receiver_charset = xstrdup(global_opts.charset_name);
  253. }
  254. if (global_opts.debug_level > 0) {
  255. fprintf(stderr, "Waypoint type: %d\n"
  256. "Chosen waypoint length %d\n",
  257. gps_waypt_type, receiver_short_length);
  258. if (gps_category_type) {
  259. fprintf(stderr, "Waypoint category type: %d\n",
  260. gps_category_type);
  261. }
  262. }
  263. // Allow override of sent character set for internationalized GPSes.
  264. if (global_opts.charset != &cet_cs_vec_utf8) {
  265. receiver_charset = xstrdup(global_opts.charset_name);
  266. }
  267. /*
  268. * If the user provided a short_length, override the calculated value.
  269. */
  270. if (snlen) {
  271. setshort_length(mkshort_handle, atoi(snlen));
  272. } else {
  273. setshort_length(mkshort_handle, receiver_short_length);
  274. }
  275. if (snwhiteopt) {
  276. setshort_whitespace_ok(mkshort_handle, atoi(snwhiteopt));
  277. }
  278. /*
  279. * Until Garmins documents how to determine valid character space
  280. * for the new models, we just release this safety check manually.
  281. */
  282. if (receiver_must_upper) {
  283. setshort_goodchars(mkshort_handle, valid_waypt_chars);
  284. } else {
  285. setshort_badchars(mkshort_handle, "");
  286. }
  287. setshort_mustupper(mkshort_handle, receiver_must_upper);
  288. if (receiver_charset) {
  289. cet_convert_init(receiver_charset, 1);
  290. }
  291. }
  292. static void
  293. rd_init(const QString& fname)
  294. {
  295. if (setjmp(gdx_jmp_buf)) {
  296. const char* vec_opts = NULL;
  297. const gdx_info* gi = gdx_get_info();
  298. gpx_vec = find_vec("gpx", &vec_opts);
  299. gpx_vec->rd_init(gi->from_device.canon);
  300. } else {
  301. gpx_vec = NULL;
  302. rw_init(fname);
  303. }
  304. }
  305. static void
  306. rw_deinit(void)
  307. {
  308. if (gps_baud_rate != DEFAULT_BAUD) {
  309. if (0 == GPS_Set_Baud_Rate(portname, DEFAULT_BAUD)) {
  310. gps_baud_rate = baud;
  311. }
  312. }
  313. if (mkshort_handle) {
  314. mkshort_del_handle(&mkshort_handle);
  315. }
  316. xfree(portname);
  317. portname = NULL;
  318. }
  319. static int
  320. waypt_read_cb(int total_ct, GPS_PWay* )
  321. {
  322. static int i;
  323. if (global_opts.verbose_status) {
  324. i++;
  325. waypt_status_disp(total_ct, i);
  326. }
  327. return 0;
  328. }
  329. static void
  330. waypt_read(void)
  331. {
  332. int i,n;
  333. GPS_PWay* way = NULL;
  334. if (getposn) {
  335. Waypoint* wpt = new Waypoint;
  336. wpt->latitude = gps_save_lat;
  337. wpt->longitude = gps_save_lon;
  338. wpt->shortname = "Position";
  339. if (gps_save_time) {
  340. wpt->SetCreationTime(gps_save_time);
  341. }
  342. waypt_add(wpt);
  343. return;
  344. }
  345. if ((n = GPS_Command_Get_Waypoint(portname, &way, waypt_read_cb)) < 0) {
  346. fatal(MYNAME ":Can't get waypoint from %s\n", portname);
  347. }
  348. for (i = 0; i < n; i++) {
  349. Waypoint* wpt_tmp = new Waypoint;
  350. wpt_tmp->shortname = way[i]->ident;
  351. wpt_tmp->description = QString(way[i]->cmnt).simplified();
  352. wpt_tmp->shortname = wpt_tmp->shortname.simplified();
  353. wpt_tmp->description = wpt_tmp->description.simplified();
  354. wpt_tmp->longitude = way[i]->lon;
  355. wpt_tmp->latitude = way[i]->lat;
  356. if (gps_waypt_type == 103) {
  357. wpt_tmp->icon_descr = d103_symbol_from_icon_number(
  358. way[i]->smbl);
  359. } else {
  360. wpt_tmp->icon_descr = gt_find_desc_from_icon_number(
  361. way[i]->smbl, PCX);
  362. }
  363. /*
  364. * If a unit doesn't store altitude info (i.e. a D103)
  365. * gpsmem will default the alt to INT_MAX. Other units
  366. * (I can't recall if it was the V (D109) hor the Vista (D108)
  367. * return INT_MAX+1, contrary to the Garmin protocol doc which
  368. * says they should report 1.0e25. So we'll try to trap
  369. * all the cases here. Yes, libjeeps should probably
  370. * do this and not us...
  371. */
  372. if ((way[i]->alt == (float)(1U<<31)) ||
  373. (way[i]->alt == INT_MAX) ||
  374. (way[i]->alt >= (float) 1.0e20)
  375. ) {
  376. wpt_tmp->altitude = unknown_alt;
  377. } else {
  378. wpt_tmp->altitude = way[i]->alt;
  379. }
  380. if (way[i]->time_populated) {
  381. wpt_tmp->SetCreationTime(way[i]->time);
  382. }
  383. garmin_fs_garmin_after_read(way[i], wpt_tmp, gps_waypt_type);
  384. waypt_add(wpt_tmp);
  385. GPS_Way_Del(&way[i]);
  386. }
  387. if (way) {
  388. xfree(way);
  389. }
  390. }
  391. static int lap_read_nop_cb(int, struct GPS_SWay**)
  392. {
  393. return 0;
  394. }
  395. // returns 1 if the waypoint's start_time can be found
  396. // in the laps array, 0 otherwise
  397. unsigned int checkWayPointIsAtSplit(Waypoint* wpt, GPS_PLap* laps, int nlaps)
  398. {
  399. int result = 0;
  400. if ((laps != NULL) && (nlaps > 0)) {
  401. int i;
  402. for (i=(nlaps-1); i >= 0; i--) {
  403. GPS_PLap lap = laps[i];
  404. time_t delta = lap->start_time - wpt->GetCreationTime().toTime_t();
  405. if ((delta >= -1) && (delta <= 1)) {
  406. result = 1;
  407. break;
  408. // as an optimization this will stop going through
  409. // the lap array when the negative delta gets too
  410. // big. It assumes that laps is sorted by time in
  411. // ascending order (which appears to be the case for
  412. // Forerunners. Don't know about other devices.
  413. } else if (delta < -1) {
  414. break;
  415. }
  416. }
  417. }
  418. return result;
  419. }
  420. static
  421. void
  422. track_read(void)
  423. {
  424. int32 ntracks;
  425. GPS_PTrack* array;
  426. route_head* trk_head = NULL;
  427. int trk_num = 0;
  428. int i;
  429. const char* trk_name = "";
  430. GPS_PLap* laps = NULL;
  431. int nlaps = 0;
  432. int next_is_new_trkseg = 0;
  433. if (gps_lap_type != -1) {
  434. nlaps = GPS_Command_Get_Lap(portname, &laps, &lap_read_nop_cb);
  435. }
  436. ntracks = GPS_Command_Get_Track(portname, &array, waypt_read_cb);
  437. if (ntracks <= 0) {
  438. return;
  439. }
  440. for (i = 0; i < ntracks; i++) {
  441. Waypoint* wpt;
  442. /*
  443. * This is probably always in slot zero, but the Garmin
  444. * serial spec says these can appear anywhere. Toss them
  445. * out so we don't treat it as an extraneous trackpoint.
  446. */
  447. if (array[i]->ishdr) {
  448. trk_name = array[i]->trk_ident;
  449. if (!trk_name) {
  450. trk_name = "";
  451. }
  452. }
  453. if (trk_head == NULL || array[i]->ishdr) {
  454. trk_head = route_head_alloc();
  455. trk_head->rte_num = trk_num;
  456. trk_head->rte_name = trk_name;
  457. trk_num++;
  458. track_add_head(trk_head);
  459. }
  460. /* Need to do this here because fitness devices set tnew
  461. * on a trackpoint without lat/lon.
  462. */
  463. if (array[i]->tnew) {
  464. next_is_new_trkseg = 1;
  465. }
  466. if (array[i]->no_latlon || array[i]->ishdr) {
  467. continue;
  468. }
  469. wpt = new Waypoint;
  470. wpt->longitude = array[i]->lon;
  471. wpt->latitude = array[i]->lat;
  472. wpt->altitude = array[i]->alt;
  473. wpt->heartrate = array[i]->heartrate;
  474. wpt->cadence = array[i]->cadence;
  475. wpt->shortname = array[i]->trk_ident;
  476. wpt->SetCreationTime(array[i]->Time);
  477. wpt->wpt_flags.is_split = checkWayPointIsAtSplit(wpt, laps,
  478. nlaps);
  479. wpt->wpt_flags.new_trkseg = next_is_new_trkseg;
  480. next_is_new_trkseg = 0;
  481. if (array[i]->dpth < 1.0e25f) {
  482. WAYPT_SET(wpt, depth, array[i]->dpth);
  483. }
  484. if (array[i]->temperature_populated) {
  485. WAYPT_SET(wpt, temperature, array[i]->temperature);
  486. }
  487. track_add_wpt(trk_head, wpt);
  488. }
  489. while (ntracks) {
  490. GPS_Track_Del(&array[--ntracks]);
  491. }
  492. xfree(array);
  493. }
  494. static
  495. void
  496. route_read(void)
  497. {
  498. int32 nroutepts;
  499. int i;
  500. GPS_PWay* array;
  501. /* TODO: Fixes warning but is it right?
  502. * RJL: No, the warning isn't right; GCC's flow analysis is broken.
  503. * still, it's good taste...
  504. */
  505. route_head* rte_head = NULL;
  506. nroutepts = GPS_Command_Get_Route(portname, &array);
  507. // fprintf(stderr, "Routes %d\n", (int) nroutepts);
  508. #if 1
  509. for (i = 0; i < nroutepts; i++) {
  510. if (array[i]->isrte) {
  511. char* csrc = NULL;
  512. /* What a horrible API has libjeeps for making this
  513. * my problem.
  514. */
  515. switch (array[i]->rte_prot) {
  516. case 201:
  517. csrc = array[i]->rte_cmnt;
  518. break;
  519. case 202:
  520. csrc = array[i]->rte_ident;
  521. break;
  522. default:
  523. break;
  524. }
  525. rte_head = route_head_alloc();
  526. route_add_head(rte_head);
  527. if (csrc) {
  528. rte_head->rte_name = csrc;
  529. }
  530. } else {
  531. if (array[i]->islink) {
  532. continue;
  533. } else {
  534. Waypoint* wpt_tmp = new Waypoint;
  535. wpt_tmp->latitude = array[i]->lat;
  536. wpt_tmp->longitude = array[i]->lon;
  537. wpt_tmp->shortname = array[i]->ident;
  538. route_add_wpt(rte_head, wpt_tmp);
  539. }
  540. }
  541. }
  542. #else
  543. GPS_Fmt_Print_Route(array, nroutepts, stderr);
  544. #endif
  545. }
  546. #if 0
  547. static
  548. void
  549. lap_read_as_track(void)
  550. {
  551. int32 ntracks;
  552. GPS_PLap* array;
  553. route_head* trk_head = NULL;
  554. int trk_num = 0;
  555. int index;
  556. int i;
  557. char tbuf[128];
  558. ntracks = GPS_Command_Get_Lap(portname, &array, waypt_read_cb);
  559. if (ntracks <= 0) {
  560. return;
  561. }
  562. for (i = 0; i < ntracks; i++) {
  563. Waypoint* wpt;
  564. if (array[i]->index == -1) {
  565. index=i;
  566. } else {
  567. index=array[i]->index;
  568. index=i;
  569. }
  570. if ((trk_head == NULL) || (i == 0) ||
  571. /* D906 - last track:index is the track index */
  572. (array[i]->index == -1 && array[i]->track_index != 255) ||
  573. /* D10xx - no real separator, use begin/end time to guess */
  574. (abs(array[i-1]->start_time + array[i]->total_time/100-array[i]->start_time) > 2)
  575. ) {
  576. static struct tm* stmp;
  577. stmp = gmtime(&array[i]->start_time);
  578. trk_head = route_head_alloc();
  579. /*For D906, we would like to use the track_index in the last packet instead...*/
  580. trk_head->rte_num = ++trk_num;
  581. strftime(tbuf, 32, "%Y-%m-%dT%H:%M:%SZ", stmp);
  582. trk_head->rte_name = tbuf;
  583. track_add_head(trk_head);
  584. wpt = new Waypoint;
  585. wpt->longitude = array[i]->begin_lon;
  586. wpt->latitude = array[i]->begin_lat;
  587. wpt->heartrate = array[i]->avg_heart_rate;
  588. wpt->cadence = array[i]->avg_cadence;
  589. wpt->speed = array[i]->max_speed;
  590. wpt->creation_time = array[i]->start_time;
  591. wpt->microseconds = 0;
  592. sprintf(tbuf, "#%d-0", index);
  593. wpt->shortname = tbuf;
  594. sprintf(tbuf, "D:%f Cal:%d MS:%f AH:%d MH:%d AC:%d I:%d T:%d",
  595. array[i]->total_distance, array[i]->calories, array[i]->max_speed, array[i]->avg_heart_rate,
  596. array[i]->max_heart_rate, array[i]->avg_cadence, array[i]->intensity, array[i]->trigger_method);
  597. wpt->description = tbuf;
  598. track_add_wpt(trk_head, wpt);
  599. }
  600. /*Allow even if no correct location, no skip if invalid */
  601. /* if (array[i]->no_latlon) {
  602. * continue;
  603. * }
  604. */
  605. wpt = new Waypoint;
  606. wpt->longitude = array[i]->end_lon;
  607. wpt->latitude = array[i]->end_lat;
  608. wpt->heartrate = array[i]->avg_heart_rate;
  609. wpt->cadence = array[i]->avg_cadence;
  610. wpt->speed = array[i]->max_speed;
  611. wpt->creation_time = array[i]->start_time + array[i]->total_time/100;
  612. wpt->microseconds = 10000*(array[i]->total_time % 100);
  613. /*Add fields with no mapping in the description */
  614. sprintf(tbuf, "#%d", index);
  615. wpt->shortname = tbuf;
  616. sprintf(tbuf, "D:%f Cal:%d MS:%f AH:%d MH:%d AC:%d I:%d T:%d (%f,%f)",
  617. array[i]->total_distance, array[i]->calories, array[i]->max_speed, array[i]->avg_heart_rate,
  618. array[i]->max_heart_rate, array[i]->avg_cadence, array[i]->intensity, array[i]->trigger_method,
  619. array[i]->begin_lon, array[i]->begin_lat);
  620. wpt->description = tbuf;
  621. track_add_wpt(trk_head, wpt);
  622. }
  623. while (ntracks) {
  624. GPS_Lap_Del(&array[--ntracks]);
  625. }
  626. xfree(array);
  627. }
  628. #endif
  629. /*
  630. * Rather than propogate Garmin-specific data types outside of the Garmin
  631. * code, we convert the PVT (position/velocity/time) data from the receiver
  632. * to the data type we use throughout. Yes, we do lose some data that way.
  633. */
  634. static void
  635. pvt2wpt(GPS_PPvt_Data pvt, Waypoint* wpt)
  636. {
  637. double wptime, wptimes;
  638. wpt->altitude = pvt->alt;
  639. wpt->latitude = pvt->lat;
  640. wpt->longitude = pvt->lon;
  641. WAYPT_SET(wpt,course,1);
  642. WAYPT_SET(wpt,speed,1);
  643. wpt->course = 180 + DEG(atan2(-pvt->east, -pvt->north));
  644. /* velocity in m/s */
  645. WAYPT_SET(wpt,speed, sqrt(pvt->north*pvt->north + pvt->east*pvt->east));
  646. // wpt->vs = pvt->up;
  647. /*
  648. * The unit reports time in three fields:
  649. * 1) The # of days to most recent Sun. since 1989-12-31 midnight UTC.
  650. * 2) The number of seconds (fractions allowed) since that Sunday.
  651. * 3) The number of leap seconds that offset the current UTC and GPS
  652. * reference clocks.
  653. */
  654. wptime = 631065600.0 + pvt->wn_days * 86400.0 +
  655. pvt->tow
  656. - pvt->leap_scnds;
  657. wptimes = floor(wptime);
  658. wpt->SetCreationTime(wptimes, 1000000.0 * (wptime - wptimes));
  659. /*
  660. * The Garmin spec fifteen different models that use a different
  661. * table for 'fix' without a really good way to tell if the model
  662. * we're talking to happens to be one of those...By inspection,
  663. * it looks like even though the models (Summit, Legend, etc.) may
  664. * be popular, it's older (2001 and earlier or so) versions that
  665. * are affected and I think there are relatively few readers of
  666. * the fix field anyway. Time will tell if this is a good plan.
  667. */
  668. switch (pvt->fix) {
  669. case 0:
  670. wpt->fix = fix_unknown;
  671. break;
  672. case 1:
  673. wpt->fix = fix_none;
  674. break;
  675. case 2:
  676. wpt->fix = fix_2d;
  677. break;
  678. case 3:
  679. wpt->fix = fix_3d;
  680. break;
  681. case 4:
  682. wpt->fix = fix_dgps;
  683. break; /* 2D_diff */
  684. case 5:
  685. wpt->fix = fix_dgps;
  686. break; /* 3D_diff */
  687. default:
  688. /* undocumented type. */
  689. break;
  690. }
  691. }
  692. static gpsdevh* pvt_fd;
  693. static void
  694. pvt_init(const QString& fname)
  695. {
  696. rw_init(fname);
  697. GPS_Command_Pvt_On(qPrintable(fname), &pvt_fd);
  698. }
  699. static Waypoint*
  700. pvt_read(posn_status* posn_status)
  701. {
  702. Waypoint* wpt = new Waypoint;
  703. GPS_PPvt_Data pvt = GPS_Pvt_New();
  704. if (GPS_Command_Pvt_Get(&pvt_fd, &pvt)) {
  705. pvt2wpt(pvt, wpt);
  706. GPS_Pvt_Del(&pvt);
  707. wpt->shortname = "Position";
  708. if (gps_errno && posn_status) {
  709. posn_status->request_terminate = 1;
  710. }
  711. return wpt;
  712. }
  713. /*
  714. * If the caller has not given us a better way to return the
  715. * error, do it now.
  716. */
  717. if (gps_errno) {
  718. fatal(MYNAME ": Fatal error reading position.\n");
  719. }
  720. delete wpt;
  721. GPS_Pvt_Del(&pvt);
  722. return NULL;
  723. }
  724. static void
  725. data_read(void)
  726. {
  727. if (gpx_vec) {
  728. gpx_vec->read();
  729. return;
  730. }
  731. if (poweroff) {
  732. return;
  733. }
  734. if (global_opts.masked_objective & WPTDATAMASK) {
  735. waypt_read();
  736. }
  737. if (global_opts.masked_objective & TRKDATAMASK) {
  738. track_read();
  739. }
  740. if (global_opts.masked_objective & RTEDATAMASK) {
  741. route_read();
  742. }
  743. if (!(global_opts.masked_objective &
  744. (WPTDATAMASK | TRKDATAMASK | RTEDATAMASK | POSNDATAMASK))) {
  745. fatal(MYNAME ": Nothing to do.\n");
  746. }
  747. }
  748. static GPS_PWay
  749. sane_GPS_Way_New(void)
  750. {
  751. GPS_PWay way;
  752. way = GPS_Way_New();
  753. if (!way) {
  754. fatal(MYNAME ":not enough memory\n");
  755. }
  756. /*
  757. * Undo less than helpful defaults from Way_New.
  758. */
  759. way->rte_ident[0] = 0;
  760. way->rte_cmnt[0] = 0;
  761. way->rte_link_subclass[0] = 0;
  762. way->rte_link_ident[0] = 0;
  763. way->city[0] = 0;
  764. way->state[0] = 0;
  765. way->facility[0] = 0;
  766. way->addr[0] = 0;
  767. way->cross_road[0] = 0;
  768. way->cross_road[0] = 0;
  769. way->dpth = 1.0e25f;
  770. way->wpt_class = 0; // user waypoint by default.
  771. return way;
  772. }
  773. static int
  774. waypt_write_cb(GPS_PWay*)
  775. {
  776. static int i;
  777. int n = waypt_count();
  778. if (global_opts.verbose_status) {
  779. i++;
  780. waypt_status_disp(n, i);
  781. }
  782. return 0;
  783. }
  784. /*
  785. * If we're using smart names, try to put the cache info in the
  786. * description.
  787. */
  788. const char*
  789. get_gc_info(Waypoint* wpt)
  790. {
  791. if (global_opts.smart_names) {
  792. if (wpt->gc_data->type == gt_virtual) {
  793. return "V ";
  794. }
  795. if (wpt->gc_data->type == gt_unknown) {
  796. return "? ";
  797. }
  798. if (wpt->gc_data->type == gt_multi) {
  799. return "Mlt ";
  800. }
  801. if (wpt->gc_data->type == gt_earth) {
  802. return "EC ";
  803. }
  804. if (wpt->gc_data->type == gt_event) {
  805. return "Ev ";
  806. }
  807. if (wpt->gc_data->container == gc_micro) {
  808. return "M ";
  809. }
  810. if (wpt->gc_data->container == gc_small) {
  811. return "S ";
  812. }
  813. }
  814. return "";
  815. }
  816. static int
  817. waypoint_prepare(void)
  818. {
  819. int i;
  820. int n = waypt_count();
  821. #if NEWQ
  822. extern QList<Waypoint*> waypt_list;
  823. #else
  824. queue* elem, *tmp;
  825. extern queue waypt_head;
  826. #endif
  827. int icon;
  828. tx_waylist = (struct GPS_SWay**) xcalloc(n,sizeof(*tx_waylist));
  829. for (i = 0; i < n; i++) {
  830. tx_waylist[i] = sane_GPS_Way_New();
  831. }
  832. i = 0;
  833. #if NEWQ
  834. // Iterate with waypt_disp_all?
  835. foreach(Waypoint* wpt, waypt_list) {
  836. #else
  837. QUEUE_FOR_EACH(&waypt_head, elem, tmp) {
  838. Waypoint* wpt = (Waypoint*) elem;
  839. #endif
  840. char* ident;
  841. char obuf[256];
  842. QString src;
  843. if (!wpt->description.isEmpty()) {
  844. src = wpt->description;
  845. }
  846. if (!wpt->notes.isEmpty()) {
  847. src = wpt->notes;
  848. }
  849. /*
  850. * mkshort will do collision detection and namespace
  851. * cleaning
  852. */
  853. ident = mkshort(mkshort_handle,
  854. global_opts.synthesize_shortnames ? CSTRc(src) :
  855. CSTRc(wpt->shortname));
  856. /* Should not be a strcpy as 'ident' isn't really a C string,
  857. * but rather a garmin "fixed length" buffer that's padded
  858. * to the end with spaces. So this is NOT (strlen+1).
  859. */
  860. memcpy(tx_waylist[i]->ident, ident, strlen(ident));
  861. if (global_opts.synthesize_shortnames) {
  862. xfree(ident);
  863. }
  864. tx_waylist[i]->ident[sizeof(tx_waylist[i]->ident)-1] = 0;
  865. // If we were explictly given a comment from GPX, use that.
  866. // This logic really is horrible and needs to be untangled.
  867. if (!wpt->description.isEmpty() &&
  868. global_opts.smart_names && !wpt->gc_data->diff) {
  869. memcpy(tx_waylist[i]->cmnt, CSTRc(wpt->description), strlen(CSTRc(wpt->description)));
  870. } else {
  871. if (global_opts.smart_names &&
  872. wpt->gc_data->diff && wpt->gc_data->terr) {
  873. snprintf(obuf, sizeof(obuf), "%s%d/%d %s",
  874. get_gc_info(wpt),
  875. wpt->gc_data->diff, wpt->gc_data->terr,
  876. CSTRc(src));
  877. memcpy(tx_waylist[i]->cmnt, obuf, strlen(obuf));
  878. } else {
  879. memcpy(tx_waylist[i]->cmnt, CSTRc(src), strlen(CSTRc(src)));
  880. }
  881. }
  882. tx_waylist[i]->lon = wpt->longitude;
  883. tx_waylist[i]->lat = wpt->latitude;
  884. if (deficon) {
  885. icon = gt_find_icon_number_from_desc(deficon, PCX);
  886. } else {
  887. if (get_cache_icon(wpt)) {
  888. icon = gt_find_icon_number_from_desc(get_cache_icon(wpt), PCX);
  889. } else {
  890. icon = gt_find_icon_number_from_desc(wpt->icon_descr, PCX);
  891. }
  892. }
  893. /* For units that support tiny numbers of waypoints, just
  894. * overwrite that and go very literal.
  895. */
  896. if (gps_waypt_type == 103) {
  897. icon = d103_icon_number_from_symbol(wpt->icon_descr);
  898. }
  899. tx_waylist[i]->smbl = icon;
  900. if (wpt->altitude == unknown_alt) {
  901. tx_waylist[i]->alt_is_unknown = 1;
  902. tx_waylist[i]->alt = 0;
  903. } else {
  904. tx_waylist[i]->alt = wpt->altitude;
  905. }
  906. gpsbabel::DateTime t = wpt->GetCreationTime();
  907. if (t.isValid()) {
  908. tx_waylist[i]->time = t.toTime_t();
  909. tx_waylist[i]->time_populated = 1;
  910. }
  911. if (category) {
  912. tx_waylist[i]->category = 1 << (atoi(category) - 1);
  913. }
  914. if (categorybits) {
  915. tx_waylist[i]->category = categorybits;
  916. }
  917. garmin_fs_garmin_before_write(wpt, tx_waylist[i], gps_waypt_type);
  918. i++;
  919. }
  920. return n;
  921. }
  922. static void
  923. waypoint_write(void)
  924. {
  925. int i, n;
  926. int32 ret;
  927. n = waypoint_prepare();
  928. if ((ret = GPS_Command_Send_Waypoint(portname, tx_waylist, n, waypt_write_cb)) < 0) {
  929. fatal(MYNAME ":communication error sending wayoints..\n");
  930. }
  931. for (i = 0; i < n; ++i) {
  932. GPS_Way_Del(&tx_waylist[i]);
  933. }
  934. if (global_opts.verbose_status) {
  935. fprintf(stdout, "\r\n");
  936. fflush(stdout);
  937. }
  938. xfree(tx_waylist);
  939. }
  940. static void
  941. route_hdr_pr(const route_head* rte)
  942. {
  943. (*cur_tx_routelist_entry)->rte_num = rte->rte_num;
  944. (*cur_tx_routelist_entry)->isrte = 1;
  945. if (!rte->rte_name.isEmpty()) {
  946. strncpy((*cur_tx_routelist_entry)->rte_ident, CSTRc(rte->rte_name),
  947. sizeof((*cur_tx_routelist_entry)->rte_ident));
  948. }
  949. }
  950. static void
  951. route_waypt_pr(const Waypoint* wpt)
  952. {
  953. GPS_PWay rte = *cur_tx_routelist_entry;
  954. /*
  955. * As stupid as this is, libjeeps seems to want an empty
  956. * waypoint between every link in a route that has nothing
  957. * but the 'islink' member set. Rather than "fixing" libjeeps,
  958. * we just double them up (sigh) and do that here.
  959. */
  960. rte->islink = 1;
  961. rte->lon = wpt->longitude;
  962. rte->lat = wpt->latitude;
  963. cur_tx_routelist_entry++;
  964. rte = *cur_tx_routelist_entry;
  965. rte->lon = wpt->longitude;
  966. rte->lat = wpt->latitude;
  967. rte->smbl = gt_find_icon_number_from_desc(wpt->icon_descr, PCX);
  968. // map class so unit doesn't duplicate routepoints as a waypoint.
  969. rte->wpt_class = 0x80;
  970. if (wpt->altitude != unknown_alt) {
  971. rte->alt = wpt->altitude;
  972. } else {
  973. rte->alt_is_unknown = 1;
  974. rte->alt = 0;
  975. }
  976. // Garmin protocol spec says no spaces, no lowercase, etc. in a route.
  977. // enforce that here, since jeeps doesn't.
  978. //
  979. // This was strncpy(rte->ident, wpt->shortname, sizeof(rte->ident));
  980. char* d;
  981. d = rte->ident;
  982. for (int idx = 0; idx < wpt->shortname.length(); idx++) {
  983. int c = wpt->shortname[idx].toLatin1();
  984. if (receiver_must_upper && isalpha(c)) {
  985. c = toupper(c);
  986. }
  987. if (strchr(valid_waypt_chars, c)) {
  988. *d++ = c;
  989. }
  990. }
  991. rte->ident[sizeof(rte->ident)-1] = 0;
  992. if (wpt->description.isEmpty()) {
  993. rte->cmnt[0] = 0;
  994. } else {
  995. strncpy(rte->cmnt, CSTR(wpt->description), sizeof(rte->cmnt));
  996. rte->cmnt[sizeof(rte->cmnt)-1] = 0;
  997. }
  998. cur_tx_routelist_entry++;
  999. }
  1000. static void
  1001. route_noop(const route_head* )
  1002. {
  1003. }
  1004. static void
  1005. route_write(void)
  1006. {
  1007. int i;
  1008. int n = 2 * route_waypt_count(); /* Doubled for the islink crap. */
  1009. tx_routelist = (struct GPS_SWay**) xcalloc(n,sizeof(GPS_PWay));
  1010. cur_tx_routelist_entry = tx_routelist;
  1011. for (i = 0; i < n; i++) {
  1012. tx_routelist[i] = sane_GPS_Way_New();
  1013. }
  1014. route_disp_all(route_hdr_pr, route_noop, route_waypt_pr);
  1015. GPS_Command_Send_Route(portname, tx_routelist, n);
  1016. }
  1017. static void
  1018. track_hdr_pr(const route_head* trk_head)
  1019. {
  1020. (*cur_tx_tracklist_entry)->ishdr = gpsTrue;
  1021. if (!trk_head->rte_name.isEmpty()) {
  1022. strncpy((*cur_tx_tracklist_entry)->trk_ident, CSTRc(trk_head->rte_name), sizeof((*cur_tx_tracklist_entry)->trk_ident));
  1023. (*cur_tx_tracklist_entry)->trk_ident[sizeof((*cur_tx_tracklist_entry)->trk_ident)-1] = 0;
  1024. } else {
  1025. sprintf((*cur_tx_tracklist_entry)->trk_ident, "TRACK%02d", my_track_count);
  1026. }
  1027. cur_tx_tracklist_entry++;
  1028. my_track_count++;
  1029. }
  1030. static void
  1031. track_waypt_pr(const Waypoint* wpt)
  1032. {
  1033. (*cur_tx_tracklist_entry)->lat = wpt->latitude;
  1034. (*cur_tx_tracklist_entry)->lon = wpt->longitude;
  1035. (*cur_tx_tracklist_entry)->alt = (wpt->altitude != unknown_alt) ? wpt->altitude : 1e25;
  1036. (*cur_tx_tracklist_entry)->Time = wpt->GetCreationTime().toTime_t();
  1037. if (!wpt->shortname.isEmpty()) {
  1038. strncpy((*cur_tx_tracklist_entry)->trk_ident, CSTRc(wpt->shortname), sizeof((*cur_tx_tracklist_entry)->trk_ident));
  1039. (*cur_tx_tracklist_entry)->trk_ident[sizeof((*cur_tx_tracklist_entry)->trk_ident)-1] = 0;
  1040. }
  1041. (*cur_tx_tracklist_entry)->tnew = wpt->wpt_flags.new_trkseg;
  1042. cur_tx_tracklist_entry++;
  1043. }
  1044. static int
  1045. track_prepare(void)
  1046. {
  1047. int i;
  1048. int32 n = track_waypt_count() + track_count();
  1049. tx_tracklist = (struct GPS_STrack**) xcalloc(n, sizeof(GPS_PTrack));
  1050. cur_tx_tracklist_entry = tx_tracklist;
  1051. for (i = 0; i < n; i++) {
  1052. tx_tracklist[i] = GPS_Track_New();
  1053. }
  1054. my_track_count = 0;
  1055. track_disp_all(track_hdr_pr, route_noop, track_waypt_pr);
  1056. GPS_Prepare_Track_For_Device(&tx_tracklist, &n);
  1057. return n;
  1058. }
  1059. static void
  1060. track_write(void)
  1061. {
  1062. int i, n;
  1063. n = track_prepare();
  1064. GPS_Command_Send_Track(portname, tx_tracklist, n, (eraset)? 1 : 0);
  1065. for (i = 0; i < n; i++) {
  1066. GPS_Track_Del(&tx_tracklist[i]);
  1067. }
  1068. xfree(tx_tracklist);
  1069. }
  1070. static void
  1071. course_write(void)
  1072. {
  1073. int i, n_trk, n_wpt;
  1074. n_wpt = waypoint_prepare();
  1075. n_trk = track_prepare();
  1076. GPS_Command_Send_Track_As_Course(portname, tx_tracklist, n_trk,
  1077. tx_waylist, n_wpt, (eraset)? 1 : 0);
  1078. for (i = 0; i < n_wpt; ++i) {
  1079. GPS_Way_Del(&tx_waylist[i]);
  1080. }
  1081. xfree(tx_waylist);
  1082. for (i = 0; i < n_trk; i++) {
  1083. GPS_Track_Del(&tx_tracklist[i]);
  1084. }
  1085. xfree(tx_tracklist);
  1086. }
  1087. static void
  1088. data_write(void)
  1089. {
  1090. if (poweroff) {
  1091. return;
  1092. }
  1093. /* If we have both trackpoints and waypoints and the device
  1094. * supports courses, combine them to a course. Otherwise,
  1095. * send tracks & waypoints separately.
  1096. */
  1097. if ((global_opts.masked_objective & WPTDATAMASK) &&
  1098. (global_opts.masked_objective & TRKDATAMASK) &&
  1099. gps_course_transfer != -1) {
  1100. course_write();
  1101. } else {
  1102. if (global_opts.masked_objective & WPTDATAMASK) {
  1103. waypoint_write();
  1104. }
  1105. if (global_opts.masked_objective & TRKDATAMASK) {
  1106. track_write();
  1107. }
  1108. }
  1109. if (global_opts.masked_objective & RTEDATAMASK) {
  1110. route_write();
  1111. }
  1112. }
  1113. ff_vecs_t garmin_vecs = {
  1114. ff_type_serial,
  1115. FF_CAP_RW_ALL,
  1116. rd_init,
  1117. rw_init,
  1118. rw_deinit,
  1119. rw_deinit,
  1120. data_read,
  1121. data_write,
  1122. NULL,
  1123. garmin_args,
  1124. CET_CHARSET_ASCII, 0,
  1125. { pvt_init, pvt_read, rw_deinit, NULL, NULL, NULL }
  1126. };
  1127. static const char* d103_icons[16] = {
  1128. "dot",
  1129. "house",
  1130. "gas",
  1131. "car",
  1132. "fish",
  1133. "boat",
  1134. "anchor",
  1135. "wreck",
  1136. "exit",
  1137. "skull",
  1138. "flag",
  1139. "camp",
  1140. "circle_x",
  1141. "deer",
  1142. "1st_aid",
  1143. "back-track"
  1144. };
  1145. static const char*
  1146. d103_symbol_from_icon_number(unsigned int n)
  1147. {
  1148. if (n <= 15) {
  1149. return d103_icons[n];
  1150. } else {
  1151. return "unknown";
  1152. }
  1153. }
  1154. static int
  1155. d103_icon_number_from_symbol(const QString& s)
  1156. {
  1157. unsigned int i;
  1158. if (s.isNull()) {
  1159. return 0;
  1160. }
  1161. for (i = 0; i < sizeof(d103_icons) / sizeof(d103_icons[0]); i++) {
  1162. if (0 == (s.compare(d103_icons[i], Qt::CaseInsensitive))) {
  1163. return i;
  1164. }
  1165. }
  1166. return 0;
  1167. }