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.

raymarine.cc 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /*
  2. Support for Raymarine Waypoint File (.rwf).
  3. Copyright (C) 2006,2007 Olaf Klein, o.b.klein@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. /*
  17. Known format limits:
  18. Waypoint name: max. 16 characters
  19. Route name: max. 16 characters
  20. Routes: max. 50 waypoints per route
  21. ???: the character set may be only a subset of std. ASCII
  22. History:
  23. 2006/10/30: Initial release (not yet in GPSBabel source tree)
  24. 2006/11/08:
  25. 2007/03/17: Remove GUIDs from writer (not really valid)
  26. Fix "PredictedTwa" output
  27. Initialize location with "My Waypoints"
  28. Change default value for RcCount and RelSet (now 0)
  29. 2007/04/18: Limit route names also to 16 characters
  30. Bug-fix - add missing comma (write_route_wpt_cb/items)
  31. Change line feeds to fixed CRLF
  32. Sort waypoints by name (not really needed, but nice)
  33. Add some MapSource icon names to icon mappings
  34. Remove unused id from icon table
  35. */
  36. #include "defs.h"
  37. #include "cet_util.h"
  38. #include "csv_util.h"
  39. #include "inifile.h"
  40. #include <ctype.h>
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. typedef unsigned long long guid_t;
  44. static inifile_t* fin;
  45. static gbfile* fout;
  46. static Waypoint** waypt_table;
  47. static short_handle hshort_wpt, hshort_rte;
  48. static int waypt_table_sz, waypt_table_ct;
  49. static int rte_index, rte_wpt_index;
  50. static char* opt_location;
  51. #define MYNAME "raymarine"
  52. static
  53. arglist_t raymarine_args[] = {
  54. { "location", &opt_location, "Default location", "My Waypoints", ARGTYPE_STRING, ARG_NOMINMAX },
  55. ARG_TERMINATOR
  56. };
  57. /* from csv_util.c: convert excel time (days since 1900) to time_t and back again */
  58. #define EXCEL_TO_TIMET(a) ((a - 25569.0) * 86400.0)
  59. #define TIMET_TO_EXCEL(a) ((a / 86400.0) + 25569.0)
  60. #define LINE_FEED "\r\n"
  61. /* Bitmaps */
  62. typedef struct {
  63. const char* name;
  64. const char* mps_name;
  65. } raymarine_symbol_mapping_t;
  66. static raymarine_symbol_mapping_t raymarine_symbols[] = {
  67. { /* 0 */ "Unknown Symbol 0" },
  68. { /* 1 */ "Unknown Symbol 1" },
  69. { /* 2 */ "Unknown Symbol 2" },
  70. { /* 3 */ "Red Square" },
  71. { /* 4 */ "Big Fish" },
  72. { /* 5 */ "Anchor" },
  73. { /* 6 */ "Smiley", "Contact, Smiley" },
  74. { /* 7 */ "Sad" },
  75. { /* 8 */ "Red Button", "Navaid, Red" },
  76. { /* 9 */ "Sailfish" },
  77. { /* 10 */ "Danger", "Skull and Crossbones" },
  78. { /* 11 */ "Attention" },
  79. { /* 12 */ "Black Square" },
  80. { /* 13 */ "Intl. Dive Flag", "Diver Down Flag 2" },
  81. { /* 14 */ "Vessel", "Marina" },
  82. { /* 15 */ "Lobster" },
  83. { /* 16 */ "Buoy", "Buoy, White" },
  84. { /* 17 */ "Exclamation" },
  85. { /* 18 */ "Red X" },
  86. { /* 19 */ "Check Mark" },
  87. { /* 20 */ "Black Plus" },
  88. { /* 21 */ "Black Cross" },
  89. { /* 22 */ "MOB" },
  90. { /* 23 */ "Billfish" },
  91. { /* 24 */ "Bottom Mark" },
  92. { /* 25 */ "Circle", "Circle, Red" },
  93. { /* 26 */ "Diamond", "Block, Red" },
  94. { /* 27 */ "Diamond Quarters", "Diamond, Red" },
  95. { /* 28 */ "U.S. Dive Flag", "Diver Down Flag 1" },
  96. { /* 29 */ "Dolphin" },
  97. { /* 30 */ "Few Fish" },
  98. { /* 31 */ "Multiple Fish" },
  99. { /* 32 */ "Many Fish" },
  100. { /* 33 */ "Single Fish" },
  101. { /* 34 */ "Small Fish" },
  102. { /* 35 */ "Marker" },
  103. { /* 36 */ "Cocktails", "Bar" },
  104. { /* 37 */ "Red Box Marker" },
  105. { /* 38 */ "Reef" },
  106. { /* 39 */ "Rocks" },
  107. { /* 40 */ "Fish School" },
  108. { /* 41 */ "Seaweed", "Weed Bed" },
  109. { /* 42 */ "Shark" },
  110. { /* 43 */ "Sportfisher" },
  111. { /* 44 */ "Swimmer", "Swimming Area" },
  112. { /* 45 */ "Top Mark" },
  113. { /* 46 */ "Trawler" },
  114. { /* 47 */ "Tree" },
  115. { /* 48 */ "Triangle", "Triangle, Red" },
  116. { /* 49 */ "Wreck", "Shipwreck" }
  117. };
  118. #define RAYMARINE_SYMBOL_CT sizeof(raymarine_symbols) / sizeof(raymarine_symbol_mapping_t)
  119. #define RAYMARINE_STD_SYMBOL 3
  120. static int
  121. find_symbol_num(const QString& descr)
  122. {
  123. if (!descr.isNull()) {
  124. raymarine_symbol_mapping_t* a;
  125. a = &raymarine_symbols[0];
  126. for (unsigned int i = 0; i < RAYMARINE_SYMBOL_CT; i++, a++) {
  127. if (descr.compare(a->name, Qt::CaseInsensitive) == 0) {
  128. return i;
  129. }
  130. if (a->mps_name && (descr.compare(a->mps_name, Qt::CaseInsensitive) == 0)) {
  131. return i;
  132. }
  133. }
  134. }
  135. return RAYMARINE_STD_SYMBOL;
  136. }
  137. /* ============================================= */
  138. /* %%% R A Y M A R I N E R E A D E R %%% */
  139. /* ============================================= */
  140. static void
  141. raymarine_rd_init(const QString& fname)
  142. {
  143. fin = inifile_init(qPrintable(fname), MYNAME);
  144. if (fin->unicode) {
  145. cet_convert_init(CET_CHARSET_UTF8, 1);
  146. }
  147. }
  148. static void
  149. raymarine_rd_done(void)
  150. {
  151. inifile_done(fin);
  152. }
  153. static void
  154. raymarine_read(void)
  155. {
  156. Waypoint* wpt;
  157. unsigned int ix;
  158. unsigned int rx;
  159. /* Read all waypoints */
  160. for (ix = 0; ix < 0x3FFF; ix++) {
  161. char sect[10];
  162. char* str, *name, *lat, *lon;
  163. /* built section identifier */
  164. snprintf(sect, sizeof(sect), "Wp%d", ix);
  165. /* try to read our most expected values */
  166. if (NULL == (name = inifile_readstr(fin, sect, "Name"))) {
  167. break;
  168. }
  169. if (NULL == (lat = inifile_readstr(fin, sect, "Lat"))) {
  170. break;
  171. }
  172. if (NULL == (lon = inifile_readstr(fin, sect, "Long"))) {
  173. break;
  174. }
  175. wpt = new Waypoint;
  176. wpt->shortname = name;
  177. wpt->latitude = atof(lat);
  178. wpt->longitude = atof(lon);
  179. waypt_add(wpt);
  180. /* try to read optional values */
  181. if (((str = inifile_readstr(fin, sect, "Notes"))) && *str) {
  182. wpt->notes = str;
  183. }
  184. if (((str = inifile_readstr(fin, sect, "Time"))) && *str) {
  185. wpt->SetCreationTime(EXCEL_TO_TIMET(atof(str)));
  186. }
  187. if (((str = inifile_readstr(fin, sect, "Bmp"))) && *str) {
  188. unsigned int symbol = atoi(str);
  189. if ((symbol < 3) && (symbol >= RAYMARINE_SYMBOL_CT)) {
  190. symbol = RAYMARINE_STD_SYMBOL;
  191. }
  192. wpt->icon_descr = raymarine_symbols[symbol].name;
  193. }
  194. }
  195. /* Read all routes */
  196. for (rx = 0; rx < 0x3FFF; rx++) {
  197. char sect[10];
  198. char* name;
  199. route_head* rte;
  200. int wx;
  201. snprintf(sect, sizeof(sect), "Rt%d", rx);
  202. if (NULL == (name = inifile_readstr(fin, sect, "Name"))) {
  203. break;
  204. }
  205. rte = route_head_alloc();
  206. rte->rte_name = name;
  207. route_add_head(rte);
  208. for (wx = 0; wx < 0x3FFF; wx++) {
  209. char buff[32];
  210. char* str;
  211. Waypoint* wpt;
  212. snprintf(buff, sizeof(buff), "Mk%d", wx);
  213. str = inifile_readstr(fin, sect, buff);
  214. if ((str == NULL) || (*str == '\0')) {
  215. break;
  216. }
  217. wpt = find_waypt_by_name(str);
  218. if (wpt == NULL)
  219. fatal(MYNAME ": No associated waypoint for route point %s (Route %s)!\n",
  220. str, qPrintable(rte->rte_name));
  221. route_add_wpt(rte, new Waypoint(*wpt));
  222. }
  223. }
  224. }
  225. /* ============================================= */
  226. /* %%% R A Y M A R I N E W R I T E R %%% */
  227. /* ============================================= */
  228. /* make waypoint shortnames unique */
  229. static char
  230. same_points(const Waypoint* A, const Waypoint* B)
  231. {
  232. return ( /* !!! We are case-sensitive !!! */
  233. (A->shortname == B->shortname) &&
  234. (A->latitude == B->latitude) &&
  235. (A->longitude == B->longitude));
  236. }
  237. static void
  238. register_waypt(const Waypoint* ref, const char is_rtept)
  239. {
  240. int i;
  241. Waypoint* wpt = (Waypoint*) ref;
  242. for (i = 0; i < waypt_table_ct; i++) {
  243. Waypoint* cmp = waypt_table[i];
  244. if (same_points(wpt, cmp)) {
  245. wpt->extra_data = cmp->extra_data;
  246. return;
  247. }
  248. }
  249. if (waypt_table_ct >= waypt_table_sz) {
  250. waypt_table_sz += 32;
  251. if (waypt_table) {
  252. waypt_table = (Waypoint**) xrealloc(waypt_table, waypt_table_sz * sizeof(wpt));
  253. } else {
  254. waypt_table = (Waypoint**) xmalloc(waypt_table_sz * sizeof(wpt));
  255. }
  256. }
  257. wpt->extra_data = (void*)mkshort(hshort_wpt, CSTRc(wpt->shortname));
  258. waypt_table[waypt_table_ct] = (Waypoint*)wpt;
  259. waypt_table_ct++;
  260. }
  261. static void
  262. enum_waypt_cb(const Waypoint* wpt)
  263. {
  264. register_waypt((Waypoint*) wpt, 0);
  265. }
  266. static void
  267. enum_rtept_cb(const Waypoint* wpt)
  268. {
  269. register_waypt((Waypoint*) wpt, 1);
  270. }
  271. static int
  272. qsort_cb(const void* a, const void* b)
  273. {
  274. const Waypoint* wa = *(Waypoint**)a;
  275. const Waypoint* wb = *(Waypoint**)b;
  276. return wa->shortname.compare(wb->shortname);
  277. }
  278. static void
  279. write_waypoint(gbfile* fout, const Waypoint* wpt, const int waypt_no, const char* location)
  280. {
  281. QString notes;
  282. char* name;
  283. double time;
  284. notes = wpt->notes;
  285. if (notes == NULL) {
  286. notes = wpt->description;
  287. if (notes == NULL) {
  288. notes = "";
  289. }
  290. }
  291. notes = csv_stringclean(notes, LINE_FEED);
  292. time = wpt->creation_time.isValid() ? TIMET_TO_EXCEL(wpt->GetCreationTime().toTime_t()) : TIMET_TO_EXCEL(gpsbabel_time);
  293. name = (char*)wpt->extra_data;
  294. gbfprintf(fout, "[Wp%d]" LINE_FEED
  295. "Loc=%s" LINE_FEED
  296. "Name=%s" LINE_FEED
  297. "Lat=%.15f" LINE_FEED
  298. "Long=%.15f" LINE_FEED,
  299. waypt_no, location, name, wpt->latitude, wpt->longitude
  300. );
  301. gbfprintf(fout, "Rng=%.15f" LINE_FEED
  302. "Bear=%.15f" LINE_FEED
  303. "Bmp=%d" LINE_FEED
  304. "Fixed=1" LINE_FEED
  305. "Locked=0" LINE_FEED
  306. "Notes=%s" LINE_FEED,
  307. 0.0, 0.0,
  308. find_symbol_num(wpt->icon_descr),
  309. CSTR(notes)
  310. );
  311. gbfprintf(fout, "Rel=" LINE_FEED
  312. "RelSet=0" LINE_FEED
  313. "RcCount=0" LINE_FEED
  314. "RcRadius=%.15f" LINE_FEED
  315. "Show=1" LINE_FEED
  316. "RcShow=0" LINE_FEED
  317. "SeaTemp=%.15f" LINE_FEED
  318. "Depth=%.15f" LINE_FEED
  319. "Time=%.10f00000" LINE_FEED,
  320. 0.0, -32678.0, 65535.0, time
  321. );
  322. }
  323. static void
  324. write_route_head_cb(const route_head* rte)
  325. {
  326. QString name;
  327. name = rte->rte_name;
  328. if (name.isEmpty()) {
  329. name=QString("Route%1").arg(rte_index);
  330. }
  331. name = mkshort(hshort_rte, name);
  332. gbfprintf(fout, "[Rt%d]" LINE_FEED
  333. "Name=%s" LINE_FEED
  334. "Visible=1" LINE_FEED,
  335. rte_index,
  336. CSTR(name)
  337. );
  338. rte_index++;
  339. rte_wpt_index = 0;
  340. }
  341. static void
  342. write_route_wpt_cb(const Waypoint* wpt)
  343. {
  344. static const char* items[] = {
  345. "Cog",
  346. "Eta",
  347. "Length",
  348. "PredictedDrift",
  349. "PredictedSet",
  350. "PredictedSog",
  351. "PredictedTime",
  352. "PredictedTwa",
  353. "PredictedTwd",
  354. "PredictedTws"
  355. };
  356. gbfprintf(fout, "Mk%d=%s" LINE_FEED, rte_wpt_index, (char*)wpt->extra_data);
  357. for (unsigned i = 0; i < sizeof(items) / sizeof(char*); i++) {
  358. gbfprintf(fout, "%s%d=%.15f" LINE_FEED, items[i], rte_wpt_index, 0.0);
  359. }
  360. rte_wpt_index++;
  361. return;
  362. }
  363. static void
  364. enum_route_hdr_cb(const route_head* rte)
  365. {
  366. is_fatal(rte->rte_waypt_ct > 50,
  367. MYNAME ": Routes with more than 50 points are not supported by Raymarine!");
  368. }
  369. static short_handle
  370. raymarine_new_short_handle(void)
  371. {
  372. short_handle res;
  373. res = mkshort_new_handle();
  374. setshort_length(res, 16);
  375. setshort_badchars(res, ",");
  376. setshort_mustupper(res, 0);
  377. setshort_mustuniq(res, 1);
  378. setshort_whitespace_ok(res, 1);
  379. setshort_repeating_whitespace_ok(res, 1);
  380. return res;
  381. }
  382. static void
  383. raymarine_wr_init(const QString& fname)
  384. {
  385. fout = gbfopen(fname, "wb", MYNAME);
  386. hshort_wpt = raymarine_new_short_handle();
  387. hshort_rte = raymarine_new_short_handle();
  388. }
  389. static void
  390. raymarine_wr_done(void)
  391. {
  392. mkshort_del_handle(&hshort_wpt);
  393. mkshort_del_handle(&hshort_rte);
  394. gbfclose(fout);
  395. }
  396. static void
  397. raymarine_write(void)
  398. {
  399. int i;
  400. Waypoint* wpt;
  401. waypt_table_sz = 0;
  402. waypt_table_ct = 0;
  403. waypt_table = NULL;
  404. /* enumerate all possible waypoints */
  405. waypt_disp_all(enum_waypt_cb);
  406. route_disp_all(enum_route_hdr_cb, NULL, enum_rtept_cb);
  407. if (waypt_table_ct == 0) {
  408. return;
  409. }
  410. qsort(waypt_table, waypt_table_ct, sizeof(*waypt_table), qsort_cb);
  411. /* write out waypoint summary */
  412. for (i = 0; i < waypt_table_ct; i++) {
  413. Waypoint* wpt = waypt_table[i];
  414. write_waypoint(fout, wpt, i, opt_location);
  415. }
  416. /* write out all routes with their waypoints */
  417. rte_index = 0;
  418. route_disp_all(write_route_head_cb, NULL, write_route_wpt_cb);
  419. /* release local used data */
  420. for (i = 0; i < waypt_table_ct; i++) {
  421. wpt = waypt_table[i];
  422. xfree(wpt->extra_data);
  423. wpt->extra_data = NULL;
  424. }
  425. xfree(waypt_table);
  426. }
  427. /* ================================================== */
  428. /* %%% M O D U L E R E G I S T R A T I O N %%% */
  429. /* ================================================== */
  430. ff_vecs_t raymarine_vecs = {
  431. ff_type_file,
  432. {
  433. (ff_cap)(ff_cap_read | ff_cap_write) /* waypoints */,
  434. ff_cap_none /* tracks */,
  435. (ff_cap)(ff_cap_read | ff_cap_write) /* routes */,
  436. },
  437. raymarine_rd_init,
  438. raymarine_wr_init,
  439. raymarine_rd_done,
  440. raymarine_wr_done,
  441. raymarine_read,
  442. raymarine_write,
  443. NULL,
  444. raymarine_args,
  445. CET_CHARSET_ASCII, 0 /* should we force this to 1 ? */
  446. };