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.

1023 lines
28KB

  1. /*
  2. Copyright (C) 2008 Björn Augustsson, oggust@gmail.com
  3. Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org
  4. Copyright (C) 2005-2013 Robert Lipe, robertlipe+source@gpsbabel.org
  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. #include "defs.h"
  18. #include <QtCore/QMap>
  19. #include <stdio.h>
  20. #include <math.h>
  21. #define MYNAME "humminbird"
  22. #define WPT_NAME_LEN 12
  23. #define RTE_NAME_LEN 20
  24. #define TRK_NAME_LEN 20
  25. #define MAX_RTE_POINTS 50
  26. #define MAX_ITEMS_PER_GROUP 12
  27. /*
  28. I suspect that these are actually
  29. struct signature {
  30. uint8_t format, // 1 = track, 2 = waypoint, 3 = route, 4 = iTrack
  31. uint8_t version,
  32. gpuint16 record_length
  33. }
  34. The v3 TRK_MAGIC doesn't have a length, probably because it wouldn't fit.
  35. (It would be 0x200008)
  36. Still, they're useful in the code as a plain signature.
  37. */
  38. #define TRK_MAGIC 0x01030000L
  39. #define TRK_MAGIC2 0x01021F70L
  40. #define WPT_MAGIC 0x02020024L
  41. #define WPT_MAGIC2 0x02030024L // New for 2013. No visible diff?!
  42. #define RTE_MAGIC 0x03030088L
  43. #define EAST_SCALE 20038297.0 /* this is i1924_equ_axis*M_PI */
  44. #define i1924_equ_axis 6378388.0
  45. #define i1924_polar_axis 6356911.946
  46. #define BAD_CHARS "\r\n\t"
  47. /* The hwr data format is records-based, and the records are 36 bytes long. */
  48. typedef struct humminbird_waypt_s {
  49. /* O.K.: the file can also contain routes with a different magic. */
  50. /* uint32_t signature; */ /* Just for error checking(?) */
  51. uint16_t num; /* Always ascending in the file. */
  52. uint16_t zero; /* Always seems to be zero. */
  53. uint8_t status; /* Always seems to be 1. Ends up as <h:status>
  54. in gpx files exported by HumminbirdPC. */
  55. uint8_t icon; /* See below */
  56. uint16_t depth; /* Water depth. These are fishfinders. In centimeters */
  57. uint32_t time; /* This is a time_t. In UTC */
  58. int32_t east;
  59. int32_t north;
  60. char name[WPT_NAME_LEN];
  61. } humminbird_waypt_t;
  62. typedef struct humminbird_rte_s {
  63. /* O.K.: the file can contain also routes with a different magic. */
  64. /* uint32_t signature; */ /* Just for error checking(?) */
  65. uint16_t num;
  66. uint16_t zero;
  67. uint8_t status;
  68. uint8_t U0;
  69. uint8_t U1;
  70. int8_t count;
  71. uint32_t time;
  72. char name[RTE_NAME_LEN];
  73. uint16_t points[MAX_RTE_POINTS];
  74. } humminbird_rte_t;
  75. typedef struct humminbird_trk_header_s { /* 68 bytes, incl signature */
  76. /* uint32_t signature; */
  77. uint16_t trk_num;
  78. uint16_t zero;
  79. uint16_t num_points;
  80. uint16_t unknown; /* Always zero so far. */
  81. uint32_t time; /* a time_t, in UTC */
  82. int32_t start_east; /* Start of track */
  83. int32_t start_north;
  84. int32_t end_east; /* end of track */
  85. int32_t end_north;
  86. int32_t sw_east; /* Bounding box, enclosing the track */
  87. int32_t sw_north; /* sw is the south-west point */
  88. int32_t ne_east; /* ne is the north-east point */
  89. int32_t ne_north;
  90. char name[20];
  91. } humminbird_trk_header_t;
  92. typedef struct humminbird_trk_point_s {
  93. int16_t deltaeast;
  94. int16_t deltanorth;
  95. uint16_t depth; /* in centimeters */
  96. } humminbird_trk_point_t;
  97. typedef struct humminbird_trk_header_old_s { /* 16 bytes, incl signature */
  98. /* uint32_t signature; */
  99. uint16_t trk_num;
  100. uint16_t zero;
  101. uint16_t num_points;
  102. uint16_t unknown; /* Always zero so far. */
  103. uint32_t time; /* a time_t, in UTC */
  104. int32_t start_east; /* Start of track */
  105. int32_t start_north;
  106. int32_t end_east; /* end of track */
  107. int32_t end_north;
  108. } humminbird_trk_header_old_t;
  109. typedef struct humminbird_trk_point_old_s {
  110. int16_t deltaeast;
  111. int16_t deltanorth;
  112. } humminbird_trk_point_old_t;
  113. typedef struct group_header {
  114. uint8_t status;
  115. uint8_t icon;
  116. uint16_t depth;
  117. uint32_t time; /* a time_t, in UTC */
  118. uint16_t parent_idx;
  119. uint16_t reserved1;
  120. uint16_t first_body_index;
  121. uint16_t reserved2;
  122. char name[WPT_NAME_LEN];
  123. } group_header_t;
  124. typedef struct group_body {
  125. uint8_t status;
  126. uint8_t icon;
  127. uint16_t next_idx;
  128. uint16_t item[MAX_ITEMS_PER_GROUP];
  129. } group_body_t;
  130. static const char* humminbird_icons[] = {
  131. "Normal", /* 0 */
  132. "House", /* 1 */
  133. "Red cross", /* 2 */
  134. "Fish", /* 3 */
  135. "Duck", /* 4 */
  136. "Anchor", /* 5 */
  137. "Buoy", /* 6 */
  138. "Airport", /* 7 */
  139. "Camping", /* 8 */
  140. "Danger", /* 9 */
  141. "Fuel", /* 10 */
  142. "Rock", /* 11 */
  143. "Weed", /* 12 */
  144. "Wreck", /* 13 */
  145. "Phone", /* 14 */
  146. "Coffee", /* 15 */
  147. "Beer", /* 16 */
  148. "Mooring", /* 17 */
  149. "Pier", /* 18 */
  150. "Slip", /* 19 */
  151. "Ramp", /* 20 */
  152. "Circle", /* 21 */
  153. "Diamond", /* 22 */
  154. "Flag", /* 23 */
  155. "Pattern", /* 24 */
  156. "Shower", /* 25 */
  157. "Water tap", /* 26 */
  158. "Tree", /* 27 */
  159. "Recording", /* 28 */
  160. "Snapshot" /* 29 */
  161. };
  162. static gbfile* fin;
  163. static gbfile* fout;
  164. static int waypoint_num;
  165. static short_handle wptname_sh, rtename_sh, trkname_sh;
  166. static humminbird_rte_t* humrte;
  167. static int rte_num;
  168. static QMap<QString, Waypoint*> map;
  169. static
  170. arglist_t humminbird_args[] = {
  171. ARG_TERMINATOR
  172. };
  173. /* Takes a latitude in degrees,
  174. * returns a latitude in degrees. */
  175. static double
  176. geodetic_to_geocentric_hwr(const double gd_lat)
  177. {
  178. const double cos_ae = 0.9966349016452;
  179. const double cos2_ae = cos_ae * cos_ae;
  180. const double gdr = gd_lat *M_PI / 180.0;
  181. return atan(cos2_ae * tan(gdr)) * 180.0/M_PI;
  182. }
  183. /* Takes a latitude in degrees,
  184. * returns a latitude in degrees. */
  185. static double
  186. geocentric_to_geodetic_hwr(const double gc_lat)
  187. {
  188. const double cos_ae = 0.9966349016452;
  189. const double cos2_ae = cos_ae * cos_ae;
  190. const double gcr = gc_lat *M_PI / 180.0;
  191. return atan(tan(gcr)/cos2_ae) * 180.0/M_PI;
  192. }
  193. /* Takes a projected "north" value, returns latitude in degrees. */
  194. static double
  195. gudermannian_i1924(const double x)
  196. {
  197. const double norm_x = x/i1924_equ_axis;
  198. return atan(sinh(norm_x)) * 180.0/M_PI;
  199. }
  200. /* Takes latitude in degrees, returns projected "north" value. */
  201. static double
  202. inverse_gudermannian_i1924(const double x)
  203. {
  204. const double x_r = x/180.0 * M_PI;
  205. const double guder = log(tan(M_PI/4.0 + x_r/2.0));
  206. return guder * i1924_equ_axis;
  207. }
  208. /*******************************************************************************
  209. * %%% global callbacks called by gpsbabel main process %%% *
  210. *******************************************************************************/
  211. static void
  212. humminbird_rd_init(const QString& fname)
  213. {
  214. fin = gbfopen_be(fname, "rb", MYNAME);
  215. }
  216. static void
  217. humminbird_rd_deinit(void)
  218. {
  219. gbfclose(fin);
  220. }
  221. static void
  222. humminbird_read_wpt(gbfile* fin)
  223. {
  224. humminbird_waypt_t w;
  225. double guder;
  226. int num_icons;
  227. Waypoint* wpt;
  228. if (! gbfread(&w, 1, sizeof(w), fin)) {
  229. fatal(MYNAME ": Unexpected end of file!\n");
  230. }
  231. /* Fix endianness - these are now BE */
  232. w.num = be_read16(&w.num);
  233. w.zero = be_read16(&w.zero);
  234. w.depth = be_read16(&w.depth);
  235. w.time = be_read32(&w.time);
  236. w.north = be_read32(&w.north);
  237. w.east = be_read32(&w.east);
  238. /* All right! Copy the data to the gpsbabel struct... */
  239. wpt = new Waypoint;
  240. // Could probably find a way to eliminate the alloc/copy.
  241. char* s = xstrndup(w.name, sizeof(w.name));
  242. wpt->shortname = s;
  243. xfree(s);
  244. wpt->SetCreationTime(w.time);
  245. guder = gudermannian_i1924(w.north);
  246. wpt->latitude = geocentric_to_geodetic_hwr(guder);
  247. wpt->longitude = (double)w.east / EAST_SCALE * 180.0;
  248. wpt->altitude = 0.0; /* It's from a fishfinder... */
  249. if (w.depth != 0) {
  250. WAYPT_SET(wpt,depth,(double)w.depth / 100.0);
  251. }
  252. num_icons = sizeof(humminbird_icons) / sizeof(humminbird_icons[0]);
  253. if (w.icon < num_icons) {
  254. wpt->icon_descr = humminbird_icons[w.icon];
  255. }
  256. // In newer versions, this is an enum (though it looks like a bitfield)
  257. // that describes a sub-status
  258. switch (w.status) {
  259. case 0: // Waypoint not used. So why do we have one?
  260. delete wpt;
  261. break;
  262. case 1: // Waypoint permanent.
  263. case 2: // Waypoint temporary.
  264. case 3: // Waypoint man-overboard.
  265. waypt_add(wpt);
  266. break;
  267. case 16: // Waypoint group header.
  268. case 17: // Waypoint group body.
  269. case 63: // Waypoint group invalid.
  270. default:
  271. delete wpt;
  272. break;
  273. }
  274. /* register the point over his internal Humminbird "Number" */
  275. QString buff = QString::number(w.num);
  276. map[buff] = wpt;
  277. }
  278. static void
  279. humminbird_read_route(gbfile* fin)
  280. {
  281. humminbird_rte_t hrte;
  282. if (! gbfread(&hrte, 1, sizeof(hrte), fin)) {
  283. fatal(MYNAME ": Unexpected end of file!\n");
  284. }
  285. hrte.time = be_read32(&hrte.time);
  286. hrte.num = be_read16(&hrte.num);
  287. if (hrte.count > 0) {
  288. int i;
  289. route_head* rte = NULL;
  290. for (i = 0; i < hrte.count; i++) {
  291. const Waypoint* wpt;
  292. char buff[10];
  293. hrte.points[i] = be_read16(&hrte.points[i]);
  294. /* locate the point over his internal Humminbird "Number" */
  295. snprintf(buff, sizeof(buff), "%d", hrte.points[i]);
  296. if ((map.value(buff))) {
  297. wpt = map.value(buff);
  298. if (rte == NULL) {
  299. rte = route_head_alloc();
  300. route_add_head(rte);
  301. // TODO: find a way to eliminate the copy.
  302. char* s = xstrndup(hrte.name, sizeof(hrte.name));
  303. rte->rte_name = s;
  304. xfree(s);
  305. /* rte->rte_num = hrte.num + 1; only internal number */
  306. }
  307. route_add_wpt(rte, new Waypoint(*wpt));
  308. }
  309. }
  310. }
  311. }
  312. static void
  313. humminbird_read_track(gbfile* fin)
  314. {
  315. humminbird_trk_header_t th;
  316. humminbird_trk_point_t* points;
  317. route_head* trk;
  318. Waypoint* first_wpt;
  319. int i;
  320. int max_points = 0;
  321. int32_t accum_east;
  322. int32_t accum_north;
  323. double g_lat;
  324. if (! gbfread(&th, 1, sizeof(th), fin)) {
  325. fatal(MYNAME ": Unexpected end of file reading header!\n");
  326. }
  327. th.trk_num = be_read16(&th.trk_num);
  328. th.num_points = be_read16(&th.num_points);
  329. th.time = be_read32(&th.time);
  330. th.start_east = be_read32(&th.start_east);
  331. th.start_north = be_read32(&th.start_north);
  332. th.end_east = be_read32(&th.end_east);
  333. th.end_north = be_read32(&th.end_north);
  334. th.sw_east = be_read32(&th.sw_east);
  335. th.sw_north = be_read32(&th.sw_north);
  336. th.ne_east = be_read32(&th.ne_east);
  337. th.ne_north = be_read32(&th.ne_north);
  338. max_points = (131080 - sizeof(uint32_t) - sizeof(th)) / sizeof(humminbird_trk_point_t);
  339. if (th.num_points == max_points + 1) {
  340. th.num_points--;
  341. }
  342. if (th.num_points > max_points) {
  343. fatal(MYNAME ": Too many track points! (%d)\n", th.num_points);
  344. }
  345. /* num_points is actually one too big, because it includes the value in
  346. the header. But we want the extra point at the end because the
  347. freak-value filter below looks at points[i+1] */
  348. points = (humminbird_trk_point_t*) xcalloc(th.num_points, sizeof(humminbird_trk_point_t));
  349. if (! gbfread(points, sizeof(humminbird_trk_point_t), th.num_points-1, fin)) {
  350. fatal(MYNAME ": Unexpected end of file reading points!\n");
  351. }
  352. accum_east = th.start_east;
  353. accum_north = th.start_north;
  354. trk = route_head_alloc();
  355. track_add_head(trk);
  356. // TODO: find a way to eliminate the copy.
  357. char* s = xstrndup(th.name, sizeof(th.name));
  358. trk->rte_name = s;
  359. xfree(s);
  360. trk->rte_num = th.trk_num;
  361. /* We create one wpt for the info in the header */
  362. first_wpt = new Waypoint;
  363. g_lat = gudermannian_i1924(accum_north);
  364. first_wpt->latitude = geocentric_to_geodetic_hwr(g_lat);
  365. first_wpt->longitude = accum_east/EAST_SCALE * 180.0;
  366. first_wpt->altitude = 0.0;
  367. /* No depth info in the header. */
  368. track_add_wpt(trk, first_wpt);
  369. for (i=0 ; i<th.num_points-1 ; i++) {
  370. Waypoint* wpt = new Waypoint;
  371. int16_t next_deltaeast, next_deltanorth;
  372. double guder;
  373. points[i].depth = be_read16(&points[i].depth);
  374. points[i].deltaeast = be_read16(&points[i].deltaeast);
  375. points[i].deltanorth = be_read16(&points[i].deltanorth);
  376. /* Every once in a while the delta values are
  377. 32767 followed by -32768. Filter that. */
  378. next_deltaeast = be_read16(&points[i+1].deltaeast);
  379. if (points[ i ].deltaeast == 32767 &&
  380. next_deltaeast == -32768) {
  381. points[ i ].deltaeast = -1;
  382. points[i+1].deltaeast = 0; /* BE 0 == LE 0 */
  383. }
  384. next_deltanorth = be_read16(&points[i+1].deltanorth);
  385. if (points[ i ].deltanorth == 32767 &&
  386. next_deltanorth == -32768) {
  387. points[ i ].deltanorth = -1;
  388. points[i+1].deltanorth = 0;
  389. }
  390. accum_east += points[i].deltaeast;
  391. accum_north += points[i].deltanorth;
  392. guder = gudermannian_i1924(accum_north);
  393. wpt->latitude = geocentric_to_geodetic_hwr(guder);
  394. wpt->longitude = accum_east/EAST_SCALE * 180.0;
  395. wpt->altitude = 0.0;
  396. if (points[i].depth != 0) {
  397. WAYPT_SET(wpt,depth,(double)points[i].depth / 100.0);
  398. }
  399. if (i == th.num_points-2 && th.time != 0) {
  400. /* Last point. Add the date from the header. */
  401. /* Unless it's zero. Sometimes happens, possibly if
  402. the gps didn't have a lock when the track was
  403. saved. */
  404. wpt->SetCreationTime(th.time);
  405. }
  406. track_add_wpt(trk, wpt);
  407. }
  408. xfree(points);
  409. }
  410. static void
  411. humminbird_read_track_old(gbfile* fin)
  412. {
  413. humminbird_trk_header_old_t th;
  414. humminbird_trk_point_old_t* points;
  415. route_head* trk;
  416. Waypoint* first_wpt;
  417. int i;
  418. int max_points = 0;
  419. int32_t accum_east;
  420. int32_t accum_north;
  421. double g_lat;
  422. const int file_len = 8048;
  423. char namebuf[TRK_NAME_LEN];
  424. if (! gbfread(&th, 1, sizeof(th), fin)) {
  425. fatal(MYNAME ": Unexpected end of file reading header!\n");
  426. }
  427. th.trk_num = be_read16(&th.trk_num);
  428. th.num_points = be_read16(&th.num_points);
  429. th.time = be_read32(&th.time);
  430. th.start_east = be_read32(&th.start_east);
  431. th.start_north = be_read32(&th.start_north);
  432. th.end_east = be_read32(&th.end_east);
  433. th.end_north = be_read32(&th.end_north);
  434. // These files are always 8048 bytes long. Note that that's the value
  435. // of the second 16-bit word in the signature.
  436. max_points = (file_len - (sizeof(th) + sizeof(uint32_t) + TRK_NAME_LEN)) / sizeof(humminbird_trk_point_old_t);
  437. if (th.num_points > max_points) {
  438. fatal(MYNAME ": Too many track points! (%d)\n", th.num_points);
  439. }
  440. /* num_points is actually one too big, because it includes the value in
  441. the header. But we want the extra point at the end because the
  442. freak-value filter below looks at points[i+1] */
  443. points = (humminbird_trk_point_old_t*)xcalloc(th.num_points, sizeof(humminbird_trk_point_old_t));
  444. if (! gbfread(points, sizeof(humminbird_trk_point_old_t), th.num_points-1, fin)) {
  445. fatal(MYNAME ": Unexpected end of file reading points!\n");
  446. }
  447. accum_east = th.start_east;
  448. accum_north = th.start_north;
  449. trk = route_head_alloc();
  450. track_add_head(trk);
  451. /* The name is not in the header, but at the end of the file.
  452. (The last 20 bytes.) */
  453. gbfseek(fin, file_len-TRK_NAME_LEN, SEEK_SET);
  454. gbfread(&namebuf, 1, TRK_NAME_LEN, fin);
  455. trk->rte_name = xstrndup(namebuf, sizeof(namebuf));
  456. trk->rte_num = th.trk_num;
  457. /* We create one wpt for the info in the header */
  458. first_wpt = new Waypoint;
  459. g_lat = gudermannian_i1924(accum_north);
  460. first_wpt->latitude = geocentric_to_geodetic_hwr(g_lat);
  461. first_wpt->longitude = accum_east/EAST_SCALE * 180.0;
  462. first_wpt->altitude = 0.0;
  463. track_add_wpt(trk, first_wpt);
  464. for (i=0 ; i<th.num_points-1 ; i++) {
  465. Waypoint* wpt = new Waypoint;
  466. // int16_t next_deltaeast, next_deltanorth;
  467. double guder;
  468. points[i].deltaeast = be_read16(&points[i].deltaeast);
  469. points[i].deltanorth = be_read16(&points[i].deltanorth);
  470. // I've commented this out, don't know if it happens in this
  471. // format. It happens in the newer version though.
  472. // /* Every once in a while the delta values are
  473. // 32767 followed by -32768. Filter that. */
  474. //
  475. // next_deltaeast = be_read16(&points[i+1].deltaeast);
  476. // if (points[ i ].deltaeast == 32767 &&
  477. // next_deltaeast == -32768) {
  478. // points[ i ].deltaeast = -1;
  479. // points[i+1].deltaeast = 0; /* BE 0 == LE 0 */
  480. // }
  481. // next_deltanorth = be_read16(&points[i+1].deltanorth);
  482. // if (points[ i ].deltanorth == 32767 &&
  483. // next_deltanorth == -32768) {
  484. // points[ i ].deltanorth = -1;
  485. // points[i+1].deltanorth = 0;
  486. // }
  487. //
  488. accum_east += points[i].deltaeast;
  489. accum_north += points[i].deltanorth;
  490. guder = gudermannian_i1924(accum_north);
  491. wpt->latitude = geocentric_to_geodetic_hwr(guder);
  492. wpt->longitude = accum_east/EAST_SCALE * 180.0;
  493. wpt->altitude = 0.0;
  494. if (i == th.num_points-2 && th.time != 0) {
  495. /* Last point. Add the date from the header. */
  496. /* Unless it's zero. Sometimes happens, possibly if
  497. the gps didn't have a lock when the track was
  498. saved. */
  499. wpt->SetCreationTime(th.time);
  500. }
  501. track_add_wpt(trk, wpt);
  502. }
  503. xfree(points);
  504. }
  505. static void
  506. humminbird_read(void)
  507. {
  508. while (! gbfeof(fin)) {
  509. uint32_t signature;
  510. signature = gbfgetuint32(fin);
  511. switch (signature) {
  512. case WPT_MAGIC:
  513. case WPT_MAGIC2:
  514. humminbird_read_wpt(fin);
  515. break;
  516. case RTE_MAGIC:
  517. humminbird_read_route(fin);
  518. break;
  519. case TRK_MAGIC:
  520. humminbird_read_track(fin);
  521. return; /* Don't continue. The rest of the file is all zeores */
  522. case TRK_MAGIC2:
  523. humminbird_read_track_old(fin);
  524. return; /* Don't continue. The rest of the file is all zeores */
  525. default:
  526. fatal(MYNAME ": Invalid record header \"0x%08X\" (no or unknown humminbird file)!\n", signature);
  527. }
  528. }
  529. }
  530. /************************************************************************************************/
  531. static void
  532. humminbird_wr_init(const QString& fname)
  533. {
  534. fout = gbfopen_be(fname, "wb", MYNAME);
  535. wptname_sh = mkshort_new_handle();
  536. setshort_length(wptname_sh, WPT_NAME_LEN - 1);
  537. setshort_badchars(wptname_sh, BAD_CHARS);
  538. setshort_mustupper(wptname_sh, 0);
  539. setshort_mustuniq(wptname_sh, 0);
  540. setshort_whitespace_ok(wptname_sh, 1);
  541. setshort_repeating_whitespace_ok(wptname_sh, 1);
  542. setshort_defname(wptname_sh, "WPT");
  543. rtename_sh = mkshort_new_handle();
  544. setshort_length(rtename_sh, RTE_NAME_LEN - 1);
  545. setshort_badchars(rtename_sh, BAD_CHARS);
  546. setshort_mustupper(rtename_sh, 0);
  547. setshort_mustuniq(rtename_sh, 0);
  548. setshort_whitespace_ok(rtename_sh, 1);
  549. setshort_repeating_whitespace_ok(rtename_sh, 1);
  550. setshort_defname(rtename_sh, "Route");
  551. trkname_sh = mkshort_new_handle();
  552. setshort_length(trkname_sh, RTE_NAME_LEN - 1);
  553. setshort_badchars(trkname_sh, BAD_CHARS);
  554. setshort_mustupper(trkname_sh, 0);
  555. setshort_mustuniq(trkname_sh, 0);
  556. setshort_whitespace_ok(trkname_sh, 1);
  557. setshort_repeating_whitespace_ok(trkname_sh, 1);
  558. setshort_defname(trkname_sh, "Track");
  559. waypoint_num = 0;
  560. rte_num = 0;
  561. }
  562. static void
  563. humminbird_wr_deinit(void)
  564. {
  565. mkshort_del_handle(&wptname_sh);
  566. mkshort_del_handle(&rtename_sh);
  567. mkshort_del_handle(&trkname_sh);
  568. gbfclose(fout);
  569. }
  570. static void
  571. humminbird_write_waypoint(const Waypoint* wpt)
  572. {
  573. humminbird_waypt_t hum;
  574. double lat, north, east;
  575. int i;
  576. int num_icons = sizeof(humminbird_icons) / sizeof(humminbird_icons[0]);
  577. be_write16(&hum.num, waypoint_num++);
  578. hum.zero = 0;
  579. hum.status = 1;
  580. hum.icon = 255;
  581. // Icon....
  582. if (!wpt->icon_descr.isNull()) {
  583. for (i = 0; i < num_icons; i++) {
  584. if (!wpt->icon_descr.compare(humminbird_icons[i], Qt::CaseInsensitive)) {
  585. hum.icon = i;
  586. break;
  587. }
  588. }
  589. if (hum.icon == 255) { /* no success, no try to find the item in a more comlex name */
  590. hum.icon = 0; /* i.e. "Diamond" as part of "Diamond, Green" or "Green Diamond" */
  591. for (i = 0; i < num_icons; i++) {
  592. char* match;
  593. int j;
  594. xasprintf(&match, "*%s*", humminbird_icons[i]);
  595. j = wpt->icon_descr.compare(match, Qt::CaseInsensitive);
  596. xfree(match);
  597. if (j != 0) {
  598. hum.icon = i;
  599. break;
  600. }
  601. }
  602. }
  603. }
  604. hum.depth = si_round(WAYPT_GET(wpt, depth, 0)*100.0);
  605. be_write16(&hum.depth, hum.depth);
  606. be_write32(&hum.time, wpt->GetCreationTime().toTime_t());
  607. east = wpt->longitude / 180.0 * EAST_SCALE;
  608. be_write32(&hum.east, si_round((east)));
  609. lat = geodetic_to_geocentric_hwr(wpt->latitude);
  610. north = inverse_gudermannian_i1924(lat);
  611. be_write32(&hum.north, si_round(north));
  612. QString name;
  613. name = (global_opts.synthesize_shortnames)
  614. ? mkshort_from_wpt(wptname_sh, wpt)
  615. : mkshort(wptname_sh, wpt->shortname);
  616. memset(&hum.name, 0, sizeof(hum.name));
  617. memcpy(&hum.name, CSTR(name), name.length());
  618. gbfputuint32(WPT_MAGIC, fout);
  619. gbfwrite(&hum, sizeof(hum), 1, fout);
  620. }
  621. static humminbird_trk_header_t* trk_head;
  622. static humminbird_trk_point_t* trk_points;
  623. static int32_t last_east;
  624. static int32_t last_north;
  625. static uint32_t last_time;
  626. static void
  627. humminbird_track_head(const route_head* trk)
  628. {
  629. int max_points = (131080 - sizeof(uint32_t)- sizeof(humminbird_trk_header_t)) / sizeof(humminbird_trk_point_t);
  630. trk_head = NULL;
  631. last_time = 0;
  632. if (trk->rte_waypt_ct > 0) {
  633. QString name;
  634. trk_head = (humminbird_trk_header_t*) xcalloc(1, sizeof(humminbird_trk_header_t));
  635. trk_points = (humminbird_trk_point_t*) xcalloc(max_points, sizeof(humminbird_trk_point_t));
  636. name = mkshort(trkname_sh, trk->rte_name);
  637. strncpy(trk_head->name, CSTR(name), sizeof(trk_head->name)-1);
  638. be_write16(&trk_head->trk_num, trk->rte_num);
  639. }
  640. }
  641. static void
  642. humminbird_track_tail(const route_head* rte)
  643. {
  644. int max_points = (131080 - sizeof(uint32_t)- sizeof(humminbird_trk_header_t)) / sizeof(humminbird_trk_point_t);
  645. if (trk_head == NULL) {
  646. return;
  647. }
  648. be_write32(&trk_head->end_east, last_east);
  649. be_write32(&trk_head->end_north, last_north);
  650. be_write32(&trk_head->time, last_time);
  651. /* Fix some endianness */
  652. be_write32(&trk_head->sw_east, trk_head->sw_east);
  653. be_write32(&trk_head->ne_east, trk_head->ne_east);
  654. be_write32(&trk_head->sw_north, trk_head->sw_north);
  655. be_write32(&trk_head->ne_north, trk_head->ne_north);
  656. be_write16(&trk_head->num_points, trk_head->num_points);
  657. /* Actually write it out */
  658. gbfputuint32(TRK_MAGIC, fout);
  659. gbfwrite(trk_head, 1, sizeof(humminbird_trk_header_t), fout);
  660. gbfwrite(trk_points, max_points, sizeof(humminbird_trk_point_t), fout);
  661. gbfputuint16(0, fout); /* Odd but true. The format doesn't fit an int nr of entries. */
  662. xfree(trk_head);
  663. xfree(trk_points);
  664. trk_head = NULL;
  665. trk_points = NULL;
  666. }
  667. static void
  668. humminbird_track_cb(const Waypoint* wpt)
  669. {
  670. int32_t north, east;
  671. double lat;
  672. int i;
  673. if (trk_head == NULL) {
  674. return;
  675. }
  676. i = trk_head->num_points;
  677. east = si_round(wpt->longitude / 180.0 * EAST_SCALE);
  678. lat = geodetic_to_geocentric_hwr(wpt->latitude);
  679. north = si_round(inverse_gudermannian_i1924(lat));
  680. if (wpt->creation_time.isValid()) {
  681. last_time = wpt->GetCreationTime().toTime_t();
  682. }
  683. if (i == 0) {
  684. /* It's the first point. That info goes in the header. */
  685. be_write32(&trk_head->start_east, east);
  686. be_write32(&trk_head->start_north, north);
  687. /* Bounding box. Easy for one point. */
  688. /* These are not BE yet, fixed in the end. */
  689. trk_head->sw_east = east;
  690. trk_head->ne_east = east;
  691. trk_head->sw_north = north;
  692. trk_head->ne_north = north;
  693. /* No depth info in the header. */
  694. } else {
  695. /* These points are 16-bit differential. */
  696. int j = i-1;
  697. trk_points[j].deltaeast = east - last_east;
  698. trk_points[j].deltanorth = north - last_north;
  699. trk_points[j].depth = si_round(WAYPT_GET(wpt, depth, 0)*100.0);
  700. /* BE-ify */
  701. be_write16(&trk_points[j].deltaeast, trk_points[j].deltaeast);
  702. be_write16(&trk_points[j].deltanorth, trk_points[j].deltanorth);
  703. be_write16(&trk_points[j].depth, trk_points[j].depth);
  704. /* Update bounding box in header if neccessary */
  705. if (east > trk_head->ne_east) {
  706. trk_head->ne_east = east;
  707. }
  708. if (east < trk_head->sw_east) {
  709. trk_head->sw_east = east;
  710. }
  711. if (north > trk_head->ne_north) {
  712. trk_head->ne_north = north;
  713. }
  714. if (north < trk_head->sw_north) {
  715. trk_head->sw_north = north;
  716. }
  717. }
  718. last_east = east;
  719. last_north = north;
  720. trk_head->num_points++;
  721. }
  722. static void
  723. humminbird_track_write(void)
  724. {
  725. track_disp_all(humminbird_track_head, humminbird_track_tail, humminbird_track_cb);
  726. }
  727. static void
  728. humminbird_rte_head(const route_head* rte)
  729. {
  730. humrte = NULL;
  731. if (rte->rte_waypt_ct > 0) {
  732. humrte = (humminbird_rte_t*) xcalloc(1, sizeof(*humrte));
  733. }
  734. }
  735. static void
  736. humminbird_rte_tail(const route_head* rte)
  737. {
  738. if (humrte == NULL) {
  739. return;
  740. }
  741. if (humrte->count > 0) {
  742. int i;
  743. humrte->num = rte_num++;
  744. humrte->time = gpsbabel_time;
  745. for (i = 0; i < humrte->count; i++) {
  746. be_write16(&humrte->points[i], humrte->points[i]);
  747. }
  748. be_write16(&humrte->num, humrte->num);
  749. be_write32(&humrte->time, humrte->time);
  750. QString name = mkshort(rtename_sh, rte->rte_name);
  751. strncpy(humrte->name, CSTR(name), sizeof(humrte->name));
  752. gbfputuint32(RTE_MAGIC, fout);
  753. gbfwrite(humrte, sizeof(*humrte), 1, fout);
  754. }
  755. xfree(humrte);
  756. humrte = NULL;
  757. }
  758. static void
  759. humminbird_write_rtept(const Waypoint* wpt)
  760. {
  761. int i;
  762. if (humrte == NULL) {
  763. return;
  764. }
  765. i = gb_ptr2int(wpt->extra_data);
  766. if (i <= 0) {
  767. return;
  768. }
  769. if (humrte->count < MAX_RTE_POINTS) {
  770. humrte->points[humrte->count] = i - 1;
  771. humrte->count++;
  772. } else {
  773. warning(MYNAME ": Sorry, routes are limited to %d points!\n", MAX_RTE_POINTS);
  774. fatal(MYNAME ": You can use our simplify filter to reduce the number of route points.\n");
  775. }
  776. }
  777. static void
  778. humminbird_write_waypoint_wrapper(const Waypoint* wpt)
  779. {
  780. char* key;
  781. Waypoint* tmpwpt;
  782. xasprintf(&key, "%s\01%.9f\01%.9f", CSTRc(wpt->shortname), wpt->latitude, wpt->longitude);
  783. if (!(tmpwpt = map[key])) {
  784. tmpwpt = (Waypoint*)wpt;
  785. map[key] = (Waypoint*) wpt;
  786. tmpwpt->extra_data = gb_int2ptr(waypoint_num + 1); /* NOT NULL */
  787. humminbird_write_waypoint(wpt);
  788. } else {
  789. void* p = tmpwpt->extra_data;
  790. tmpwpt = (Waypoint*)wpt;
  791. tmpwpt->extra_data = p;
  792. }
  793. xfree(key);
  794. }
  795. static void
  796. humminbird_write(void)
  797. {
  798. waypt_disp_all(humminbird_write_waypoint_wrapper);
  799. route_disp_all(NULL, NULL, humminbird_write_waypoint_wrapper);
  800. route_disp_all(humminbird_rte_head, humminbird_rte_tail, humminbird_write_rtept);
  801. }
  802. /**************************************************************************/
  803. ff_vecs_t humminbird_vecs = {
  804. ff_type_file,
  805. {
  806. (ff_cap)(ff_cap_read | ff_cap_write) /* waypoints */,
  807. ff_cap_read /* tracks */,
  808. (ff_cap)(ff_cap_read | ff_cap_write) /* routes */
  809. },
  810. humminbird_rd_init,
  811. humminbird_wr_init,
  812. humminbird_rd_deinit,
  813. humminbird_wr_deinit,
  814. humminbird_read,
  815. humminbird_write,
  816. NULL, // humminbird_exit,
  817. humminbird_args,
  818. CET_CHARSET_ASCII, 1 /* ascii is the expected character set */
  819. /* currently fixed !!! */
  820. };
  821. /**************************************************************************/
  822. /**************************************************************************/
  823. ff_vecs_t humminbird_ht_vecs = {
  824. ff_type_file,
  825. {
  826. ff_cap_read /* waypoints */,
  827. (ff_cap)(ff_cap_read | ff_cap_write) /* tracks */,
  828. ff_cap_read /* routes */
  829. },
  830. humminbird_rd_init,
  831. humminbird_wr_init,
  832. humminbird_rd_deinit,
  833. humminbird_wr_deinit,
  834. humminbird_read,
  835. humminbird_track_write,
  836. NULL, // humminbird_exit,
  837. humminbird_args,
  838. CET_CHARSET_ASCII, 1 /* ascii is the expected character set */
  839. /* currently fixed !!! */
  840. };
  841. /**************************************************************************/