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.

lowranceusr4.cc 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. /*
  2. Access to Lowrance USR version 4 files.
  3. Contributed to gpsbabel by Kris Beevers (beevek at gmail.com)
  4. Copyright (C) 2011 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. HISTORY:
  17. 01/06/2012 - Kris Beevers (beevek at gmail.com)
  18. - First pass read-write support
  19. */
  20. #include "defs.h"
  21. #include <math.h> /* for lat/lon conversion */
  22. #include <time.h> /* for gmtime */
  23. #include <stdio.h> /* for gmtime */
  24. #include <stdlib.h> // atoi
  25. /* from waypt.c, we need to iterate over waypoints when extracting
  26. routes */
  27. #if NEWQ
  28. extern QList<Waypoint*> waypt_list;
  29. #else
  30. extern queue waypt_head;
  31. #endif
  32. static gbfile* file_in;
  33. static gbfile* file_out;
  34. static short_handle mkshort_handle;
  35. static route_head* trk_head;
  36. static route_head* rte_head;
  37. static int reading_version;
  38. static int waypt_uid;
  39. static int route_uid;
  40. static int track_uid;
  41. static Waypoint** waypt_table;
  42. static int waypt_table_sz, waypt_table_ct;
  43. static char* opt_title;
  44. static char* opt_serialnum;
  45. static int opt_serialnum_i;
  46. static char* opt_content_descr;
  47. #define MYNAME "Lowrance USR4"
  48. #define MAXUSRSTRINGSIZE 256
  49. #define SEMIMINOR 6356752.3142
  50. #define DEGREESTORADIANS 0.017453292
  51. typedef struct {
  52. format_specific_data fs;
  53. int uid_unit;
  54. int uid_seq_low;
  55. int uid_seq_high;
  56. } lowranceusr4_fsdata;
  57. static int
  58. lowranceusr4_readstr(char* buf, const int maxlen, gbfile* file, int bytes_per_char)
  59. {
  60. int org, len;
  61. int bytesread = 0;
  62. org = len = gbfgetint32(file); /* bytes */
  63. if (len < 0) {
  64. buf[0] = '\0'; /* seems len=-1 means no string */
  65. return 0;
  66. } else if (len) {
  67. if (len > maxlen*bytes_per_char) {
  68. len = maxlen*bytes_per_char;
  69. }
  70. if (bytes_per_char == 1) {
  71. bytesread += gbfread(buf, 1, len, file);
  72. } else {
  73. /* simple adjustment to read strings where characters are 16
  74. bits (or more). for now let's just project the characters
  75. down onto utf-8 space by ignoring all but the most
  76. significant byte. */
  77. int i, j;
  78. char discard;
  79. for (i = 0; i < len/bytes_per_char; ++i) {
  80. bytesread += gbfread(&buf[i], 1, 1, file);
  81. for (j = 1; j < bytes_per_char; ++j) {
  82. bytesread +=gbfread(&discard, 1, 1, file);
  83. }
  84. }
  85. buf[len/bytes_per_char] = '\0';
  86. }
  87. if (org > bytesread) {
  88. (void) gbfseek(file, (org - bytesread), SEEK_CUR);
  89. }
  90. }
  91. return len;
  92. }
  93. static void
  94. lowranceusr4_writestr(const QString& buf, gbfile* file, unsigned int bytes_per_char)
  95. {
  96. unsigned int len = 0;
  97. len = buf.length();
  98. if (0xffffffff / bytes_per_char < len) {
  99. /* be pedantic and check for the unlikely event that we are asked
  100. to write more than 2^32 bytes */
  101. len = 0xffffffff / bytes_per_char;
  102. }
  103. gbfputint32(len*bytes_per_char, file_out);
  104. if (bytes_per_char == 1) {
  105. (void) gbfwrite(CSTR(buf), 1, len, file);
  106. } else {
  107. for (unsigned int i = 0; i < len; ++i) {
  108. gbfputc(buf[i].cell(), file_out);
  109. for (unsigned int j = 1; j < bytes_per_char; ++j) {
  110. gbfputc('\0', file_out);
  111. }
  112. }
  113. }
  114. }
  115. static
  116. arglist_t lowranceusr4_args[] = {
  117. {
  118. "title", &opt_title, "(output) Output title string",
  119. "", ARGTYPE_STRING, ARG_NOMINMAX
  120. },
  121. {
  122. "serialnum", &opt_serialnum, "(output) Device serial number",
  123. "0", ARGTYPE_INT, ARG_NOMINMAX
  124. },
  125. {
  126. "description", &opt_content_descr, "(output) Content description",
  127. "", ARGTYPE_STRING, ARG_NOMINMAX
  128. },
  129. ARG_TERMINATOR
  130. };
  131. static void
  132. rd_init(const QString& fname)
  133. {
  134. file_in = gbfopen_le(fname, "rb", MYNAME);
  135. }
  136. static void
  137. rd_deinit(void)
  138. {
  139. gbfclose(file_in);
  140. }
  141. static void
  142. wr_init(const QString& fname)
  143. {
  144. file_out = gbfopen_le(fname, "wb", MYNAME);
  145. mkshort_handle = mkshort_new_handle();
  146. }
  147. static void
  148. wr_deinit(void)
  149. {
  150. gbfclose(file_out);
  151. mkshort_del_handle(&mkshort_handle);
  152. }
  153. /**
  154. * Latitude and longitude for USR coords are in the lowrance mercator meter
  155. * format in WGS84. The below code converts them to degrees.
  156. */
  157. static double
  158. lon_mm_to_deg(double x)
  159. {
  160. return x / (DEGREESTORADIANS * SEMIMINOR);
  161. }
  162. static double
  163. lat_mm_to_deg(double x)
  164. {
  165. return (2.0 * atan(exp(x / SEMIMINOR)) - M_PI / 2.0) / DEGREESTORADIANS;
  166. }
  167. /* will be useful for write support */
  168. static long
  169. lon_deg_to_mm(double x)
  170. {
  171. return (long)(x * SEMIMINOR * DEGREESTORADIANS);
  172. }
  173. static long
  174. lat_deg_to_mm(double x)
  175. {
  176. return (long)(SEMIMINOR * log(tan((x * DEGREESTORADIANS + M_PI / 2.0) / 2.0)));
  177. }
  178. static time_t
  179. lowranceusr4_get_timestamp(int jd_number, time_t t)
  180. {
  181. int a, b, c, d, e, m;
  182. struct tm* ptm, ntm;
  183. time_t out;
  184. /* get UTC time from time_t */
  185. ptm = gmtime(&t);
  186. memset(&ntm, 0, sizeof(ntm));
  187. ntm.tm_hour = ptm->tm_hour;
  188. ntm.tm_min = ptm->tm_min;
  189. ntm.tm_sec = ptm->tm_sec;
  190. /* convert the JD number to get day/month/year */
  191. a = jd_number + 32044;
  192. b = (4*a + 3) / 146097;
  193. c = a - (146097*b) / 4;
  194. d = (4*c + 3) / 1461;
  195. e = c - (1461*d) / 4;
  196. m = (5*e + 2) / 153;
  197. ntm.tm_mday = e + 1 - (153*m + 2) / 5;
  198. ntm.tm_mon = m + 3 - 12 * (m / 10) - 1;
  199. ntm.tm_year = 100 * b + d - 4800 + m / 10 - 1900;
  200. /* put it all back together into a unix timestamp in UTC */
  201. out = mkgmtime(&ntm);
  202. return out;
  203. }
  204. static int
  205. lowranceusr4_jd_from_timestamp(time_t t)
  206. {
  207. return (int)round((float)t / 86400.0 + 2440587.0);
  208. }
  209. static void
  210. lowranceusr4_copy_fsdata(lowranceusr4_fsdata** dest, lowranceusr4_fsdata* src)
  211. {
  212. *dest = (lowranceusr4_fsdata*)xmalloc(sizeof(*src));
  213. ** dest = *src;
  214. (*dest)->fs.next = NULL;
  215. }
  216. static void
  217. lowranceusr4_free_fsdata(void* fsdata)
  218. {
  219. xfree(fsdata);
  220. }
  221. static
  222. lowranceusr4_fsdata*
  223. lowranceusr4_alloc_fsdata(void)
  224. {
  225. lowranceusr4_fsdata* fsdata = (lowranceusr4_fsdata*) xcalloc(sizeof(*fsdata), 1);
  226. fsdata->fs.type = FS_LOWRANCEUSR4;
  227. fsdata->fs.copy = (fs_copy) lowranceusr4_copy_fsdata;
  228. fsdata->fs.destroy = lowranceusr4_free_fsdata;
  229. fsdata->fs.convert = NULL;
  230. fsdata->uid_unit = 0;
  231. fsdata->uid_seq_low = 0;
  232. fsdata->uid_seq_high = 0;
  233. return fsdata;
  234. }
  235. /* below couple of functions mostly borrowed from raymarine.c */
  236. /* make waypoint shortnames unique */
  237. static char
  238. same_points(const Waypoint* A, const Waypoint* B)
  239. {
  240. return ( /* !!! We are case-sensitive !!! */
  241. (A->shortname == B->shortname) &&
  242. (A->latitude == B->latitude) &&
  243. (A->longitude == B->longitude));
  244. }
  245. static void
  246. register_waypt(const Waypoint* ref)
  247. {
  248. int i;
  249. Waypoint* wpt = (Waypoint*) ref;
  250. for (i = 0; i < waypt_table_ct; i++) {
  251. Waypoint* cmp = waypt_table[i];
  252. if (same_points(wpt, cmp)) {
  253. return;
  254. }
  255. }
  256. if (waypt_table_ct >= waypt_table_sz) {
  257. waypt_table_sz += 32;
  258. if (waypt_table) {
  259. waypt_table = (Waypoint**) xrealloc(waypt_table, waypt_table_sz * sizeof(wpt));
  260. } else {
  261. waypt_table = (Waypoint**) xmalloc(waypt_table_sz * sizeof(wpt));
  262. }
  263. }
  264. if (global_opts.debug_level >= 2) {
  265. printf(MYNAME " adding waypt %s (%s) to table at index %d\n",
  266. qPrintable(wpt->shortname), qPrintable(wpt->description), waypt_table_ct);
  267. }
  268. waypt_table[waypt_table_ct] = (Waypoint*)wpt;
  269. waypt_table_ct++;
  270. }
  271. /* end borrowed from raymarine.c */
  272. static int
  273. lowranceusr4_find_waypt_index(const Waypoint* wpt)
  274. {
  275. int i;
  276. for (i = 0; i < waypt_table_ct; ++i) {
  277. if (same_points(wpt, (const Waypoint*)waypt_table[i])) {
  278. return i;
  279. }
  280. }
  281. return waypt_table_ct+1; /* should never happen */
  282. }
  283. static void
  284. lowranceusr4_parse_waypoints(void)
  285. {
  286. short int icon_num;
  287. unsigned int i, num_waypts, create_date, create_time;
  288. int text_len;
  289. char buff[MAXUSRSTRINGSIZE + 1];
  290. num_waypts = gbfgetint32(file_in);
  291. if (global_opts.debug_level >= 1) {
  292. printf(MYNAME " parse_waypoints: Num waypoints %d\n", num_waypts);
  293. }
  294. for (i = 0; i < num_waypts; ++i) {
  295. Waypoint* wpt_tmp;
  296. wpt_tmp = new Waypoint;
  297. lowranceusr4_fsdata* fsdata = lowranceusr4_alloc_fsdata();
  298. fs_chain_add(&(wpt_tmp->fs), (format_specific_data*) fsdata);
  299. /* read/parse waypoint, with fields as follows (taken mostly
  300. from http://lowranceusrv4togpxconverter.blogspot.com/):
  301. UID unit number - uint32
  302. UID sequence number - int64
  303. Waypt stream version number - uint16
  304. Waypt name length (bytes) - uint32
  305. Waypoint name - utf-16 string w/above length (w->shortname)
  306. Longitude (mercator meters) - int32 (w->longitude)
  307. Latitude (mercator meters) - int32 (w->latitude)
  308. Flags - uint32
  309. Icon ID - uint16 (to w->icon_descr via conversion)
  310. Color ID - uint16
  311. Description length (bytes) - uint32
  312. Description - utf-16 string w/above length (w->description)
  313. Alarm radius - float (w->proximity)
  314. Creation date - uint32 (w->creation_time)
  315. Creation time - uint32 (w->creation_time)
  316. Unused - uint8
  317. Depth (feet) - float (w->depth)
  318. Loran GRI - int32
  319. Loran TdA - int32
  320. Loran TdB - int32
  321. */
  322. /* UID unit number */
  323. fsdata->uid_unit = gbfgetint32(file_in);
  324. /* 64-bit UID sequence number */
  325. fsdata->uid_seq_low = gbfgetint32(file_in);
  326. fsdata->uid_seq_high = gbfgetint32(file_in);
  327. /* Waypt stream version number, discard for now */
  328. gbfgetint16(file_in);
  329. /* Waypoint name; input is 2 bytes per char, we convert to 1 */
  330. text_len = lowranceusr4_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in, 2);
  331. if (text_len) {
  332. buff[text_len] = '\0';
  333. wpt_tmp->shortname = buff;
  334. }
  335. /* Long/Lat */
  336. wpt_tmp->longitude = lon_mm_to_deg(gbfgetint32(file_in));
  337. wpt_tmp->latitude = lat_mm_to_deg(gbfgetint32(file_in));
  338. /* Flags, discard for now */
  339. gbfgetint32(file_in);
  340. /* Icon ID; TODO: need to run this through something like
  341. lowranceusr_find_desc_from_icon_number to convert to a gpsbabel
  342. icon description; however it doesn't seem that the icon ids
  343. used in usr4 match those from usr{2,3} so we need a new
  344. mapping. */
  345. icon_num = gbfgetint16(file_in);
  346. (void) icon_num; // Hush warning for now.
  347. /* wpt_tmp->icon_descr = lowranceusr_find_desc_from_icon_number(icon_num); */
  348. /* Color ID, discard for now */
  349. gbfgetint16(file_in);
  350. /* Waypoint descr; input is 2 bytes per char, we convert to 1 */
  351. text_len = lowranceusr4_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in, 2);
  352. if (text_len) {
  353. buff[text_len] = '\0';
  354. wpt_tmp->description = buff;
  355. }
  356. /* Alarm radius; XXX: I'm not sure what the units are here,
  357. assuming meters but may be feet? */
  358. WAYPT_SET(wpt_tmp, proximity, gbfgetflt(file_in));
  359. /* Creation date/time; the date is a Julian day number, and the
  360. time is a unix timestamp. */
  361. create_date = gbfgetint32(file_in);
  362. create_time = gbfgetint32(file_in);
  363. // Julian date 2440487 is 1/1/1970. If that's the date we're working
  364. // with, as a practical matter, we have no date, so don't even compute
  365. // or set it.
  366. if (create_date > 2440587) {
  367. wpt_tmp->SetCreationTime(lowranceusr4_get_timestamp(create_date,
  368. create_time));
  369. }
  370. /* Unused byte */
  371. gbfgetc(file_in);
  372. /* Depth in feet */
  373. WAYPT_SET(wpt_tmp, depth, FEET_TO_METERS(gbfgetflt(file_in)));
  374. /* Loran data, discard for now */
  375. gbfgetint32(file_in);
  376. gbfgetint32(file_in);
  377. gbfgetint32(file_in);
  378. if (global_opts.debug_level >= 1) {
  379. printf(MYNAME " parse_waypoints: name = %s, uid_unit = %u, "
  380. "uid_seq_low = %d, uid_seq_high = %d, lat = %f, lon = %f, depth = %f\n",
  381. qPrintable(wpt_tmp->shortname), fsdata->uid_unit,
  382. fsdata->uid_seq_low, fsdata->uid_seq_high,
  383. wpt_tmp->latitude, wpt_tmp->longitude, wpt_tmp->depth);
  384. }
  385. waypt_add(wpt_tmp);
  386. }
  387. }
  388. static Waypoint*
  389. lowranceusr4_find_waypt(int uid_unit, int uid_seq_low, int uid_seq_high)
  390. {
  391. #if !NEWQ
  392. queue* elem, *tmp;
  393. #endif
  394. lowranceusr4_fsdata* fs = NULL;
  395. #if NEWQ
  396. // Iterate with waypt_disp_all?
  397. foreach(Waypoint* waypointp, waypt_list) {
  398. #else
  399. QUEUE_FOR_EACH(&waypt_head, elem, tmp) {
  400. Waypoint* waypointp = (Waypoint*) elem;
  401. #endif
  402. fs = (lowranceusr4_fsdata*) fs_chain_find(waypointp->fs, FS_LOWRANCEUSR4);
  403. if (fs && fs->uid_unit == uid_unit &&
  404. fs->uid_seq_low == uid_seq_low &&
  405. fs->uid_seq_high == uid_seq_high) {
  406. return waypointp;
  407. }
  408. }
  409. if (global_opts.debug_level >= 1) {
  410. printf(MYNAME " lowranceusr4_find_waypt: warning, failed finding waypoint with ids %d %d %d\n",
  411. uid_unit, uid_seq_low, uid_seq_high);
  412. }
  413. return NULL;
  414. }
  415. static void
  416. lowranceusr4_parse_routes(void)
  417. {
  418. unsigned int num_routes, i, j, text_len;
  419. unsigned int num_legs;
  420. char buff[MAXUSRSTRINGSIZE + 1];
  421. Waypoint* wpt_tmp;
  422. unsigned int uid_unit, uid_seq_low, uid_seq_high;
  423. num_routes = gbfgetint32(file_in);
  424. if (global_opts.debug_level >= 1) {
  425. printf(MYNAME " parse_routes: Num routes = %d\n", num_routes);
  426. }
  427. for (i = 0; i < num_routes; ++i) {
  428. rte_head = route_head_alloc();
  429. route_add_head(rte_head);
  430. rte_head->rte_num = i+1;
  431. lowranceusr4_fsdata* fsdata = lowranceusr4_alloc_fsdata();
  432. fs_chain_add(&(rte_head->fs), (format_specific_data*) fsdata);
  433. /* read/parse route, with fields as follows (taken mostly
  434. from http://lowranceusrv4togpxconverter.blogspot.com/):
  435. UID unit number - uint32
  436. UID sequence number - int64
  437. Route stream version number - uint16
  438. Route name length (bytes) - uint32
  439. Route name - utf-16 string w/above length (r->rte_name)
  440. Number of waypoints - uint32 (N)
  441. Waypoint list - sequence of N (uint32, uint64) waypoint UIDs
  442. */
  443. /* UID unit number */
  444. fsdata->uid_unit = gbfgetint32(file_in);
  445. /* 64-bit UID sequence number */
  446. fsdata->uid_seq_low = gbfgetint32(file_in);
  447. fsdata->uid_seq_high = gbfgetint32(file_in);
  448. /* Route stream version number, discard for now */
  449. gbfgetint16(file_in);
  450. /* Route name; input is 2 bytes per char, we convert to 1 */
  451. text_len = lowranceusr4_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in, 2);
  452. if (text_len) {
  453. buff[text_len] = '\0';
  454. rte_head->rte_name = buff;
  455. }
  456. num_legs = gbfgetint32(file_in);
  457. if (global_opts.debug_level >= 1) {
  458. printf(MYNAME " parse_routes: route name=%s has %d waypoints\n",
  459. qPrintable(rte_head->rte_name), num_legs);
  460. }
  461. for (j = 0; j < num_legs; ++j) {
  462. uid_unit = gbfgetint32(file_in);
  463. uid_seq_low = gbfgetint32(file_in);
  464. uid_seq_high = gbfgetint32(file_in);
  465. wpt_tmp = lowranceusr4_find_waypt(uid_unit, uid_seq_low, uid_seq_high);
  466. if (wpt_tmp) {
  467. if (global_opts.debug_level >= 2) {
  468. printf(MYNAME " parse_routes: added wpt %s to route %s\n",
  469. qPrintable(wpt_tmp->shortname), qPrintable(rte_head->rte_name));
  470. }
  471. route_add_wpt(rte_head, new Waypoint(*wpt_tmp));
  472. }
  473. }
  474. /* Mystery byte, discard */
  475. gbfgetc(file_in);
  476. }
  477. }
  478. static void
  479. lowranceusr4_parse_trails(void)
  480. {
  481. int num_trails, num_trail_pts, M, i, j, k, trk_num, text_len;
  482. char buff[MAXUSRSTRINGSIZE + 1];
  483. Waypoint* wpt_tmp;
  484. /* num trails */
  485. num_trails = gbfgetint32(file_in);
  486. if (global_opts.debug_level >= 1) {
  487. printf(MYNAME " parse_trails: num trails = %d\n", num_trails);
  488. }
  489. for (i = trk_num = 0; i < num_trails; ++i) {
  490. trk_head = route_head_alloc();
  491. trk_head->rte_num = ++trk_num;
  492. track_add_head(trk_head);
  493. lowranceusr4_fsdata* fsdata = lowranceusr4_alloc_fsdata();
  494. fs_chain_add(&(trk_head->fs), (format_specific_data*) fsdata);
  495. /* read/parse trail, with fields as follows (taken mostly from
  496. http://lowranceusrv4togpxconverter.blogspot.com/):
  497. UID unit number - uint32
  498. UID sequence number - int64
  499. Trail stream version number - uint16
  500. Trail name length (bytes) - uint32
  501. Trail name - utf-16 string w/above length (t->rte_name)
  502. Flags - uint32
  503. Color ID - uint32
  504. Comment length (bytes) - uint32
  505. Comment - utf-16 string w/above length (t->rte_desc)
  506. Creation date - uint32
  507. Creation time - uint32
  508. Unused - uint8
  509. Active flag - uint8
  510. Visible flag - uint8
  511. Data count (?) - uint32
  512. Data type depth (?) - uint8
  513. Data type water temp (?) - uint8
  514. Data type SOG (?) - uint8
  515. Trackpoint count - int32 (N)
  516. Trackpoint list - sequence of N objects as follows:
  517. Unknown (?) - uint16
  518. Unknown (?) - uint8
  519. POSIX timestamp (?) - uint32 (w->creation_time)
  520. Longitude (radians) - double (w->longitude)
  521. Latitude (radians) - double (w->latitude)
  522. Data item count - uint32 (M)
  523. Data items - sequence of M objects as follows:
  524. Unknown (?) - uint8
  525. Unknown (?) - float
  526. */
  527. /* UID unit number */
  528. fsdata->uid_unit = gbfgetint32(file_in);
  529. /* 64-bit UID sequence number */
  530. fsdata->uid_seq_low = gbfgetint32(file_in);
  531. fsdata->uid_seq_high = gbfgetint32(file_in);
  532. /* Trail stream version number, discard for now */
  533. gbfgetint16(file_in);
  534. /* Trail name; input is 2 bytes per char, we convert to 1 */
  535. text_len = lowranceusr4_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in, 2);
  536. if (text_len) {
  537. buff[text_len] = '\0';
  538. trk_head->rte_name = buff;
  539. }
  540. /* Flags, discard for now */
  541. gbfgetint32(file_in);
  542. /* Color ID, discard for now */
  543. gbfgetint32(file_in);
  544. /* Comment/description */
  545. text_len = lowranceusr4_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in, 2);
  546. if (text_len) {
  547. buff[text_len] = '\0';
  548. trk_head->rte_desc = buff;
  549. }
  550. /* Creation date/time, discard for now */
  551. gbfgetint32(file_in);
  552. gbfgetint32(file_in);
  553. /* Some flag bytes, discard for now */
  554. gbfgetc(file_in);
  555. gbfgetc(file_in);
  556. gbfgetc(file_in);
  557. /* Some mysterious "data count" and "data type" stuff, not sure
  558. what it's for, need dox */
  559. gbfgetint32(file_in);
  560. gbfgetc(file_in);
  561. gbfgetc(file_in);
  562. gbfgetc(file_in);
  563. num_trail_pts = gbfgetint32(file_in);
  564. if (global_opts.debug_level >= 1) {
  565. printf(MYNAME " parse_trails: trail %d name=%s has %d trackpoints\n",
  566. trk_num, qPrintable(trk_head->rte_name), num_trail_pts);
  567. }
  568. for (j = 0; j < num_trail_pts; ++j) {
  569. wpt_tmp = new Waypoint;
  570. /* Some unknown bytes */
  571. gbfgetint16(file_in);
  572. gbfgetc(file_in);
  573. /* POSIX timestamp */
  574. wpt_tmp->SetCreationTime(QDateTime::fromTime_t(gbfgetint32(file_in)));
  575. /* Long/Lat */
  576. wpt_tmp->longitude = gbfgetdbl(file_in) / DEGREESTORADIANS; /* rad to deg */
  577. wpt_tmp->latitude = gbfgetdbl(file_in) / DEGREESTORADIANS;
  578. /* Mysterious per-trackpoint data, toss it for now */
  579. M = gbfgetint32(file_in);
  580. for (k = 0; k < M; ++k) {
  581. gbfgetc(file_in);
  582. gbfgetflt(file_in);
  583. }
  584. track_add_wpt(trk_head, wpt_tmp);
  585. if (global_opts.debug_level >= 2) {
  586. printf(MYNAME " parse_routes: added trackpoint %f,%f to route %s\n",
  587. wpt_tmp->latitude, wpt_tmp->longitude, qPrintable(trk_head->rte_name));
  588. }
  589. }
  590. }
  591. }
  592. static void
  593. data_read(void)
  594. {
  595. short int MajorVersion, MinorVersion;
  596. int text_len, DataStreamVersion;
  597. unsigned int create_date, create_time, serial_num;
  598. unsigned char byte;
  599. char buff[MAXUSRSTRINGSIZE + 1];
  600. MajorVersion = gbfgetint16(file_in);
  601. reading_version = MajorVersion;
  602. MinorVersion = gbfgetint16(file_in);
  603. DataStreamVersion = gbfgetint32(file_in);
  604. if (global_opts.debug_level >= 1) {
  605. printf(MYNAME " data_read: Major Version %d Minor Version %d Data Stream Version %d\n",
  606. MajorVersion, MinorVersion, DataStreamVersion);
  607. }
  608. if (MajorVersion != 4) {
  609. fatal(MYNAME ": input file is from an unsupported version of the USR format (must be version 4)\n");
  610. }
  611. text_len = lowranceusr4_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in, 1);
  612. if (text_len > 0 && global_opts.debug_level >= 1) {
  613. buff[text_len] = '\0';
  614. printf(MYNAME " file title: %s\n", buff);
  615. }
  616. text_len = lowranceusr4_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in, 1);
  617. if (text_len > 0 && global_opts.debug_level >= 1) {
  618. buff[text_len] = '\0';
  619. printf(MYNAME " date string: %s\n", buff);
  620. }
  621. /* for now we won't use these for anything */
  622. create_date = gbfgetint32(file_in);
  623. (void) create_date;
  624. create_time = gbfgetint32(file_in);
  625. (void) create_time;
  626. byte = gbfgetc(file_in); /* unused, apparently */
  627. (void) byte;
  628. serial_num = gbfgetint32(file_in);
  629. if (global_opts.debug_level >= 1) {
  630. printf(MYNAME " device serial number %u\n", (unsigned int)serial_num);
  631. }
  632. text_len = lowranceusr4_readstr(&buff[0], MAXUSRSTRINGSIZE, file_in, 1);
  633. if (text_len > 0 && global_opts.debug_level >= 1) {
  634. buff[text_len] = '\0';
  635. printf(MYNAME " content description: %s\n", buff);
  636. }
  637. lowranceusr4_parse_waypoints();
  638. lowranceusr4_parse_routes();
  639. lowranceusr4_parse_trails();
  640. }
  641. static void
  642. lowranceusr4_waypt_disp(const Waypoint* wpt)
  643. {
  644. /* UID unit number */
  645. gbfputint32(opt_serialnum_i, file_out);
  646. /* 64-bit UID sequence number */
  647. gbfputint32(waypt_uid++, file_out);
  648. gbfputint32(0, file_out);
  649. /* Waypt stream version number: this always seems to be 2 in my data
  650. so that's what I'll use */
  651. gbfputint16(2, file_out);
  652. /* Waypt name */
  653. lowranceusr4_writestr(wpt->shortname, file_out, 2);
  654. /* Long/Lat */
  655. gbfputint32(lon_deg_to_mm(wpt->longitude), file_out);
  656. gbfputint32(lat_deg_to_mm(wpt->latitude), file_out);
  657. /* Flags: this always seems to be 2 or 4 in my data, not sure what
  658. it means */
  659. gbfputint32(2, file_out);
  660. /* Icon ID; TODO: need to invert icon description to an icon number,
  661. see parse_waypoints above */
  662. gbfputint16(0, file_out);
  663. /* Color ID */
  664. gbfputint16(0, file_out);
  665. /* Waypt description */
  666. lowranceusr4_writestr(wpt->description, file_out, 2);
  667. /* Alarm radius */
  668. gbfputflt(WAYPT_GET(wpt, proximity, 0.0), file_out);
  669. /* Creation date/time */
  670. gbfputint32(lowranceusr4_jd_from_timestamp(wpt->GetCreationTime().toTime_t()), file_out);
  671. gbfputint32(wpt->GetCreationTime().toTime_t(), file_out);
  672. /* Unused byte */
  673. gbfputc(0, file_out);
  674. /* Depth in feet */
  675. gbfputflt(METERS_TO_FEET(WAYPT_GET(wpt, depth, 0.0)), file_out);
  676. /* Loran data */
  677. gbfputint32(0, file_out);
  678. gbfputint32(0, file_out);
  679. gbfputint32(0, file_out);
  680. }
  681. static void
  682. lowranceusr4_write_waypoints(void)
  683. {
  684. int i;
  685. /* enumerate all waypoints from both the plain old waypoint list and
  686. also all routes */
  687. waypt_table_sz = 0;
  688. waypt_table_ct = 0;
  689. waypt_table = NULL;
  690. waypt_disp_all(register_waypt);
  691. route_disp_all(NULL, NULL, register_waypt);
  692. if (global_opts.debug_level >= 1) {
  693. printf(MYNAME " writing %d waypoints\n", waypt_table_ct);
  694. }
  695. gbfputint32(waypt_table_ct, file_out);
  696. waypt_uid = 0;
  697. for (i = 0; i < waypt_table_ct; ++i) {
  698. if (global_opts.debug_level >= 2) {
  699. printf(MYNAME " writing out waypt %d (%s - %s)\n",
  700. i, qPrintable(waypt_table[i]->shortname), qPrintable(waypt_table[i]->description));
  701. }
  702. lowranceusr4_waypt_disp((const Waypoint*)waypt_table[i]);
  703. }
  704. }
  705. static void
  706. lowranceusr4_write_route_hdr(const route_head* rte)
  707. {
  708. if (global_opts.debug_level >= 1) {
  709. printf(MYNAME " writing route #%d (%s) with %d waypts\n",
  710. route_uid, qPrintable(rte->rte_name), rte->rte_waypt_ct);
  711. }
  712. /* UID unit number */
  713. gbfputint32(opt_serialnum_i, file_out);
  714. /* 64-bit UID sequence number */
  715. gbfputint32(route_uid++, file_out);
  716. gbfputint32(0, file_out);
  717. /* Route stream version number: seems to be 1 in my data */
  718. gbfputint16(1, file_out);
  719. /* Waypt name */
  720. lowranceusr4_writestr(rte->rte_name, file_out, 2);
  721. /* Num waypoints */
  722. gbfputint32(rte->rte_waypt_ct, file_out);
  723. }
  724. static void
  725. lowranceusr4_write_wpt_uids(const Waypoint* wpt)
  726. {
  727. int waypt_idx;
  728. /* find the index of wpt in our table */
  729. waypt_idx = lowranceusr4_find_waypt_index(wpt);
  730. if (global_opts.debug_level >= 2) {
  731. if (waypt_idx > waypt_table_ct) {
  732. printf(MYNAME " WARNING: failed finding waypoint %s in waypoint table\n",
  733. qPrintable(wpt->shortname));
  734. } else {
  735. printf(MYNAME " adding waypt %d (%s) to route\n",
  736. waypt_idx, qPrintable(waypt_table[waypt_idx]->shortname));
  737. }
  738. }
  739. gbfputint32(opt_serialnum_i, file_out);
  740. gbfputint32(waypt_idx, file_out);
  741. gbfputint32(0, file_out);
  742. }
  743. static void
  744. lowranceusr4_write_route_trl(const route_head* rte)
  745. {
  746. /* Mystery byte */
  747. gbfputc(0, file_out);
  748. }
  749. static void
  750. lowranceusr4_write_routes(void)
  751. {
  752. if (global_opts.debug_level >= 1) {
  753. printf(MYNAME " writing %d routes\n", route_count());
  754. }
  755. gbfputint32(route_count(), file_out);
  756. route_uid = 0;
  757. route_disp_all(lowranceusr4_write_route_hdr,
  758. lowranceusr4_write_route_trl,
  759. lowranceusr4_write_wpt_uids);
  760. }
  761. static void
  762. lowranceusr4_write_track_hdr(const route_head* trk)
  763. {
  764. if (global_opts.debug_level >= 1) {
  765. printf(MYNAME " writing track %d (%s) with %d trackpoints\n",
  766. track_uid, qPrintable(trk->rte_name), trk->rte_waypt_ct);
  767. }
  768. /* UID unit number */
  769. gbfputint32(opt_serialnum_i, file_out);
  770. /* 64-bit UID sequence number */
  771. gbfputint32(track_uid++, file_out);
  772. gbfputint32(0, file_out);
  773. /* Route stream version number: always seems to be 3 in my data */
  774. gbfputint16(3, file_out);
  775. /* Track name */
  776. lowranceusr4_writestr(trk->rte_name, file_out, 2);
  777. /* Flags: always seems to be 2 in my data */
  778. gbfputint32(2, file_out);
  779. /* Color ID */
  780. gbfputint32(0, file_out);
  781. /* Comment */
  782. lowranceusr4_writestr(trk->rte_desc, file_out, 2);
  783. /* Creation date/time */
  784. gbfputint32(0, file_out);
  785. gbfputint32(0, file_out);
  786. /* Unused byte */
  787. gbfputc(0, file_out);
  788. /* Active flag */
  789. gbfputc(0, file_out);
  790. /* Visible flag; I'll just assume all tracks should be visible for
  791. now */
  792. gbfputc(1, file_out);
  793. /* Mysterious "data count" and "data type" stuff */
  794. gbfputint32(0, file_out);
  795. gbfputc(0, file_out);
  796. gbfputc(0, file_out);
  797. gbfputc(0, file_out);
  798. /* Trackpoint count */
  799. gbfputint32(trk->rte_waypt_ct, file_out);
  800. }
  801. static void
  802. lowranceusr4_write_track_waypt(const Waypoint* wpt)
  803. {
  804. /* Some unknown bytes */
  805. gbfputint16(0, file_out);
  806. gbfputc(0, file_out);
  807. /* Timestamp */
  808. gbfputint32(wpt->GetCreationTime().toTime_t(), file_out);
  809. /* Long/Lat */
  810. gbfputdbl(wpt->longitude * DEGREESTORADIANS, file_out);
  811. gbfputdbl(wpt->latitude * DEGREESTORADIANS, file_out);
  812. /* Mysterious per-trackpoint data; we'll just say there are "0"
  813. mystery entries */
  814. gbfputint32(0, file_out);
  815. }
  816. static void
  817. lowranceusr4_write_trails(void)
  818. {
  819. if (global_opts.debug_level >= 1) {
  820. printf(MYNAME " writing %d tracks\n", track_count());
  821. }
  822. gbfputint32(track_count(), file_out);
  823. track_uid = 0;
  824. track_disp_all(lowranceusr4_write_track_hdr, NULL, lowranceusr4_write_track_waypt);
  825. }
  826. static void
  827. data_write(void)
  828. {
  829. short int MajorVersion, MinorVersion;
  830. int DataStreamVersion;
  831. time_t now;
  832. struct tm* now_tm;
  833. char buf[256];
  834. setshort_length(mkshort_handle, 15);
  835. MajorVersion = 4;
  836. MinorVersion = 0;
  837. DataStreamVersion = 10;
  838. gbfputint16(MajorVersion, file_out);
  839. gbfputint16(MinorVersion, file_out);
  840. gbfputint32(DataStreamVersion, file_out);
  841. /* file title */
  842. lowranceusr4_writestr(opt_title, file_out, 1);
  843. /* date string */
  844. now = time(NULL);
  845. now_tm = gmtime(&now);
  846. sprintf(buf, "%d/%d/%d", now_tm->tm_mon+1, now_tm->tm_mday, now_tm->tm_year+1900);
  847. lowranceusr4_writestr(buf, file_out, 1);
  848. /* creation date/time */
  849. gbfputint32(lowranceusr4_jd_from_timestamp(now), file_out);
  850. gbfputint32(now, file_out);
  851. /* unused byte */
  852. gbfputc(0, file_out);
  853. /* device serial number */
  854. opt_serialnum_i = atoi(opt_serialnum);
  855. gbfputint32(opt_serialnum_i, file_out);
  856. /* content description */
  857. lowranceusr4_writestr(opt_content_descr, file_out, 1);
  858. lowranceusr4_write_waypoints();
  859. lowranceusr4_write_routes();
  860. lowranceusr4_write_trails();
  861. }
  862. ff_vecs_t lowranceusr4_vecs = {
  863. ff_type_file,
  864. FF_CAP_RW_ALL,
  865. rd_init,
  866. wr_init,
  867. rd_deinit,
  868. wr_deinit,
  869. data_read,
  870. data_write,
  871. NULL,
  872. lowranceusr4_args,
  873. CET_CHARSET_ASCII, 0 /* CET-REVIEW */
  874. };