From: Robert Lipe Date: Wed, 28 Dec 2022 22:37:07 +0000 (-0600) Subject: Obsolete Magellan serial and SD formats (#961) X-Git-Tag: archive/raspbian/1.10.0+ds-2+rpi1~1^2~12^2~1^2~150 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=8df666b69969931f80f16f963d322b632134dc08;p=gpsbabel.git Obsolete Magellan serial and SD formats (#961) * Remove Magellan serial and SD formats. Two different polls (one on the list, one in groundspeak forums) have confirmed disuse. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index caecf0c76..6a985892d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,14 +67,12 @@ set(RESOURCES gpsbabel.qrc) # MINIMAL_FMTS set(MINIMAL_FMTS - explorist_ini.cc garmin.cc garmin_device_xml.cc garmin_tables.cc geo.cc gpx.cc kml.cc - magproto.cc nmea.cc wbt-200.cc ) @@ -209,7 +207,6 @@ set(HEADERS defs.h dg-100.h exif.h - explorist_ini.h filter.h filter_vecs.h format.h @@ -237,7 +234,6 @@ set(HEADERS kml.h legacyformat.h lowranceusr.h - magellan.h navilink.h nmea.h osm.h @@ -376,7 +372,6 @@ set(TESTS arc batch bend - classic-1 classic-2 classic-3 dg100 @@ -408,8 +403,6 @@ set(TESTS kml-read kml lowranceusr - magellan_sd - magellan mtk multiurlgpx navilink diff --git a/deprecated/explorist_ini.cc b/deprecated/explorist_ini.cc new file mode 100644 index 000000000..e9eafdb35 --- /dev/null +++ b/deprecated/explorist_ini.cc @@ -0,0 +1,74 @@ +#include "defs.h" +#include "explorist_ini.h" +#include "inifile.h" + +static inifile_t* inifile; +static const char myname[] = "explorist"; + +#ifdef DEAD_CODE_IS_REBORN +static const char* +explorist_read_value(const char* section, const char* key) +{ + return inifile_readstr(inifile, section, key); +} +#endif + +static mag_info* +explorist_ini_try(const char* path) +{ + char* inipath; + + xasprintf(&inipath, "%s/%s", path, "APP/Atlas.ini"); + inifile = inifile_init(QString::fromUtf8(inipath), myname); + if (!inifile) { + xfree(inipath); + return nullptr; + } + + auto* info = (mag_info*) xmalloc(sizeof(mag_info)); + info->geo_path = nullptr; + info->track_path = nullptr; + info->waypoint_path = nullptr; + + QString s = inifile_readstr(inifile, "UGDS", "WpFolder"); + if (!s.isNull()) { + s.replace('\\', '/'); + xasprintf(&info->waypoint_path, "%s/%s", path, CSTR(s)); + } + s = inifile_readstr(inifile, "UGDS", "GcFolder"); + if (!s.isNull()) { + s.replace('\\', '/'); + xasprintf(&info->geo_path, "%s/%s", path, CSTR(s)); + } + s = inifile_readstr(inifile, "UGDS", "TrkFolder"); + if (!s.isNull()) { + s.replace('\\', '/'); + xasprintf(&info->track_path, "%s/%s", path, CSTR(s)); + } + + inifile_done(inifile); + xfree(inipath); + return info; +} + +mag_info* +explorist_ini_get(const char** dirlist) +{ + mag_info* r = nullptr; + while (dirlist && *dirlist) { + r = explorist_ini_try(*dirlist); + if (r) { + return r; + } + } + return r; +} + +void +explorist_ini_done(mag_info* info) +{ + xfree(info->geo_path); + xfree(info->track_path); + xfree(info->waypoint_path); + xfree(info); +} diff --git a/deprecated/explorist_ini.h b/deprecated/explorist_ini.h new file mode 100644 index 000000000..078c0dc83 --- /dev/null +++ b/deprecated/explorist_ini.h @@ -0,0 +1,12 @@ + +/* + * Interesting traits of the device from the *.ini files. + */ +struct mag_info { + char* geo_path; + char* track_path; + char* waypoint_path; +}; + +mag_info* explorist_ini_get(const char** directory_list); +void explorist_ini_done(mag_info* info); diff --git a/deprecated/magellan.h b/deprecated/magellan.h new file mode 100644 index 000000000..67f062625 --- /dev/null +++ b/deprecated/magellan.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2002-2005 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + +#ifndef MAGELLAN_H_INCLUDED_ +#define MAGELLAN_H_INCLUDED_ + +#include +#include "defs.h" + +/* + * Table of "interesting" Magellan models. + * Selfishly, if I haven't heard of it, it's not in the table. + * This doesn't mean I actually have TRIED all models listed below. + * (Donations welcome. :-) + */ +enum meridian_model { + mm_unknown = 0 , + mm_gps315320, + mm_map410, + mm_map330, + mm_gps310, + mm_meridian, + mm_sportrak +}; + +struct pid_to_model_t { + meridian_model model; + int pid; + const char* model_n; +}; + +struct magellan_icon_mapping_t { + const char* token; + const char* icon; +}; + +QString mag_find_descr_from_token(const char* token); +QString mag_find_token_from_descr(const QString& icon); + +unsigned int mag_checksum(const char*buf); +QString m330_cleanse(const char* istring); + +Waypoint* mag_trkparse(char* trkmsg); +void mag_rteparse(char* rtemsg); + +#endif // MAGELLAN_H_INCLUDED_ diff --git a/deprecated/magproto.cc b/deprecated/magproto.cc new file mode 100644 index 000000000..b52becae6 --- /dev/null +++ b/deprecated/magproto.cc @@ -0,0 +1,1631 @@ +/* + Communicate Thales/Magellan serial protocol. + + Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, + 2008, 2010 Robert Lipe, robertlipe+source@gpsbabel.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + +#include // for assert +#include // for isprint, toupper +#include // for fabs, lround +#include // for sscanf, size_t +#include // for strtod, strtoul +#include // for strchr, strncmp, strlen, memmove, strrchr, memset + +#include // for QByteArray +#include // for QDateTime +#include // for QDir, operator|, QDir::Files, QDir::Name, QDir::Readable +#include // for QFileInfo +#include // for QFileInfoList +#include // for QLatin1String +#include // for QList +#include // for QScopedPointer +#include // for QString, operator== +#include // for QStringList +#include // for QTime +#include // for QVector +#include // for CaseInsensitive +#include // for qPrintable, foreach + +#include "defs.h" +#include "explorist_ini.h" // for explorist_ini_done, explorist_ini_get, mag_info +#include "format.h" // for Format +#include "gbfile.h" // for gbfclose, gbfeof, gbfgets, gbfopen, gbfwrite, gbfile +#include "gbser.h" // for gbser_deinit, gbser_init, gbser_is_serial, gbser_read_line, gbser_set_port, gbser_write, gbser_OK +#include "magellan.h" // for mm_meridian, mm_sportrak, magellan_icon_mapping_t, mm_gps315320, mm_unknown, mm_map330, mm_map410, pid_to_model_t, mm_gps310, m330_cleanse, mag_checksum, mag_find_descr_from_token, mag_find_token_from_descr, mag_rteparse, mag_trkparse +#include "src/core/datetime.h" // for DateTime +#include "vecs.h" // for Vecs + + +static int bitrate = 4800; +static int wptcmtcnt; +static int wptcmtcnt_max; +static int explorist; +static int broken_sportrak; +#define MYNAME "MAGPROTO" +#define MAXCMTCT 200 + +#define debug_serial (global_opts.debug_level > 1) + +static QString termread(char* ibuf, int size); +static void termwrite(const char* obuf, int size); +static void mag_readmsg(gpsdata_type objective); +static void mag_handon(); +static void mag_handoff(); +static short_handle mkshort_handle = nullptr; +static char* deficon = nullptr; +static char* bs = nullptr; +static char* cmts = nullptr; +static char* noack = nullptr; +static char* nukewpt = nullptr; +static int route_out_count; +static int waypoint_read_count; +static int wpt_len = 8; +static QString curfname; +static int extension_hint; +// For Explorist GC/510/610/710 families, bludgeon in GPX support. +// (This has nothing to do with the Explorist 100...600 products.) +static Format* gpx_vec; +static mag_info* explorist_info; +static QStringList os_gpx_files(const char* dirname); + +/* + * Magellan's firmware is *horribly* slow to send the next packet after + * we turn around an ack while we are reading from the device. It's + * quite spiffy when we're writing to the device. Since we're *way* + * less likely to lose data while reading from it than it is to lose data + * when we write to it, we turn off the acks when we are predominantly + * reading. + */ +static int suppress_ack; + +enum mag_rxstate { + mrs_handoff = 0, + mrs_handon, + mrs_awaiting_ack +}; + +/* + * An individual element of a route. + */ +struct mag_rte_elem { + QString wpt_name; + QString wpt_icon; +}; + +/* + * A header of a route. Related elements of a route belong to this. + */ +struct mag_rte_head_t { + QList elem_list; /* list of child rte_elems */ + char* rte_name{nullptr}; + int nelems{0}; +}; + +static QList rte_wpt_tmp; /* temporary PGMNWPL msgs for routes */ + +static gbfile* magfile_h; +static mag_rxstate magrxstate; +static int mag_error; +static unsigned int last_rx_csum; +static int found_done; +static int got_version; +static int is_file = 0; +static route_head* trk_head; +static int ignore_unable; + +static Waypoint* mag_wptparse(char*); +using cleanse_fn = QString (const char*); +static cleanse_fn* mag_cleanse; +static const char** os_get_magellan_mountpoints(); + +static const magellan_icon_mapping_t gps315_icon_table[] = { + { "a", "filled circle" }, + { "b", "box" }, + { "c", "red buoy" }, + { "d", "green buoy" }, + { "e", "buoy" }, + { "f", "rocks" }, + { "g", "red daymark" }, + { "h", "green daymark" }, + { "i", "bell" }, + { "j", "danger" }, + { "k", "diver down" }, + { "l", "fish" }, + { "m", "house" }, + { "n", "mark" }, + { "o", "car" }, + { "p", "tent" }, + { "q", "boat" }, + { "r", "food" }, + { "s", "fuel" }, + { "t", "tree" }, + { nullptr, nullptr } +}; + +static const magellan_icon_mapping_t map330_icon_table[] = { + { "a", "crossed square" }, + { "b", "box" }, + { "c", "house" }, + { "d", "aerial" }, + { "e", "airport" }, + { "f", "amusement park" }, + { "g", "ATM" }, + { "g", "Bank" }, + { "h", "auto repair" }, + { "i", "boating" }, + { "j", "camping" }, + { "k", "exit ramp" }, + { "l", "first aid" }, + { "m", "nav aid" }, + { "n", "buoy" }, + { "o", "fuel" }, + { "p", "garden" }, + { "q", "golf" }, + { "r", "hotel" }, + { "s", "hunting/fishing" }, + { "t", "large city" }, + { "u", "lighthouse" }, + { "v", "major city" }, + { "w", "marina" }, + { "x", "medium city" }, + { "y", "museum" }, + { "z", "obstruction" }, + { "aa", "park" }, + { "ab", "resort" }, + { "ac", "restaurant" }, + { "ad", "rock" }, + { "ae", "scuba" }, + { "af", "RV service" }, + { "ag", "shooting" }, + { "ah", "sight seeing" }, + { "ai", "small city" }, + { "aj", "sounding" }, + { "ak", "sports arena" }, + { "al", "tourist info" }, + { "am", "truck service" }, + { "an", "winery" }, + { "ao", "wreck" }, + { "ap", "zoo" }, + { "ah", "Virtual cache"}, /* Binos: because you "see" them. */ + { "ak", "Micro-Cache" }, /* Looks like a film canister. */ + { "an", "Multi-Cache"}, /* Winery: grapes 'coz they "bunch" */ + { "s", "Unknown Cache"}, /* 'Surprise' cache: use a target. */ + { "ac", "Event Cache"}, /* Event caches. May be food. */ + { nullptr, nullptr } +}; + +pid_to_model_t pid_to_model[] = { + { mm_gps315320, 19, "ColorTrak" }, + { mm_gps315320, 24, "GPS 315/320" }, + { mm_map410, 25, "Map 410" }, + { mm_map330, 30, "Map 330" }, + { mm_gps310, 31, "GPS 310" }, + { mm_meridian, 33, "Meridian" }, + { mm_meridian, 35, "ProMark 2" }, + { mm_sportrak, 36, "SporTrak Map/Pro" }, + { mm_sportrak, 37, "SporTrak" }, + { mm_meridian, 38, "FX324 Plotter" }, + { mm_meridian, 39, "Meridian Color" }, + { mm_meridian, 40, "FX324C Plotter" }, + { mm_sportrak, 41, "Sportrak Color" }, + { mm_sportrak, 42, "Sportrak Marine" }, + { mm_meridian, 43, "Meridian Marine" }, + { mm_sportrak, 44, "Sportrak Topo" }, + { mm_sportrak, 45, "Mystic" }, + { mm_meridian, 46, "MobileMapper" }, + { mm_meridian, 110, "Explorist 100" }, + { mm_meridian, 111, "Explorist 200" }, + { mm_unknown, 0, nullptr } +}; + +static const magellan_icon_mapping_t* icon_mapping = map330_icon_table; + +/* + * For each receiver type, return a "cleansed" version of the string + * that's valid for a waypoint name or comment. The string should be + * freed when you're done with it. + */ +static QString +m315_cleanse(const char* istring) +{ + char* rstring = (char*) xmalloc(strlen(istring)+1); + char* o; + const char* i; + static char m315_valid_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789"; + for (o=rstring,i=istring; *i; i++) { + if (strchr(m315_valid_chars, toupper(*i))) { + *o++ = toupper(*i); + } + } + *o = 0; + QString rv(rstring); + xfree(rstring); + return rv; +} + +/* + * Do same for 330, Meridian, and SportTrak. + */ +QString +m330_cleanse(const char* istring) +{ + static char m330_valid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ " + "abcdefghijklmnopqrstuvwxyz" + "0123456789+-.'/!@#<%^&>()=:\\"; + char* rstring = (char*) xmalloc(strlen(istring)+1); + char* o; + const char* i; + + for (o=rstring,i=istring; *i; i++) { + if (strchr(m330_valid_chars, *i)) { + *o++ = *i; + } + } + *o = 0; + QString rv(rstring); + xfree(rstring); + return rv; +} + +/* + * Given a protocol message, compute the checksum as needed by + * the Magellan protocol. + */ +unsigned int +mag_checksum(const char* const buf) +{ + int csum = 0; + + for (const char* p = buf; *p; p++) { + csum ^= *p; + } + + return csum; +} +static unsigned int +mag_pchecksum(const char* const buf, int len) +{ + int csum = 0; + const char* p = buf; + for (; len ; len--) { + csum ^= *p++; + } + return csum; +} + +static void +mag_writemsg(const char* const buf) +{ + unsigned int osum = mag_checksum(buf); + int retry_cnt = 5; + QScopedPointer obuf; + + if (debug_serial) { + warning("WRITE: $%s*%02X\r\n",buf, osum); + } + +retry: + + int i = xasprintf(obuf, "$%s*%02X\r\n",buf, osum); + termwrite(obuf.data(), i); + if (magrxstate == mrs_handon || magrxstate == mrs_awaiting_ack) { + magrxstate = mrs_awaiting_ack; + mag_readmsg(trkdata); + if (last_rx_csum != osum) { + if (debug_serial) { + warning("COMM ERROR: Expected %02x, got %02x", + osum, last_rx_csum); + } + if (retry_cnt--) { + goto retry; + } else { + mag_handoff(); + fatal(MYNAME + ": Too many communication errors.\n"); + } + } + } +} + +static void +mag_writeack(int osum) +{ + QScopedPointer nbuf; + QScopedPointer obuf; + + if (is_file) { + return; + } + + (void) xasprintf(nbuf, "PMGNCSM,%02X", osum); + unsigned int nsum = mag_checksum(nbuf.data()); + int i = xasprintf(obuf, "$%s*%02X\r\n",nbuf.data(), nsum); + + if (debug_serial) { + warning("ACK WRITE: %s",obuf.data()); + } + /* + * Don't call mag_writemsg here so we don't get into ack feedback + * loops. + */ + termwrite(obuf.data(), i); +} + +static void +mag_handon() +{ + if (!is_file) { + mag_writemsg("PMGNCMD,HANDON"); + } + magrxstate = mrs_handon; + +} + +static void +mag_handoff() +{ + if (!is_file) { + mag_writemsg("PMGNCMD,HANDOFF"); + } + magrxstate = mrs_handoff; +} + +static void +mag_verparse(char* ibuf) +{ + int prodid = mm_unknown; + char version[1024]; + pid_to_model_t* pp = pid_to_model; + + got_version = 1; + sscanf(ibuf,"$PMGNVER,%d,%[^,]", &prodid, version); + + for (pp = pid_to_model; pp->model != mm_unknown; pp++) { + if (pp->pid == prodid) { + break; + } + } + + if (prodid == 37) { + broken_sportrak = 1; + } + + switch (pp->model) { + case mm_gps315320: + case mm_map410: + icon_mapping = gps315_icon_table; + setshort_length(mkshort_handle, 6); + setshort_mustupper(mkshort_handle, 1); + mag_cleanse = m315_cleanse; + break; + case mm_map330: + case mm_meridian: + case mm_sportrak: + icon_mapping = map330_icon_table; + setshort_length(mkshort_handle, wpt_len); + setshort_mustupper(mkshort_handle, 0); + mag_cleanse = m330_cleanse; + break; + default: + fatal(MYNAME ": Unknown receiver type %d, model version '%s'.\n", prodid, version); + } +} + +#define IS_TKN(x) (strncmp(ibuf,x, sizeof(x)-1) == 0) + +static void +mag_readmsg(gpsdata_type objective) +{ + char ibuf[512]; /* oliskoli: corrupted data (I've seen descr with a lot + of escaped FFFFFFFF) may need more size */ + int retrycnt = 20; + +retry: + QString gr = termread(ibuf, sizeof(ibuf)); + + if (gr.isEmpty()) { + if (!got_version) { + /* + * The 315 can take up to six seconds to respond to + * a VERSION command. Since this is on startup, + * we'll be fairly persistent in retrying. + */ + if (retrycnt--) { + goto retry; + } else { + fatal(MYNAME ": No data received from GPS.\n"); + } + } else { + if (is_file) { + found_done = 1; + } + return; + } + } + + /* If column zero isn't a dollar sign, it's not for us */ + if (ibuf[0] != '$') { + fatal(MYNAME ": line doesn't start with '$'.\n"); + } + + + int isz = strlen(ibuf); + + if (isz < 5) { + if (debug_serial) { + warning("SHORT READ %d\n", isz); + } + return; + } + mag_error = 0; + while (!isprint(ibuf[isz])) { + isz--; + } + char* isump = &ibuf[isz-1]; + unsigned int isum = strtoul(isump, nullptr,16); + if (isum != mag_pchecksum(&ibuf[1], isz-3)) { + if (debug_serial) { + warning("RXERR %02x/%02x: '%s'\n", isum, mag_pchecksum(&ibuf[1],isz-5), ibuf); + } + /* Special case receive errors early on. */ + if (!got_version) { + fatal(MYNAME ": bad communication. Check bit rate.\n"); + } + } + if (debug_serial) { + warning("READ: %s\n", ibuf); + } + if (IS_TKN("$PMGNCSM,")) { + last_rx_csum = strtoul(&ibuf[9], nullptr, 16); + magrxstate = mrs_handon; + return; + } + if (strncmp(ibuf, "$PMGNWPL,", 7) == 0) { + Waypoint* wpt = mag_wptparse(ibuf); + waypoint_read_count++; + if (global_opts.verbose_status) { + waypt_status_disp(waypoint_read_count, + waypoint_read_count); + } + + if (extension_hint) { + if (extension_hint == WPTDATAMASK) { + waypt_add(wpt); + } else if (extension_hint == RTEDATAMASK) { + rte_wpt_tmp.append(wpt); + } + } else { + switch (objective) { + case wptdata: + waypt_add(wpt); + break; + case rtedata: + rte_wpt_tmp.append(wpt); + break; + default: + break; + } + } + } + if (strncmp(ibuf, "$PMGNTRK,", 7) == 0) { + Waypoint* wpt = mag_trkparse(ibuf); + /* + * Allow lazy allocation of track head. + */ + if (trk_head == nullptr) { + /* These tracks don't have names, so derive one + * from input filename. + */ + + trk_head = new route_head; + + /* Whack trailing extension if present. */ + QString s = get_filename(curfname); + int idx = s.indexOf('.'); + if (idx > 0) { + s.truncate(idx); + } + + trk_head->rte_name = s; + track_add_head(trk_head); + } + + track_add_wpt(trk_head, wpt); + } + if (strncmp(ibuf, "$PMGNRTE,", 7) == 0) { + mag_rteparse(ibuf); + } + if (IS_TKN("$PMGNVER,")) { + mag_verparse(ibuf); + } + mag_error = 0; + if (!ignore_unable && IS_TKN("$PMGNCMD,UNABLE")) { + warning("Unable to send\n"); + found_done = 1; + mag_error = 1; + ignore_unable = 0; + return; + } + if (IS_TKN("$PMGNCMD,END") || (is_file && (gbfeof(magfile_h)))) { + found_done = 1; + return; + } + + if (magrxstate != mrs_handoff) { + mag_writeack(isum); + } +} + +static void* serial_handle = nullptr; + +static int +terminit(const QString& portname, int create_ok) +{ + if (gbser_is_serial(qPrintable(portname))) { + if (serial_handle = gbser_init(qPrintable(portname)), nullptr != serial_handle) { + int rc; + if (rc = gbser_set_port(serial_handle, bitrate, 8, 0, 1), gbser_OK != rc) { + fatal(MYNAME ": Can't configure port\n"); + } + } + is_file = 0; + if (serial_handle == nullptr) { + fatal(MYNAME ": Could not open serial port %s\n", qPrintable(portname)); + } + return 1; + } else { + /* Does this check for an error? */ + magfile_h = gbfopen(portname, create_ok ? "w+b" : "rb", MYNAME); + is_file = 1; + icon_mapping = map330_icon_table; + mag_cleanse = m330_cleanse; + got_version = 1; + return 0; + } +} + +static QString termread(char* ibuf, int size) +{ + if (is_file) { + return gbfgets(ibuf, size, magfile_h); + } else { + int rc = gbser_read_line(serial_handle, ibuf, size, 2000, 0x0a, 0x0d); + if (rc != gbser_OK) { + fatal(MYNAME ": Read error\n"); + } + return ibuf; + } +} + +/* Though not documented in the protocol spec, if the unit itself + * wants to create a field containing a comma, it will encode it + * as 2C. We extrapolate that any 2 digit hex encoding may + * be valid. We don't do this in termread() since we need to do it + * after the scanf. This means we have to do it field-by-field + * basis. + * + * The buffer is modified in place and shortened by copying the remaining + * string including the terminator. + */ +static +void +mag_dequote(char* ibuf) +{ + char* esc = nullptr; + + while ((esc = strchr(ibuf, 0x1b))) { + int nremains = strlen(esc); + if (nremains >= 3) { + static const char hex[17] = "0123456789ABCDEF"; + const char* c1 = strchr(hex, esc[1]); + const char* c2 = strchr(hex, esc[2]); + if (c1 && c2) { + int escv = (c1 - hex) * 16 + (c2 - hex); + if (escv == 255) { /* corrupted data */ + char* tmp = esc + 1; + while (*tmp == 'F') { + tmp++; + } + memmove(esc, tmp, strlen(tmp) + 1); + } else { + *esc++ = (isprint(escv)) ? escv : '$'; + /* buffers overlap */ + memmove(esc, esc+2, nremains - 2); + } + } + } else { + *esc = '\0'; /* trim corrupted data, + otherwise we get an endless loop */ + } + } +} + +static void +termwrite(const char* obuf, int size) +{ + if (is_file) { + size_t nw; + if (nw = gbfwrite(obuf, 1, size, magfile_h), nw < (size_t) size) { + fatal(MYNAME ": Write error"); + } + } else { + int rc; + if (rc = gbser_write(serial_handle, obuf, size), rc < 0) { + fatal(MYNAME ": Write error"); + } + } +} + +static void termdeinit() +{ + if (is_file) { + gbfclose(magfile_h); + magfile_h = nullptr; + } else { + gbser_deinit(serial_handle); + serial_handle = nullptr; + } +} + +/* + * Arg tables are doubled up so that -? can output appropriate help + */ +static +QVector mag_sargs = { + { + "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING, + ARG_NOMINMAX, nullptr + }, + { + "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)", + "200", ARGTYPE_INT, ARG_NOMINMAX, nullptr + }, + { + "baud", &bs, "Numeric value of bitrate (baud=4800)", "4800", + ARGTYPE_INT, ARG_NOMINMAX, nullptr + }, + { + "noack", &noack, "Suppress use of handshaking in name of speed", + nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "nukewpt", &nukewpt, "Delete all waypoints", nullptr, ARGTYPE_BOOL, + ARG_NOMINMAX, nullptr + }, +}; + +static +QVector mag_fargs = { + { + "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING, + ARG_NOMINMAX, nullptr + }, + { + "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)", + nullptr, ARGTYPE_INT, ARG_NOMINMAX, nullptr + }, +}; + +/* + * The part of the serial init that's common to read and write. + */ +static void +mag_serial_init_common(const QString& portname) +{ + if (is_file) { + return; + } + + mag_handoff(); + if (!noack && !suppress_ack) { + mag_handon(); + } + + time_t now = current_time().toTime_t(); + /* + * The 315 can take up to 4.25 seconds to respond to initialization + * commands. Time out on the side of caution. + */ + time_t later = now + 6; + got_version = 0; + mag_writemsg("PMGNCMD,VERSION"); + + while (!got_version) { + mag_readmsg(trkdata); + if (current_time().toTime_t() > later) { + fatal(MYNAME ": No acknowledgment from GPS on %s\n", + qPrintable(portname)); + } + } + + if ((icon_mapping != gps315_icon_table)) { + /* + * The 315 can't handle this command, so we set a global + * to ignore the NAK on it. + */ + ignore_unable = 1; + mag_writemsg("PMGNCMD,NMEAOFF"); + ignore_unable = 0; + } + + if (nukewpt) { + /* The unit will send us an "end" message upon completion */ + mag_writemsg("PMGNCMD,DELETE,WAYPOINT"); + mag_readmsg(trkdata); + if (!found_done) { + fatal(MYNAME ": Unexpected response to waypoint delete command.\n"); + } + found_done = 0; + } + +} +static void +mag_rd_init_common(const QString& portname) +{ + waypoint_read_count = 0; + // For Explorist GC, intercept the device access and redirect to GPX. + // We actually do the rd_init() inside read as we may have multiple + // files that we have to read. + if (portname == "usb:") { + const char** dlist = os_get_magellan_mountpoints(); + explorist_info = explorist_ini_get(dlist); + if (explorist_info) { + gpx_vec = Vecs::Instance().find_vec("gpx"); + } + return; + } + + if (bs) { + bitrate=xstrtoi(bs, nullptr, 10); + } + + if (!mkshort_handle) { + mkshort_handle = mkshort_new_handle(); + } + + terminit(portname, 0); + mag_serial_init_common(portname); + + rte_wpt_tmp.clear(); + + /* find the location of the tail of the path name, + * make a copy of it, then lop off the file extension + */ + + curfname = get_filename(portname); + + /* + * I'd rather not derive behaviour from filenames but since + * we can't otherwise tell if we should put a WPT on the route + * queue or the WPT queue in the presence of (-w -r -t) we + * divine a hint from the filename extension when we can. + */ + QString exten = QFileInfo(curfname).suffix(); + if (exten.length() > 0) { + if (0 == exten.compare(QLatin1String("upt"), Qt::CaseInsensitive)) { + extension_hint = WPTDATAMASK; + } else if (0 == exten.compare(QLatin1String("log"), Qt::CaseInsensitive)) { + extension_hint = TRKDATAMASK; + } else if (0 == exten.compare(QLatin1String("rte"), Qt::CaseInsensitive)) { + extension_hint = RTEDATAMASK; + } + } + +} + +static void +mag_rd_init(const QString& portname) +{ + explorist = 0; + suppress_ack = 1; + mag_rd_init_common(portname); +} + +static void +magX_rd_init(const QString& portname) +{ + explorist = 1; + mag_rd_init_common(portname); +} + +static void +mag_wr_init_common(const QString& portname) +{ + suppress_ack = 0; + if (bs) { + bitrate=xstrtoi(bs, nullptr, 10); + } + + if (waypt_count() > 500) { + fatal(MYNAME ": Meridian/Explorist does not support more than 500 waypoints in one file. Only\n200 waypoints may have comments.\nDecrease the number of waypoints sent.\n"); + } + + if (cmts) { + wptcmtcnt_max = xstrtoi(cmts, nullptr, 10); + } else { + wptcmtcnt_max = MAXCMTCT ; + } + + if (!mkshort_handle) { + mkshort_handle = mkshort_new_handle(); + } + + terminit(portname, 1); + mag_serial_init_common(portname); + + rte_wpt_tmp.clear(); +} + +/* + * Entry point for extended (explorist) points. + */ +static void +magX_wr_init(const QString& portname) +{ + wpt_len = 20; + explorist = 1; + mag_wr_init_common(portname); + setshort_length(mkshort_handle, wpt_len); + setshort_whitespace_ok(mkshort_handle, 1); +} + +static void +mag_wr_init(const QString& portname) +{ + explorist = 0; + wpt_len = 8; + mag_wr_init_common(portname); + /* + * Whitespace is actually legal, but since waypoint name length is + * only 8 bytes, we'll conserve them. + */ + + setshort_whitespace_ok(mkshort_handle, 0); +} + +static void +mag_deinit() +{ + if (explorist_info) { + explorist_ini_done(explorist_info); + return; + } + mag_handoff(); + termdeinit(); + if (mkshort_handle) { + mkshort_del_handle(&mkshort_handle); + } + + while (!rte_wpt_tmp.isEmpty()) { + delete rte_wpt_tmp.takeFirst(); + } + + trk_head = nullptr; + + curfname.clear(); +} + +static void +mag_wr_deinit() +{ + if (explorist) { + mag_writemsg("PMGNCMD,END"); + } + mag_deinit(); +} + +/* + * I'm tired of arguing with scanf about optional fields . Detokenize + * an incoming string that may contain empty fields. + * + * Probably should be cleaned up and moved to common code, but + * making it deal with an arbitrary number of fields of arbitrary + * size is icky. We don't have to solve the general case here... + */ + +static char ifield[20][100]; +static +void parse_istring(char* istring) +{ + int f = 0; + int n; + while (istring[0]) { + char* fp = ifield[f]; + int x = sscanf(istring, "%[^,]%n", fp, &n); + f++; + if (x) { + istring += n; + /* IF more in this string, skip delim */ + if (istring[0]) { + istring++; + } + } else { + istring ++; + } + } +} + +/* + * Given an incoming track messages of the form: + * $PMGNTRK,3605.259,N,08644.389,W,00151,M,201444.61,A,,020302*66 + * create and return a populated waypoint. + */ +Waypoint* +mag_trkparse(char* trkmsg) +{ + int hms; + int fracsecs; + struct tm tm; + + auto* waypt = new Waypoint; + + memset(&tm, 0, sizeof(tm)); + + /* + * As some of the fields are optional, sscanf works badly + * for us. + */ + parse_istring(trkmsg); + double latdeg = strtod(ifield[1], nullptr); + char latdir = ifield[2][0]; + double lngdeg = strtod(ifield[3], nullptr); + char lngdir = ifield[4][0]; + int alt = strtod(ifield[5], nullptr); + char altunits = ifield[6][0]; + (void)altunits; + sscanf(ifield[7], "%d.%d", &hms, &fracsecs); + /* Field 8 is constant */ + /* Field nine is optional track name */ + int dmy = xstrtoi(ifield[10], nullptr, 10); + int sec = hms % 100; + hms = hms / 100; + int min = hms % 100; + hms = hms / 100; + int hour = hms % 100; + + int year = 100 + dmy % 100 + 1900; + dmy = dmy / 100; + int mon = dmy % 100; + dmy = dmy / 100; + int day = dmy % 100; + QDateTime dt(QDate(year, mon, day), QTime(hour, min, sec, fracsecs * 10), Qt::UTC); + waypt->SetCreationTime(dt); + + if (latdir == 'S') { + latdeg = -latdeg; + } + waypt->latitude = ddmm2degrees(latdeg); + + if (lngdir == 'W') { + lngdeg = -lngdeg; + } + waypt->longitude = ddmm2degrees(lngdeg); + + waypt->altitude = alt; + + return waypt; + +} + +/* + * Given an incoming route messages of the form: + * $PMGNRTE,4,1,c,1,DAD,a,Anna,a*61 + * generate a route. + */ +void +mag_rteparse(char* rtemsg) +{ + int n; + int frags,frag,rtenum; + char xbuf[100],next_stop[100],abuf[100]; + char* currtemsg; + static mag_rte_head_t* mag_rte_head; + +#if 0 + sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n", + &frags,&frag,xbuf,&rtenum,&n); +#else + sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n", + &frags,&frag,xbuf,&rtenum,&n); + + /* Explorist has a route name here */ + QString rte_name; + if (explorist) { + char* ca = rtemsg + n; + if (*ca++ != ',') { + fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg); + } + + char* ce = strchr(ca, ','); + if (ce == nullptr) { + fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg); + } + + if (ca == ce) { + rte_name = "Route"; + rte_name += QString::number(rtenum); + } else { + rte_name = ca; + rte_name.truncate(ce-ca); + } + + n += ((ce - ca) + 1); + } + +#endif + + /* + * This is the first component of a route. Allocate a new + * head. + */ + if (frag == 1) { + mag_rte_head = new mag_rte_head_t; + mag_rte_head->nelems = frags; + } + + currtemsg = rtemsg + n; + + /* + * The individual line may contain several route elements. + * loop and pick those up. + */ + while (sscanf(currtemsg,",%[^,],%[^,]%n",next_stop, abuf,&n)) { + if ((next_stop[0] == 0) || (next_stop[0] == '*')) { + break; + } + + /* trim CRC from waypoint icon string */ + if (char* p = strchr(abuf, '*'); p != nullptr) { + *p = '\0'; + } + + auto* rte_elem = new mag_rte_elem; + + rte_elem->wpt_name = next_stop; + rte_elem->wpt_icon = abuf; + + mag_rte_head->elem_list.append(rte_elem); + + /* Sportrak (the non-mapping unit) creates malformed + * RTE sentence with no icon info after the routepoint + * name. So if we saw an "icon" treat that as new + * routepoint. + */ + if (broken_sportrak && abuf[0]) { + rte_elem = new mag_rte_elem; + rte_elem->wpt_name = abuf; + + mag_rte_head->elem_list.append(rte_elem); + } + + next_stop[0] = 0; + currtemsg += n; + } + + /* + * If this was the last fragment of the route, add it to the + * gpsbabel internal structs now. + */ + if (frag == mag_rte_head->nelems) { + + auto* rte_head = new route_head; + route_add_head(rte_head); + rte_head->rte_num = rtenum; + rte_head->rte_name = rte_name; + + /* + * It is quite feasible that we have 200 waypoints, + * 3 of which are used in the route. We'll need to find + * those in the queue for SD routes... + */ + + while (!mag_rte_head->elem_list.isEmpty()) { + mag_rte_elem* re = mag_rte_head->elem_list.takeFirst(); + + /* + * Copy route points from temp wpt queue. + */ + foreach (const Waypoint* waypt, rte_wpt_tmp) { + if (waypt->shortname == re->wpt_name) { + auto* wpt = new Waypoint(*waypt); + route_add_wpt(rte_head, wpt); + break; + } + } + + delete re; + } + delete mag_rte_head; + } +} + +QString +mag_find_descr_from_token(const char* token) +{ + if (icon_mapping == nullptr) { + return "unknown"; + } + + for (const magellan_icon_mapping_t* i = icon_mapping; i->token; i++) { + if (token[0] == 0) { + break; + } + if (case_ignore_strcmp(token, i->token) == 0) { + return i->icon; + } + } + return icon_mapping[0].icon; +} + +QString +mag_find_token_from_descr(const QString& icon) +{ + const magellan_icon_mapping_t* i = icon_mapping; + + if (i == nullptr || icon == nullptr) { + return "a"; + } + + for (i = icon_mapping; i->token; i++) { + if (icon.compare(i->icon, Qt::CaseInsensitive) == 0) { + return i->token; + } + } + return icon_mapping[0].token; +} + +/* + * Given an incoming waypoint messages of the form: + * $PMGNWPL,3549.499,N,08650.827,W,0000257,M,HOME,HOME,c*4D + * create and return a populated waypoint. + */ +static Waypoint* +mag_wptparse(char* trkmsg) +{ + double latdeg, lngdeg; + char latdir; + char lngdir; + int alt; + char altunits; + char shortname[100]; + char descr[256]; + char icon_token[100]; + int i = 0; + + descr[0] = 0; + icon_token[0] = 0; + + auto* waypt = new Waypoint; + + sscanf(trkmsg,"$PMGNWPL,%lf,%c,%lf,%c,%d,%c,%[^,],%[^,]", + &latdeg,&latdir, + &lngdeg,&lngdir, + &alt,&altunits,shortname,descr); + char* icone = strrchr(trkmsg, '*'); + char* icons = strrchr(trkmsg, ',')+1; + + mag_dequote(descr); + + for (char* blah = icons ; blah < icone; blah++) { + icon_token[i++] = *blah; + } + icon_token[i++] = '\0'; + + if (latdir == 'S') { + latdeg = -latdeg; + } + waypt->latitude = ddmm2degrees(latdeg); + + if (lngdir == 'W') { + lngdeg = -lngdeg; + } + waypt->longitude = ddmm2degrees(lngdeg); + + waypt->altitude = alt; + waypt->shortname = shortname; + waypt->description = descr; + waypt->icon_descr = mag_find_descr_from_token(icon_token); + + return waypt; +} + +static void +mag_read() +{ + if (gpx_vec) { + QStringList f = os_gpx_files(explorist_info->track_path); + for (const auto& file : qAsConst(f)) { + gpx_vec->rd_init(file); + gpx_vec->read(); + gpx_vec->rd_deinit(); + } + + f = os_gpx_files(explorist_info->waypoint_path); + for (const auto& file : qAsConst(f)) { + gpx_vec->rd_init(file); + gpx_vec->read(); + gpx_vec->rd_deinit(); + } +#if 0 + f = os_gpx_files(explorist_info->geo_path); + for (const auto& file : qAsConst(f)) { + gpx_vec->rd_init(file); + gpx_vec->read(); + gpx_vec->rd_deinit(); + } +#endif + return; + } + + found_done = 0; + if (global_opts.masked_objective & TRKDATAMASK) { + magrxstate = mrs_handoff; + if (!is_file) { + mag_writemsg("PMGNCMD,TRACK,2"); + } + + while (!found_done) { + mag_readmsg(trkdata); + } + } + + found_done = 0; + if (global_opts.masked_objective & WPTDATAMASK) { + magrxstate = mrs_handoff; + if (!is_file) { + mag_writemsg("PMGNCMD,WAYPOINT"); + } + + while (!found_done) { + mag_readmsg(wptdata); + } + } + + found_done = 0; + if (global_opts.masked_objective & RTEDATAMASK) { + magrxstate = mrs_handoff; + if (!is_file) { + /* + * serial routes require waypoint & routes + * messages commands. + */ + mag_writemsg("PMGNCMD,WAYPOINT"); + + while (!found_done) { + mag_readmsg(rtedata); + } + + mag_writemsg("PMGNCMD,ROUTE"); + + found_done = 0; + while (!found_done) { + mag_readmsg(rtedata); + } + } else { + /* + * SD routes are a stream of PMGNWPL and + * PMGNRTE messages, in that order. + */ + while (!found_done) { + mag_readmsg(rtedata); + } + } + } +} + +static +void +mag_waypt_pr(const Waypoint* waypointp) +{ + QScopedPointer obuf; + QScopedPointer ofmtdesc; + QString icon_token; + + double ilat = waypointp->latitude; + double ilon = waypointp->longitude; + + double lon = fabs(ilon); + double lat = fabs(ilat); + + int lon_deg = lon; + int lat_deg = lat; + + lon = (lon - lon_deg) * 60.0; + lat = (lat - lat_deg) * 60.0; + + lon = (lon_deg * 100.0 + lon); + lat = (lat_deg * 100.0 + lat); + + if (deficon) { + icon_token = mag_find_token_from_descr(deficon); + } else { + icon_token = mag_find_token_from_descr(waypointp->icon_descr); + } + + if (!get_cache_icon(waypointp).isEmpty()) { + icon_token = mag_find_token_from_descr(get_cache_icon(waypointp)); + } + + QString isrc = waypointp->notes.isEmpty() ? waypointp->description : waypointp->notes; + QString owpt = global_opts.synthesize_shortnames ? + mkshort_from_wpt(mkshort_handle, waypointp) : waypointp->shortname; + QString odesc = isrc; + owpt = mag_cleanse(CSTRc(owpt)); + + if (global_opts.smart_icons && + waypointp->gc_data->diff && waypointp->gc_data->terr) { + // It's a string and compactness counts, so "1.0" is OK to be "10". + xasprintf(ofmtdesc, "%ud/%ud %s", waypointp->gc_data->diff, + waypointp->gc_data->terr, CSTRc(odesc)); + odesc = mag_cleanse(ofmtdesc.data()); + } else { + odesc = mag_cleanse(CSTRc(odesc)); + } + + /* + * For the benefit of DirectRoute (which uses waypoint comments + * to deliver turn-by-turn popups for street routing) allow a + * cap on the comments delivered so we leave space for it to route. + */ + if (!odesc.isEmpty() && (wptcmtcnt++ >= wptcmtcnt_max)) { + odesc.clear(); + } + + xasprintf(obuf, "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s", + lat, ilat < 0 ? 'S' : 'N', + lon, ilon < 0 ? 'W' : 'E', + waypointp->altitude == unknown_alt ? + 0 : waypointp->altitude, + wpt_len, + CSTRc(owpt), + CSTRc(odesc), + CSTR(icon_token)); + mag_writemsg(obuf.data()); + + if (!is_file) { + if (mag_error) { + warning("Protocol error Writing '%s'\n", obuf.data()); + } + } +} + +static +void mag_track_disp(const Waypoint* waypointp) +{ + QScopedPointer obuf; + + double ilat = waypointp->latitude; + double ilon = waypointp->longitude; + + QByteArray dmy(""); + QByteArray hms(""); + if (waypointp->creation_time.isValid()) { + // Round to hundredths of seconds before conversion to string. + // Rounding can ripple all the way from the msec to the year. + QDateTime dt = waypointp->GetCreationTime().toUTC(); + dt = dt.addMSecs(10 * lround(dt.time().msec()/10.0) - dt.time().msec()); + assert((dt.time().msec() % 10) == 0); + dmy = dt.toString(u"ddMMyy").toUtf8(); + hms = dt.toString(u"hhmmss.zzz").left(9).toUtf8(); + } + + double lon = fabs(ilon); + double lat = fabs(ilat); + + int lon_deg = lon; + int lat_deg = lat; + + lon = (lon - lon_deg) * 60.0; + lat = (lat - lat_deg) * 60.0; + + lon = (lon_deg * 100.0 + lon); + lat = (lat_deg * 100.0 + lat); + + xasprintf(obuf,"PMGNTRK,%4.3f,%c,%09.3f,%c,%05.0f,%c,%s,A,,%s", + lat, ilat < 0 ? 'S' : 'N', + lon, ilon < 0 ? 'W' : 'E', + waypointp->altitude == unknown_alt ? + 0 : waypointp->altitude, + 'M', hms.constData(), dmy.constData()); + mag_writemsg(obuf.data()); +} + +static +void mag_track_pr() +{ + track_disp_all(nullptr, nullptr, mag_track_disp); +} + +/* +The spec says to stack points: + $PMGNRTE,2,1,c,1,FOO,POINT1,b,POINT2,c,POINT3,d*6C + +Meridian SD card and serial (at least) writes in pairs: + $PMGNRTE,4,1,c,1,HOME,c,I49X73,a*15 + ... + $PMGNRTE,4,4,c,1,RON273,a,MYCF93,a*7B + +The spec also says that some units don't like single-legged pairs, +and to replace the 2nd name with "<<>>", but I haven't seen one of those. +*/ + +static void +mag_route_trl(const route_head* rte) +{ + QScopedPointer obuff; + QString buff1; + QString buff2; + QString icon_token; + + /* count waypoints for this route */ + int i = rte->rte_waypt_ct(); + + /* number of output PMGNRTE messages at 2 points per line */ + int numlines = (i / 2) + (i % 2); + + /* increment the route counter. */ + route_out_count++; + + int thisline = i = 0; + foreach (const Waypoint* waypointp, rte->waypoint_list) { + i++; + + if (deficon) { + icon_token = mag_find_token_from_descr(deficon); + } else { + icon_token = mag_find_token_from_descr(waypointp->icon_descr); + } + + QString* pbuff; + if (i == 1) { + pbuff = &buff1; + } else { + pbuff = &buff2; + } + // Write name, icon tuple into alternating buff1/buff2 buffer. + *pbuff = waypointp->shortname + ',' + icon_token; + + if ((waypointp == rte->waypoint_list.back()) || ((i % 2) == 0)) { + QString expbuf; + thisline++; + if (explorist) { + expbuf = rte->rte_name + ','; + } + + xasprintf(obuff, "PMGNRTE,%d,%d,c,%d,%s%s,%s", + numlines, thisline, + rte->rte_num ? rte->rte_num : route_out_count, + CSTRc(expbuf), + CSTR(buff1), CSTR(buff2)); + + mag_writemsg(obuff.data()); + buff1.clear(); + buff2.clear(); + i = 0; + } + } +} + +static void +mag_route_pr() +{ + route_out_count = 0; + route_disp_all(nullptr, mag_route_trl, mag_waypt_pr); + +} + +static void +mag_write() +{ + + wptcmtcnt = 0; + + switch (global_opts.objective) { + case trkdata: + mag_track_pr(); + break; + case wptdata: + waypt_disp_all(mag_waypt_pr); + break; + case rtedata: + mag_route_pr(); + break; + default: + fatal(MYNAME ": Unknown objective.\n"); + } +} + +const char** os_get_magellan_mountpoints() +{ +#if __APPLE__ + const char** dlist = (const char**) xcalloc(2, sizeof *dlist); + dlist[0] = xstrdup("/Volumes/Magellan"); + dlist[1] = nullptr; + return dlist; +#else + fatal("Not implemented"); + return nullptr; +#endif +} + +static QStringList +os_gpx_files(const char* dirname) +{ + QDir dir(dirname); + + const QFileInfoList filist = dir.entryInfoList(QStringList("*.gpx"), QDir::Files | QDir::Readable, QDir::Name); + QStringList rv; + for (const auto& fi : filist) { + rv.append(fi.absoluteFilePath()); + } + return rv; +} + +/* + * This is repeated just so it shows up as separate menu options + * for the benefit of GUI wrappers. + */ +ff_vecs_t mag_svecs = { + ff_type_serial, + FF_CAP_RW_ALL, + mag_rd_init, + mag_wr_init, + mag_deinit, + mag_deinit, + mag_read, + mag_write, + nullptr, + &mag_sargs, + CET_CHARSET_ASCII, 0, /* CET-REVIEW */ + NULL_POS_OPS +}; + +ff_vecs_t mag_fvecs = { + ff_type_file, + FF_CAP_RW_ALL, + mag_rd_init, + mag_wr_init, + mag_deinit, + mag_deinit, + mag_read, + mag_write, + nullptr, + &mag_fargs, + CET_CHARSET_ASCII, 0, /* CET-REVIEW */ + NULL_POS_OPS +}; + +/* + * Extended (Explorist) entry tables. + */ +ff_vecs_t magX_fvecs = { + ff_type_file, + FF_CAP_RW_ALL, + magX_rd_init, + magX_wr_init, + mag_deinit, + mag_wr_deinit, + mag_read, + mag_write, + nullptr, + &mag_fargs, + CET_CHARSET_ASCII, 0, /* CET-REVIEW */ + NULL_POS_OPS +}; diff --git a/explorist_ini.cc b/explorist_ini.cc deleted file mode 100644 index e9eafdb35..000000000 --- a/explorist_ini.cc +++ /dev/null @@ -1,74 +0,0 @@ -#include "defs.h" -#include "explorist_ini.h" -#include "inifile.h" - -static inifile_t* inifile; -static const char myname[] = "explorist"; - -#ifdef DEAD_CODE_IS_REBORN -static const char* -explorist_read_value(const char* section, const char* key) -{ - return inifile_readstr(inifile, section, key); -} -#endif - -static mag_info* -explorist_ini_try(const char* path) -{ - char* inipath; - - xasprintf(&inipath, "%s/%s", path, "APP/Atlas.ini"); - inifile = inifile_init(QString::fromUtf8(inipath), myname); - if (!inifile) { - xfree(inipath); - return nullptr; - } - - auto* info = (mag_info*) xmalloc(sizeof(mag_info)); - info->geo_path = nullptr; - info->track_path = nullptr; - info->waypoint_path = nullptr; - - QString s = inifile_readstr(inifile, "UGDS", "WpFolder"); - if (!s.isNull()) { - s.replace('\\', '/'); - xasprintf(&info->waypoint_path, "%s/%s", path, CSTR(s)); - } - s = inifile_readstr(inifile, "UGDS", "GcFolder"); - if (!s.isNull()) { - s.replace('\\', '/'); - xasprintf(&info->geo_path, "%s/%s", path, CSTR(s)); - } - s = inifile_readstr(inifile, "UGDS", "TrkFolder"); - if (!s.isNull()) { - s.replace('\\', '/'); - xasprintf(&info->track_path, "%s/%s", path, CSTR(s)); - } - - inifile_done(inifile); - xfree(inipath); - return info; -} - -mag_info* -explorist_ini_get(const char** dirlist) -{ - mag_info* r = nullptr; - while (dirlist && *dirlist) { - r = explorist_ini_try(*dirlist); - if (r) { - return r; - } - } - return r; -} - -void -explorist_ini_done(mag_info* info) -{ - xfree(info->geo_path); - xfree(info->track_path); - xfree(info->waypoint_path); - xfree(info); -} diff --git a/explorist_ini.h b/explorist_ini.h deleted file mode 100644 index 078c0dc83..000000000 --- a/explorist_ini.h +++ /dev/null @@ -1,12 +0,0 @@ - -/* - * Interesting traits of the device from the *.ini files. - */ -struct mag_info { - char* geo_path; - char* track_path; - char* waypoint_path; -}; - -mag_info* explorist_ini_get(const char** directory_list); -void explorist_ini_done(mag_info* info); diff --git a/magellan.h b/magellan.h deleted file mode 100644 index 67f062625..000000000 --- a/magellan.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright (C) 2002-2005 Robert Lipe, robertlipe@usa.net - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - */ - -#ifndef MAGELLAN_H_INCLUDED_ -#define MAGELLAN_H_INCLUDED_ - -#include -#include "defs.h" - -/* - * Table of "interesting" Magellan models. - * Selfishly, if I haven't heard of it, it's not in the table. - * This doesn't mean I actually have TRIED all models listed below. - * (Donations welcome. :-) - */ -enum meridian_model { - mm_unknown = 0 , - mm_gps315320, - mm_map410, - mm_map330, - mm_gps310, - mm_meridian, - mm_sportrak -}; - -struct pid_to_model_t { - meridian_model model; - int pid; - const char* model_n; -}; - -struct magellan_icon_mapping_t { - const char* token; - const char* icon; -}; - -QString mag_find_descr_from_token(const char* token); -QString mag_find_token_from_descr(const QString& icon); - -unsigned int mag_checksum(const char*buf); -QString m330_cleanse(const char* istring); - -Waypoint* mag_trkparse(char* trkmsg); -void mag_rteparse(char* rtemsg); - -#endif // MAGELLAN_H_INCLUDED_ diff --git a/magproto.cc b/magproto.cc deleted file mode 100644 index b52becae6..000000000 --- a/magproto.cc +++ /dev/null @@ -1,1631 +0,0 @@ -/* - Communicate Thales/Magellan serial protocol. - - Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, - 2008, 2010 Robert Lipe, robertlipe+source@gpsbabel.org - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - */ - -#include // for assert -#include // for isprint, toupper -#include // for fabs, lround -#include // for sscanf, size_t -#include // for strtod, strtoul -#include // for strchr, strncmp, strlen, memmove, strrchr, memset - -#include // for QByteArray -#include // for QDateTime -#include // for QDir, operator|, QDir::Files, QDir::Name, QDir::Readable -#include // for QFileInfo -#include // for QFileInfoList -#include // for QLatin1String -#include // for QList -#include // for QScopedPointer -#include // for QString, operator== -#include // for QStringList -#include // for QTime -#include // for QVector -#include // for CaseInsensitive -#include // for qPrintable, foreach - -#include "defs.h" -#include "explorist_ini.h" // for explorist_ini_done, explorist_ini_get, mag_info -#include "format.h" // for Format -#include "gbfile.h" // for gbfclose, gbfeof, gbfgets, gbfopen, gbfwrite, gbfile -#include "gbser.h" // for gbser_deinit, gbser_init, gbser_is_serial, gbser_read_line, gbser_set_port, gbser_write, gbser_OK -#include "magellan.h" // for mm_meridian, mm_sportrak, magellan_icon_mapping_t, mm_gps315320, mm_unknown, mm_map330, mm_map410, pid_to_model_t, mm_gps310, m330_cleanse, mag_checksum, mag_find_descr_from_token, mag_find_token_from_descr, mag_rteparse, mag_trkparse -#include "src/core/datetime.h" // for DateTime -#include "vecs.h" // for Vecs - - -static int bitrate = 4800; -static int wptcmtcnt; -static int wptcmtcnt_max; -static int explorist; -static int broken_sportrak; -#define MYNAME "MAGPROTO" -#define MAXCMTCT 200 - -#define debug_serial (global_opts.debug_level > 1) - -static QString termread(char* ibuf, int size); -static void termwrite(const char* obuf, int size); -static void mag_readmsg(gpsdata_type objective); -static void mag_handon(); -static void mag_handoff(); -static short_handle mkshort_handle = nullptr; -static char* deficon = nullptr; -static char* bs = nullptr; -static char* cmts = nullptr; -static char* noack = nullptr; -static char* nukewpt = nullptr; -static int route_out_count; -static int waypoint_read_count; -static int wpt_len = 8; -static QString curfname; -static int extension_hint; -// For Explorist GC/510/610/710 families, bludgeon in GPX support. -// (This has nothing to do with the Explorist 100...600 products.) -static Format* gpx_vec; -static mag_info* explorist_info; -static QStringList os_gpx_files(const char* dirname); - -/* - * Magellan's firmware is *horribly* slow to send the next packet after - * we turn around an ack while we are reading from the device. It's - * quite spiffy when we're writing to the device. Since we're *way* - * less likely to lose data while reading from it than it is to lose data - * when we write to it, we turn off the acks when we are predominantly - * reading. - */ -static int suppress_ack; - -enum mag_rxstate { - mrs_handoff = 0, - mrs_handon, - mrs_awaiting_ack -}; - -/* - * An individual element of a route. - */ -struct mag_rte_elem { - QString wpt_name; - QString wpt_icon; -}; - -/* - * A header of a route. Related elements of a route belong to this. - */ -struct mag_rte_head_t { - QList elem_list; /* list of child rte_elems */ - char* rte_name{nullptr}; - int nelems{0}; -}; - -static QList rte_wpt_tmp; /* temporary PGMNWPL msgs for routes */ - -static gbfile* magfile_h; -static mag_rxstate magrxstate; -static int mag_error; -static unsigned int last_rx_csum; -static int found_done; -static int got_version; -static int is_file = 0; -static route_head* trk_head; -static int ignore_unable; - -static Waypoint* mag_wptparse(char*); -using cleanse_fn = QString (const char*); -static cleanse_fn* mag_cleanse; -static const char** os_get_magellan_mountpoints(); - -static const magellan_icon_mapping_t gps315_icon_table[] = { - { "a", "filled circle" }, - { "b", "box" }, - { "c", "red buoy" }, - { "d", "green buoy" }, - { "e", "buoy" }, - { "f", "rocks" }, - { "g", "red daymark" }, - { "h", "green daymark" }, - { "i", "bell" }, - { "j", "danger" }, - { "k", "diver down" }, - { "l", "fish" }, - { "m", "house" }, - { "n", "mark" }, - { "o", "car" }, - { "p", "tent" }, - { "q", "boat" }, - { "r", "food" }, - { "s", "fuel" }, - { "t", "tree" }, - { nullptr, nullptr } -}; - -static const magellan_icon_mapping_t map330_icon_table[] = { - { "a", "crossed square" }, - { "b", "box" }, - { "c", "house" }, - { "d", "aerial" }, - { "e", "airport" }, - { "f", "amusement park" }, - { "g", "ATM" }, - { "g", "Bank" }, - { "h", "auto repair" }, - { "i", "boating" }, - { "j", "camping" }, - { "k", "exit ramp" }, - { "l", "first aid" }, - { "m", "nav aid" }, - { "n", "buoy" }, - { "o", "fuel" }, - { "p", "garden" }, - { "q", "golf" }, - { "r", "hotel" }, - { "s", "hunting/fishing" }, - { "t", "large city" }, - { "u", "lighthouse" }, - { "v", "major city" }, - { "w", "marina" }, - { "x", "medium city" }, - { "y", "museum" }, - { "z", "obstruction" }, - { "aa", "park" }, - { "ab", "resort" }, - { "ac", "restaurant" }, - { "ad", "rock" }, - { "ae", "scuba" }, - { "af", "RV service" }, - { "ag", "shooting" }, - { "ah", "sight seeing" }, - { "ai", "small city" }, - { "aj", "sounding" }, - { "ak", "sports arena" }, - { "al", "tourist info" }, - { "am", "truck service" }, - { "an", "winery" }, - { "ao", "wreck" }, - { "ap", "zoo" }, - { "ah", "Virtual cache"}, /* Binos: because you "see" them. */ - { "ak", "Micro-Cache" }, /* Looks like a film canister. */ - { "an", "Multi-Cache"}, /* Winery: grapes 'coz they "bunch" */ - { "s", "Unknown Cache"}, /* 'Surprise' cache: use a target. */ - { "ac", "Event Cache"}, /* Event caches. May be food. */ - { nullptr, nullptr } -}; - -pid_to_model_t pid_to_model[] = { - { mm_gps315320, 19, "ColorTrak" }, - { mm_gps315320, 24, "GPS 315/320" }, - { mm_map410, 25, "Map 410" }, - { mm_map330, 30, "Map 330" }, - { mm_gps310, 31, "GPS 310" }, - { mm_meridian, 33, "Meridian" }, - { mm_meridian, 35, "ProMark 2" }, - { mm_sportrak, 36, "SporTrak Map/Pro" }, - { mm_sportrak, 37, "SporTrak" }, - { mm_meridian, 38, "FX324 Plotter" }, - { mm_meridian, 39, "Meridian Color" }, - { mm_meridian, 40, "FX324C Plotter" }, - { mm_sportrak, 41, "Sportrak Color" }, - { mm_sportrak, 42, "Sportrak Marine" }, - { mm_meridian, 43, "Meridian Marine" }, - { mm_sportrak, 44, "Sportrak Topo" }, - { mm_sportrak, 45, "Mystic" }, - { mm_meridian, 46, "MobileMapper" }, - { mm_meridian, 110, "Explorist 100" }, - { mm_meridian, 111, "Explorist 200" }, - { mm_unknown, 0, nullptr } -}; - -static const magellan_icon_mapping_t* icon_mapping = map330_icon_table; - -/* - * For each receiver type, return a "cleansed" version of the string - * that's valid for a waypoint name or comment. The string should be - * freed when you're done with it. - */ -static QString -m315_cleanse(const char* istring) -{ - char* rstring = (char*) xmalloc(strlen(istring)+1); - char* o; - const char* i; - static char m315_valid_chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789"; - for (o=rstring,i=istring; *i; i++) { - if (strchr(m315_valid_chars, toupper(*i))) { - *o++ = toupper(*i); - } - } - *o = 0; - QString rv(rstring); - xfree(rstring); - return rv; -} - -/* - * Do same for 330, Meridian, and SportTrak. - */ -QString -m330_cleanse(const char* istring) -{ - static char m330_valid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ " - "abcdefghijklmnopqrstuvwxyz" - "0123456789+-.'/!@#<%^&>()=:\\"; - char* rstring = (char*) xmalloc(strlen(istring)+1); - char* o; - const char* i; - - for (o=rstring,i=istring; *i; i++) { - if (strchr(m330_valid_chars, *i)) { - *o++ = *i; - } - } - *o = 0; - QString rv(rstring); - xfree(rstring); - return rv; -} - -/* - * Given a protocol message, compute the checksum as needed by - * the Magellan protocol. - */ -unsigned int -mag_checksum(const char* const buf) -{ - int csum = 0; - - for (const char* p = buf; *p; p++) { - csum ^= *p; - } - - return csum; -} -static unsigned int -mag_pchecksum(const char* const buf, int len) -{ - int csum = 0; - const char* p = buf; - for (; len ; len--) { - csum ^= *p++; - } - return csum; -} - -static void -mag_writemsg(const char* const buf) -{ - unsigned int osum = mag_checksum(buf); - int retry_cnt = 5; - QScopedPointer obuf; - - if (debug_serial) { - warning("WRITE: $%s*%02X\r\n",buf, osum); - } - -retry: - - int i = xasprintf(obuf, "$%s*%02X\r\n",buf, osum); - termwrite(obuf.data(), i); - if (magrxstate == mrs_handon || magrxstate == mrs_awaiting_ack) { - magrxstate = mrs_awaiting_ack; - mag_readmsg(trkdata); - if (last_rx_csum != osum) { - if (debug_serial) { - warning("COMM ERROR: Expected %02x, got %02x", - osum, last_rx_csum); - } - if (retry_cnt--) { - goto retry; - } else { - mag_handoff(); - fatal(MYNAME - ": Too many communication errors.\n"); - } - } - } -} - -static void -mag_writeack(int osum) -{ - QScopedPointer nbuf; - QScopedPointer obuf; - - if (is_file) { - return; - } - - (void) xasprintf(nbuf, "PMGNCSM,%02X", osum); - unsigned int nsum = mag_checksum(nbuf.data()); - int i = xasprintf(obuf, "$%s*%02X\r\n",nbuf.data(), nsum); - - if (debug_serial) { - warning("ACK WRITE: %s",obuf.data()); - } - /* - * Don't call mag_writemsg here so we don't get into ack feedback - * loops. - */ - termwrite(obuf.data(), i); -} - -static void -mag_handon() -{ - if (!is_file) { - mag_writemsg("PMGNCMD,HANDON"); - } - magrxstate = mrs_handon; - -} - -static void -mag_handoff() -{ - if (!is_file) { - mag_writemsg("PMGNCMD,HANDOFF"); - } - magrxstate = mrs_handoff; -} - -static void -mag_verparse(char* ibuf) -{ - int prodid = mm_unknown; - char version[1024]; - pid_to_model_t* pp = pid_to_model; - - got_version = 1; - sscanf(ibuf,"$PMGNVER,%d,%[^,]", &prodid, version); - - for (pp = pid_to_model; pp->model != mm_unknown; pp++) { - if (pp->pid == prodid) { - break; - } - } - - if (prodid == 37) { - broken_sportrak = 1; - } - - switch (pp->model) { - case mm_gps315320: - case mm_map410: - icon_mapping = gps315_icon_table; - setshort_length(mkshort_handle, 6); - setshort_mustupper(mkshort_handle, 1); - mag_cleanse = m315_cleanse; - break; - case mm_map330: - case mm_meridian: - case mm_sportrak: - icon_mapping = map330_icon_table; - setshort_length(mkshort_handle, wpt_len); - setshort_mustupper(mkshort_handle, 0); - mag_cleanse = m330_cleanse; - break; - default: - fatal(MYNAME ": Unknown receiver type %d, model version '%s'.\n", prodid, version); - } -} - -#define IS_TKN(x) (strncmp(ibuf,x, sizeof(x)-1) == 0) - -static void -mag_readmsg(gpsdata_type objective) -{ - char ibuf[512]; /* oliskoli: corrupted data (I've seen descr with a lot - of escaped FFFFFFFF) may need more size */ - int retrycnt = 20; - -retry: - QString gr = termread(ibuf, sizeof(ibuf)); - - if (gr.isEmpty()) { - if (!got_version) { - /* - * The 315 can take up to six seconds to respond to - * a VERSION command. Since this is on startup, - * we'll be fairly persistent in retrying. - */ - if (retrycnt--) { - goto retry; - } else { - fatal(MYNAME ": No data received from GPS.\n"); - } - } else { - if (is_file) { - found_done = 1; - } - return; - } - } - - /* If column zero isn't a dollar sign, it's not for us */ - if (ibuf[0] != '$') { - fatal(MYNAME ": line doesn't start with '$'.\n"); - } - - - int isz = strlen(ibuf); - - if (isz < 5) { - if (debug_serial) { - warning("SHORT READ %d\n", isz); - } - return; - } - mag_error = 0; - while (!isprint(ibuf[isz])) { - isz--; - } - char* isump = &ibuf[isz-1]; - unsigned int isum = strtoul(isump, nullptr,16); - if (isum != mag_pchecksum(&ibuf[1], isz-3)) { - if (debug_serial) { - warning("RXERR %02x/%02x: '%s'\n", isum, mag_pchecksum(&ibuf[1],isz-5), ibuf); - } - /* Special case receive errors early on. */ - if (!got_version) { - fatal(MYNAME ": bad communication. Check bit rate.\n"); - } - } - if (debug_serial) { - warning("READ: %s\n", ibuf); - } - if (IS_TKN("$PMGNCSM,")) { - last_rx_csum = strtoul(&ibuf[9], nullptr, 16); - magrxstate = mrs_handon; - return; - } - if (strncmp(ibuf, "$PMGNWPL,", 7) == 0) { - Waypoint* wpt = mag_wptparse(ibuf); - waypoint_read_count++; - if (global_opts.verbose_status) { - waypt_status_disp(waypoint_read_count, - waypoint_read_count); - } - - if (extension_hint) { - if (extension_hint == WPTDATAMASK) { - waypt_add(wpt); - } else if (extension_hint == RTEDATAMASK) { - rte_wpt_tmp.append(wpt); - } - } else { - switch (objective) { - case wptdata: - waypt_add(wpt); - break; - case rtedata: - rte_wpt_tmp.append(wpt); - break; - default: - break; - } - } - } - if (strncmp(ibuf, "$PMGNTRK,", 7) == 0) { - Waypoint* wpt = mag_trkparse(ibuf); - /* - * Allow lazy allocation of track head. - */ - if (trk_head == nullptr) { - /* These tracks don't have names, so derive one - * from input filename. - */ - - trk_head = new route_head; - - /* Whack trailing extension if present. */ - QString s = get_filename(curfname); - int idx = s.indexOf('.'); - if (idx > 0) { - s.truncate(idx); - } - - trk_head->rte_name = s; - track_add_head(trk_head); - } - - track_add_wpt(trk_head, wpt); - } - if (strncmp(ibuf, "$PMGNRTE,", 7) == 0) { - mag_rteparse(ibuf); - } - if (IS_TKN("$PMGNVER,")) { - mag_verparse(ibuf); - } - mag_error = 0; - if (!ignore_unable && IS_TKN("$PMGNCMD,UNABLE")) { - warning("Unable to send\n"); - found_done = 1; - mag_error = 1; - ignore_unable = 0; - return; - } - if (IS_TKN("$PMGNCMD,END") || (is_file && (gbfeof(magfile_h)))) { - found_done = 1; - return; - } - - if (magrxstate != mrs_handoff) { - mag_writeack(isum); - } -} - -static void* serial_handle = nullptr; - -static int -terminit(const QString& portname, int create_ok) -{ - if (gbser_is_serial(qPrintable(portname))) { - if (serial_handle = gbser_init(qPrintable(portname)), nullptr != serial_handle) { - int rc; - if (rc = gbser_set_port(serial_handle, bitrate, 8, 0, 1), gbser_OK != rc) { - fatal(MYNAME ": Can't configure port\n"); - } - } - is_file = 0; - if (serial_handle == nullptr) { - fatal(MYNAME ": Could not open serial port %s\n", qPrintable(portname)); - } - return 1; - } else { - /* Does this check for an error? */ - magfile_h = gbfopen(portname, create_ok ? "w+b" : "rb", MYNAME); - is_file = 1; - icon_mapping = map330_icon_table; - mag_cleanse = m330_cleanse; - got_version = 1; - return 0; - } -} - -static QString termread(char* ibuf, int size) -{ - if (is_file) { - return gbfgets(ibuf, size, magfile_h); - } else { - int rc = gbser_read_line(serial_handle, ibuf, size, 2000, 0x0a, 0x0d); - if (rc != gbser_OK) { - fatal(MYNAME ": Read error\n"); - } - return ibuf; - } -} - -/* Though not documented in the protocol spec, if the unit itself - * wants to create a field containing a comma, it will encode it - * as 2C. We extrapolate that any 2 digit hex encoding may - * be valid. We don't do this in termread() since we need to do it - * after the scanf. This means we have to do it field-by-field - * basis. - * - * The buffer is modified in place and shortened by copying the remaining - * string including the terminator. - */ -static -void -mag_dequote(char* ibuf) -{ - char* esc = nullptr; - - while ((esc = strchr(ibuf, 0x1b))) { - int nremains = strlen(esc); - if (nremains >= 3) { - static const char hex[17] = "0123456789ABCDEF"; - const char* c1 = strchr(hex, esc[1]); - const char* c2 = strchr(hex, esc[2]); - if (c1 && c2) { - int escv = (c1 - hex) * 16 + (c2 - hex); - if (escv == 255) { /* corrupted data */ - char* tmp = esc + 1; - while (*tmp == 'F') { - tmp++; - } - memmove(esc, tmp, strlen(tmp) + 1); - } else { - *esc++ = (isprint(escv)) ? escv : '$'; - /* buffers overlap */ - memmove(esc, esc+2, nremains - 2); - } - } - } else { - *esc = '\0'; /* trim corrupted data, - otherwise we get an endless loop */ - } - } -} - -static void -termwrite(const char* obuf, int size) -{ - if (is_file) { - size_t nw; - if (nw = gbfwrite(obuf, 1, size, magfile_h), nw < (size_t) size) { - fatal(MYNAME ": Write error"); - } - } else { - int rc; - if (rc = gbser_write(serial_handle, obuf, size), rc < 0) { - fatal(MYNAME ": Write error"); - } - } -} - -static void termdeinit() -{ - if (is_file) { - gbfclose(magfile_h); - magfile_h = nullptr; - } else { - gbser_deinit(serial_handle); - serial_handle = nullptr; - } -} - -/* - * Arg tables are doubled up so that -? can output appropriate help - */ -static -QVector mag_sargs = { - { - "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING, - ARG_NOMINMAX, nullptr - }, - { - "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)", - "200", ARGTYPE_INT, ARG_NOMINMAX, nullptr - }, - { - "baud", &bs, "Numeric value of bitrate (baud=4800)", "4800", - ARGTYPE_INT, ARG_NOMINMAX, nullptr - }, - { - "noack", &noack, "Suppress use of handshaking in name of speed", - nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "nukewpt", &nukewpt, "Delete all waypoints", nullptr, ARGTYPE_BOOL, - ARG_NOMINMAX, nullptr - }, -}; - -static -QVector mag_fargs = { - { - "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING, - ARG_NOMINMAX, nullptr - }, - { - "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)", - nullptr, ARGTYPE_INT, ARG_NOMINMAX, nullptr - }, -}; - -/* - * The part of the serial init that's common to read and write. - */ -static void -mag_serial_init_common(const QString& portname) -{ - if (is_file) { - return; - } - - mag_handoff(); - if (!noack && !suppress_ack) { - mag_handon(); - } - - time_t now = current_time().toTime_t(); - /* - * The 315 can take up to 4.25 seconds to respond to initialization - * commands. Time out on the side of caution. - */ - time_t later = now + 6; - got_version = 0; - mag_writemsg("PMGNCMD,VERSION"); - - while (!got_version) { - mag_readmsg(trkdata); - if (current_time().toTime_t() > later) { - fatal(MYNAME ": No acknowledgment from GPS on %s\n", - qPrintable(portname)); - } - } - - if ((icon_mapping != gps315_icon_table)) { - /* - * The 315 can't handle this command, so we set a global - * to ignore the NAK on it. - */ - ignore_unable = 1; - mag_writemsg("PMGNCMD,NMEAOFF"); - ignore_unable = 0; - } - - if (nukewpt) { - /* The unit will send us an "end" message upon completion */ - mag_writemsg("PMGNCMD,DELETE,WAYPOINT"); - mag_readmsg(trkdata); - if (!found_done) { - fatal(MYNAME ": Unexpected response to waypoint delete command.\n"); - } - found_done = 0; - } - -} -static void -mag_rd_init_common(const QString& portname) -{ - waypoint_read_count = 0; - // For Explorist GC, intercept the device access and redirect to GPX. - // We actually do the rd_init() inside read as we may have multiple - // files that we have to read. - if (portname == "usb:") { - const char** dlist = os_get_magellan_mountpoints(); - explorist_info = explorist_ini_get(dlist); - if (explorist_info) { - gpx_vec = Vecs::Instance().find_vec("gpx"); - } - return; - } - - if (bs) { - bitrate=xstrtoi(bs, nullptr, 10); - } - - if (!mkshort_handle) { - mkshort_handle = mkshort_new_handle(); - } - - terminit(portname, 0); - mag_serial_init_common(portname); - - rte_wpt_tmp.clear(); - - /* find the location of the tail of the path name, - * make a copy of it, then lop off the file extension - */ - - curfname = get_filename(portname); - - /* - * I'd rather not derive behaviour from filenames but since - * we can't otherwise tell if we should put a WPT on the route - * queue or the WPT queue in the presence of (-w -r -t) we - * divine a hint from the filename extension when we can. - */ - QString exten = QFileInfo(curfname).suffix(); - if (exten.length() > 0) { - if (0 == exten.compare(QLatin1String("upt"), Qt::CaseInsensitive)) { - extension_hint = WPTDATAMASK; - } else if (0 == exten.compare(QLatin1String("log"), Qt::CaseInsensitive)) { - extension_hint = TRKDATAMASK; - } else if (0 == exten.compare(QLatin1String("rte"), Qt::CaseInsensitive)) { - extension_hint = RTEDATAMASK; - } - } - -} - -static void -mag_rd_init(const QString& portname) -{ - explorist = 0; - suppress_ack = 1; - mag_rd_init_common(portname); -} - -static void -magX_rd_init(const QString& portname) -{ - explorist = 1; - mag_rd_init_common(portname); -} - -static void -mag_wr_init_common(const QString& portname) -{ - suppress_ack = 0; - if (bs) { - bitrate=xstrtoi(bs, nullptr, 10); - } - - if (waypt_count() > 500) { - fatal(MYNAME ": Meridian/Explorist does not support more than 500 waypoints in one file. Only\n200 waypoints may have comments.\nDecrease the number of waypoints sent.\n"); - } - - if (cmts) { - wptcmtcnt_max = xstrtoi(cmts, nullptr, 10); - } else { - wptcmtcnt_max = MAXCMTCT ; - } - - if (!mkshort_handle) { - mkshort_handle = mkshort_new_handle(); - } - - terminit(portname, 1); - mag_serial_init_common(portname); - - rte_wpt_tmp.clear(); -} - -/* - * Entry point for extended (explorist) points. - */ -static void -magX_wr_init(const QString& portname) -{ - wpt_len = 20; - explorist = 1; - mag_wr_init_common(portname); - setshort_length(mkshort_handle, wpt_len); - setshort_whitespace_ok(mkshort_handle, 1); -} - -static void -mag_wr_init(const QString& portname) -{ - explorist = 0; - wpt_len = 8; - mag_wr_init_common(portname); - /* - * Whitespace is actually legal, but since waypoint name length is - * only 8 bytes, we'll conserve them. - */ - - setshort_whitespace_ok(mkshort_handle, 0); -} - -static void -mag_deinit() -{ - if (explorist_info) { - explorist_ini_done(explorist_info); - return; - } - mag_handoff(); - termdeinit(); - if (mkshort_handle) { - mkshort_del_handle(&mkshort_handle); - } - - while (!rte_wpt_tmp.isEmpty()) { - delete rte_wpt_tmp.takeFirst(); - } - - trk_head = nullptr; - - curfname.clear(); -} - -static void -mag_wr_deinit() -{ - if (explorist) { - mag_writemsg("PMGNCMD,END"); - } - mag_deinit(); -} - -/* - * I'm tired of arguing with scanf about optional fields . Detokenize - * an incoming string that may contain empty fields. - * - * Probably should be cleaned up and moved to common code, but - * making it deal with an arbitrary number of fields of arbitrary - * size is icky. We don't have to solve the general case here... - */ - -static char ifield[20][100]; -static -void parse_istring(char* istring) -{ - int f = 0; - int n; - while (istring[0]) { - char* fp = ifield[f]; - int x = sscanf(istring, "%[^,]%n", fp, &n); - f++; - if (x) { - istring += n; - /* IF more in this string, skip delim */ - if (istring[0]) { - istring++; - } - } else { - istring ++; - } - } -} - -/* - * Given an incoming track messages of the form: - * $PMGNTRK,3605.259,N,08644.389,W,00151,M,201444.61,A,,020302*66 - * create and return a populated waypoint. - */ -Waypoint* -mag_trkparse(char* trkmsg) -{ - int hms; - int fracsecs; - struct tm tm; - - auto* waypt = new Waypoint; - - memset(&tm, 0, sizeof(tm)); - - /* - * As some of the fields are optional, sscanf works badly - * for us. - */ - parse_istring(trkmsg); - double latdeg = strtod(ifield[1], nullptr); - char latdir = ifield[2][0]; - double lngdeg = strtod(ifield[3], nullptr); - char lngdir = ifield[4][0]; - int alt = strtod(ifield[5], nullptr); - char altunits = ifield[6][0]; - (void)altunits; - sscanf(ifield[7], "%d.%d", &hms, &fracsecs); - /* Field 8 is constant */ - /* Field nine is optional track name */ - int dmy = xstrtoi(ifield[10], nullptr, 10); - int sec = hms % 100; - hms = hms / 100; - int min = hms % 100; - hms = hms / 100; - int hour = hms % 100; - - int year = 100 + dmy % 100 + 1900; - dmy = dmy / 100; - int mon = dmy % 100; - dmy = dmy / 100; - int day = dmy % 100; - QDateTime dt(QDate(year, mon, day), QTime(hour, min, sec, fracsecs * 10), Qt::UTC); - waypt->SetCreationTime(dt); - - if (latdir == 'S') { - latdeg = -latdeg; - } - waypt->latitude = ddmm2degrees(latdeg); - - if (lngdir == 'W') { - lngdeg = -lngdeg; - } - waypt->longitude = ddmm2degrees(lngdeg); - - waypt->altitude = alt; - - return waypt; - -} - -/* - * Given an incoming route messages of the form: - * $PMGNRTE,4,1,c,1,DAD,a,Anna,a*61 - * generate a route. - */ -void -mag_rteparse(char* rtemsg) -{ - int n; - int frags,frag,rtenum; - char xbuf[100],next_stop[100],abuf[100]; - char* currtemsg; - static mag_rte_head_t* mag_rte_head; - -#if 0 - sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n", - &frags,&frag,xbuf,&rtenum,&n); -#else - sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n", - &frags,&frag,xbuf,&rtenum,&n); - - /* Explorist has a route name here */ - QString rte_name; - if (explorist) { - char* ca = rtemsg + n; - if (*ca++ != ',') { - fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg); - } - - char* ce = strchr(ca, ','); - if (ce == nullptr) { - fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg); - } - - if (ca == ce) { - rte_name = "Route"; - rte_name += QString::number(rtenum); - } else { - rte_name = ca; - rte_name.truncate(ce-ca); - } - - n += ((ce - ca) + 1); - } - -#endif - - /* - * This is the first component of a route. Allocate a new - * head. - */ - if (frag == 1) { - mag_rte_head = new mag_rte_head_t; - mag_rte_head->nelems = frags; - } - - currtemsg = rtemsg + n; - - /* - * The individual line may contain several route elements. - * loop and pick those up. - */ - while (sscanf(currtemsg,",%[^,],%[^,]%n",next_stop, abuf,&n)) { - if ((next_stop[0] == 0) || (next_stop[0] == '*')) { - break; - } - - /* trim CRC from waypoint icon string */ - if (char* p = strchr(abuf, '*'); p != nullptr) { - *p = '\0'; - } - - auto* rte_elem = new mag_rte_elem; - - rte_elem->wpt_name = next_stop; - rte_elem->wpt_icon = abuf; - - mag_rte_head->elem_list.append(rte_elem); - - /* Sportrak (the non-mapping unit) creates malformed - * RTE sentence with no icon info after the routepoint - * name. So if we saw an "icon" treat that as new - * routepoint. - */ - if (broken_sportrak && abuf[0]) { - rte_elem = new mag_rte_elem; - rte_elem->wpt_name = abuf; - - mag_rte_head->elem_list.append(rte_elem); - } - - next_stop[0] = 0; - currtemsg += n; - } - - /* - * If this was the last fragment of the route, add it to the - * gpsbabel internal structs now. - */ - if (frag == mag_rte_head->nelems) { - - auto* rte_head = new route_head; - route_add_head(rte_head); - rte_head->rte_num = rtenum; - rte_head->rte_name = rte_name; - - /* - * It is quite feasible that we have 200 waypoints, - * 3 of which are used in the route. We'll need to find - * those in the queue for SD routes... - */ - - while (!mag_rte_head->elem_list.isEmpty()) { - mag_rte_elem* re = mag_rte_head->elem_list.takeFirst(); - - /* - * Copy route points from temp wpt queue. - */ - foreach (const Waypoint* waypt, rte_wpt_tmp) { - if (waypt->shortname == re->wpt_name) { - auto* wpt = new Waypoint(*waypt); - route_add_wpt(rte_head, wpt); - break; - } - } - - delete re; - } - delete mag_rte_head; - } -} - -QString -mag_find_descr_from_token(const char* token) -{ - if (icon_mapping == nullptr) { - return "unknown"; - } - - for (const magellan_icon_mapping_t* i = icon_mapping; i->token; i++) { - if (token[0] == 0) { - break; - } - if (case_ignore_strcmp(token, i->token) == 0) { - return i->icon; - } - } - return icon_mapping[0].icon; -} - -QString -mag_find_token_from_descr(const QString& icon) -{ - const magellan_icon_mapping_t* i = icon_mapping; - - if (i == nullptr || icon == nullptr) { - return "a"; - } - - for (i = icon_mapping; i->token; i++) { - if (icon.compare(i->icon, Qt::CaseInsensitive) == 0) { - return i->token; - } - } - return icon_mapping[0].token; -} - -/* - * Given an incoming waypoint messages of the form: - * $PMGNWPL,3549.499,N,08650.827,W,0000257,M,HOME,HOME,c*4D - * create and return a populated waypoint. - */ -static Waypoint* -mag_wptparse(char* trkmsg) -{ - double latdeg, lngdeg; - char latdir; - char lngdir; - int alt; - char altunits; - char shortname[100]; - char descr[256]; - char icon_token[100]; - int i = 0; - - descr[0] = 0; - icon_token[0] = 0; - - auto* waypt = new Waypoint; - - sscanf(trkmsg,"$PMGNWPL,%lf,%c,%lf,%c,%d,%c,%[^,],%[^,]", - &latdeg,&latdir, - &lngdeg,&lngdir, - &alt,&altunits,shortname,descr); - char* icone = strrchr(trkmsg, '*'); - char* icons = strrchr(trkmsg, ',')+1; - - mag_dequote(descr); - - for (char* blah = icons ; blah < icone; blah++) { - icon_token[i++] = *blah; - } - icon_token[i++] = '\0'; - - if (latdir == 'S') { - latdeg = -latdeg; - } - waypt->latitude = ddmm2degrees(latdeg); - - if (lngdir == 'W') { - lngdeg = -lngdeg; - } - waypt->longitude = ddmm2degrees(lngdeg); - - waypt->altitude = alt; - waypt->shortname = shortname; - waypt->description = descr; - waypt->icon_descr = mag_find_descr_from_token(icon_token); - - return waypt; -} - -static void -mag_read() -{ - if (gpx_vec) { - QStringList f = os_gpx_files(explorist_info->track_path); - for (const auto& file : qAsConst(f)) { - gpx_vec->rd_init(file); - gpx_vec->read(); - gpx_vec->rd_deinit(); - } - - f = os_gpx_files(explorist_info->waypoint_path); - for (const auto& file : qAsConst(f)) { - gpx_vec->rd_init(file); - gpx_vec->read(); - gpx_vec->rd_deinit(); - } -#if 0 - f = os_gpx_files(explorist_info->geo_path); - for (const auto& file : qAsConst(f)) { - gpx_vec->rd_init(file); - gpx_vec->read(); - gpx_vec->rd_deinit(); - } -#endif - return; - } - - found_done = 0; - if (global_opts.masked_objective & TRKDATAMASK) { - magrxstate = mrs_handoff; - if (!is_file) { - mag_writemsg("PMGNCMD,TRACK,2"); - } - - while (!found_done) { - mag_readmsg(trkdata); - } - } - - found_done = 0; - if (global_opts.masked_objective & WPTDATAMASK) { - magrxstate = mrs_handoff; - if (!is_file) { - mag_writemsg("PMGNCMD,WAYPOINT"); - } - - while (!found_done) { - mag_readmsg(wptdata); - } - } - - found_done = 0; - if (global_opts.masked_objective & RTEDATAMASK) { - magrxstate = mrs_handoff; - if (!is_file) { - /* - * serial routes require waypoint & routes - * messages commands. - */ - mag_writemsg("PMGNCMD,WAYPOINT"); - - while (!found_done) { - mag_readmsg(rtedata); - } - - mag_writemsg("PMGNCMD,ROUTE"); - - found_done = 0; - while (!found_done) { - mag_readmsg(rtedata); - } - } else { - /* - * SD routes are a stream of PMGNWPL and - * PMGNRTE messages, in that order. - */ - while (!found_done) { - mag_readmsg(rtedata); - } - } - } -} - -static -void -mag_waypt_pr(const Waypoint* waypointp) -{ - QScopedPointer obuf; - QScopedPointer ofmtdesc; - QString icon_token; - - double ilat = waypointp->latitude; - double ilon = waypointp->longitude; - - double lon = fabs(ilon); - double lat = fabs(ilat); - - int lon_deg = lon; - int lat_deg = lat; - - lon = (lon - lon_deg) * 60.0; - lat = (lat - lat_deg) * 60.0; - - lon = (lon_deg * 100.0 + lon); - lat = (lat_deg * 100.0 + lat); - - if (deficon) { - icon_token = mag_find_token_from_descr(deficon); - } else { - icon_token = mag_find_token_from_descr(waypointp->icon_descr); - } - - if (!get_cache_icon(waypointp).isEmpty()) { - icon_token = mag_find_token_from_descr(get_cache_icon(waypointp)); - } - - QString isrc = waypointp->notes.isEmpty() ? waypointp->description : waypointp->notes; - QString owpt = global_opts.synthesize_shortnames ? - mkshort_from_wpt(mkshort_handle, waypointp) : waypointp->shortname; - QString odesc = isrc; - owpt = mag_cleanse(CSTRc(owpt)); - - if (global_opts.smart_icons && - waypointp->gc_data->diff && waypointp->gc_data->terr) { - // It's a string and compactness counts, so "1.0" is OK to be "10". - xasprintf(ofmtdesc, "%ud/%ud %s", waypointp->gc_data->diff, - waypointp->gc_data->terr, CSTRc(odesc)); - odesc = mag_cleanse(ofmtdesc.data()); - } else { - odesc = mag_cleanse(CSTRc(odesc)); - } - - /* - * For the benefit of DirectRoute (which uses waypoint comments - * to deliver turn-by-turn popups for street routing) allow a - * cap on the comments delivered so we leave space for it to route. - */ - if (!odesc.isEmpty() && (wptcmtcnt++ >= wptcmtcnt_max)) { - odesc.clear(); - } - - xasprintf(obuf, "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s", - lat, ilat < 0 ? 'S' : 'N', - lon, ilon < 0 ? 'W' : 'E', - waypointp->altitude == unknown_alt ? - 0 : waypointp->altitude, - wpt_len, - CSTRc(owpt), - CSTRc(odesc), - CSTR(icon_token)); - mag_writemsg(obuf.data()); - - if (!is_file) { - if (mag_error) { - warning("Protocol error Writing '%s'\n", obuf.data()); - } - } -} - -static -void mag_track_disp(const Waypoint* waypointp) -{ - QScopedPointer obuf; - - double ilat = waypointp->latitude; - double ilon = waypointp->longitude; - - QByteArray dmy(""); - QByteArray hms(""); - if (waypointp->creation_time.isValid()) { - // Round to hundredths of seconds before conversion to string. - // Rounding can ripple all the way from the msec to the year. - QDateTime dt = waypointp->GetCreationTime().toUTC(); - dt = dt.addMSecs(10 * lround(dt.time().msec()/10.0) - dt.time().msec()); - assert((dt.time().msec() % 10) == 0); - dmy = dt.toString(u"ddMMyy").toUtf8(); - hms = dt.toString(u"hhmmss.zzz").left(9).toUtf8(); - } - - double lon = fabs(ilon); - double lat = fabs(ilat); - - int lon_deg = lon; - int lat_deg = lat; - - lon = (lon - lon_deg) * 60.0; - lat = (lat - lat_deg) * 60.0; - - lon = (lon_deg * 100.0 + lon); - lat = (lat_deg * 100.0 + lat); - - xasprintf(obuf,"PMGNTRK,%4.3f,%c,%09.3f,%c,%05.0f,%c,%s,A,,%s", - lat, ilat < 0 ? 'S' : 'N', - lon, ilon < 0 ? 'W' : 'E', - waypointp->altitude == unknown_alt ? - 0 : waypointp->altitude, - 'M', hms.constData(), dmy.constData()); - mag_writemsg(obuf.data()); -} - -static -void mag_track_pr() -{ - track_disp_all(nullptr, nullptr, mag_track_disp); -} - -/* -The spec says to stack points: - $PMGNRTE,2,1,c,1,FOO,POINT1,b,POINT2,c,POINT3,d*6C - -Meridian SD card and serial (at least) writes in pairs: - $PMGNRTE,4,1,c,1,HOME,c,I49X73,a*15 - ... - $PMGNRTE,4,4,c,1,RON273,a,MYCF93,a*7B - -The spec also says that some units don't like single-legged pairs, -and to replace the 2nd name with "<<>>", but I haven't seen one of those. -*/ - -static void -mag_route_trl(const route_head* rte) -{ - QScopedPointer obuff; - QString buff1; - QString buff2; - QString icon_token; - - /* count waypoints for this route */ - int i = rte->rte_waypt_ct(); - - /* number of output PMGNRTE messages at 2 points per line */ - int numlines = (i / 2) + (i % 2); - - /* increment the route counter. */ - route_out_count++; - - int thisline = i = 0; - foreach (const Waypoint* waypointp, rte->waypoint_list) { - i++; - - if (deficon) { - icon_token = mag_find_token_from_descr(deficon); - } else { - icon_token = mag_find_token_from_descr(waypointp->icon_descr); - } - - QString* pbuff; - if (i == 1) { - pbuff = &buff1; - } else { - pbuff = &buff2; - } - // Write name, icon tuple into alternating buff1/buff2 buffer. - *pbuff = waypointp->shortname + ',' + icon_token; - - if ((waypointp == rte->waypoint_list.back()) || ((i % 2) == 0)) { - QString expbuf; - thisline++; - if (explorist) { - expbuf = rte->rte_name + ','; - } - - xasprintf(obuff, "PMGNRTE,%d,%d,c,%d,%s%s,%s", - numlines, thisline, - rte->rte_num ? rte->rte_num : route_out_count, - CSTRc(expbuf), - CSTR(buff1), CSTR(buff2)); - - mag_writemsg(obuff.data()); - buff1.clear(); - buff2.clear(); - i = 0; - } - } -} - -static void -mag_route_pr() -{ - route_out_count = 0; - route_disp_all(nullptr, mag_route_trl, mag_waypt_pr); - -} - -static void -mag_write() -{ - - wptcmtcnt = 0; - - switch (global_opts.objective) { - case trkdata: - mag_track_pr(); - break; - case wptdata: - waypt_disp_all(mag_waypt_pr); - break; - case rtedata: - mag_route_pr(); - break; - default: - fatal(MYNAME ": Unknown objective.\n"); - } -} - -const char** os_get_magellan_mountpoints() -{ -#if __APPLE__ - const char** dlist = (const char**) xcalloc(2, sizeof *dlist); - dlist[0] = xstrdup("/Volumes/Magellan"); - dlist[1] = nullptr; - return dlist; -#else - fatal("Not implemented"); - return nullptr; -#endif -} - -static QStringList -os_gpx_files(const char* dirname) -{ - QDir dir(dirname); - - const QFileInfoList filist = dir.entryInfoList(QStringList("*.gpx"), QDir::Files | QDir::Readable, QDir::Name); - QStringList rv; - for (const auto& fi : filist) { - rv.append(fi.absoluteFilePath()); - } - return rv; -} - -/* - * This is repeated just so it shows up as separate menu options - * for the benefit of GUI wrappers. - */ -ff_vecs_t mag_svecs = { - ff_type_serial, - FF_CAP_RW_ALL, - mag_rd_init, - mag_wr_init, - mag_deinit, - mag_deinit, - mag_read, - mag_write, - nullptr, - &mag_sargs, - CET_CHARSET_ASCII, 0, /* CET-REVIEW */ - NULL_POS_OPS -}; - -ff_vecs_t mag_fvecs = { - ff_type_file, - FF_CAP_RW_ALL, - mag_rd_init, - mag_wr_init, - mag_deinit, - mag_deinit, - mag_read, - mag_write, - nullptr, - &mag_fargs, - CET_CHARSET_ASCII, 0, /* CET-REVIEW */ - NULL_POS_OPS -}; - -/* - * Extended (Explorist) entry tables. - */ -ff_vecs_t magX_fvecs = { - ff_type_file, - FF_CAP_RW_ALL, - magX_rd_init, - magX_wr_init, - mag_deinit, - mag_wr_deinit, - mag_read, - mag_write, - nullptr, - &mag_fargs, - CET_CHARSET_ASCII, 0, /* CET-REVIEW */ - NULL_POS_OPS -}; diff --git a/reference/dusky.gnuplot b/reference/dusky.gnuplot index 573facd3c..27c10e6ec 100644 --- a/reference/dusky.gnuplot +++ b/reference/dusky.gnuplot @@ -29,7 +29,7 @@ plot "-" with lines 99.9720 905.512 100.1892 902.231 100.5724 882.546 -102.0104 882.546 +102.0103 882.546 102.8041 875.984 103.0804 872.703 107.3975 921.916 diff --git a/reference/dusky.gpx b/reference/dusky.gpx new file mode 100644 index 000000000..8cd9778c8 --- /dev/null +++ b/reference/dusky.gpx @@ -0,0 +1,262 @@ + + + + + + dusky + + + 190.000 + + + + 217.000 + + + + 218.000 + + + + 218.000 + + + + 215.000 + + + + 201.000 + + + + 185.000 + + + + 186.000 + + + + 190.000 + + + + 188.000 + + + + 192.000 + + + + 195.000 + + + + 196.000 + + + + 195.000 + + + + 195.000 + + + + 196.000 + + + + 187.000 + + + + 199.000 + + + + 202.000 + + + + 213.000 + + + + 225.000 + + + + 233.000 + + + + 220.000 + + + + 221.000 + + + + 233.000 + + + + 275.000 + + + + 276.000 + + + + 276.000 + + + + 275.000 + + + + 269.000 + + + + 269.000 + + + + 267.000 + + + + 266.000 + + + + 281.000 + + + + 279.000 + + + + 298.000 + + + + 297.000 + + + + 306.000 + + + + 295.000 + + + + 330.000 + + + + 317.000 + + + + 314.000 + + + + 322.000 + + + + 332.000 + + + + 328.000 + + + + 304.000 + + + + 297.000 + + + + 296.000 + + + + 311.000 + + + + 285.000 + + + + 276.000 + + + + 275.000 + + + + 271.000 + + + + 266.000 + + + + 258.000 + + + + 265.000 + + + + 267.000 + + + + 258.000 + + + + 261.000 + + + + 264.000 + + + + 265.000 + + + + 262.000 + + + + 259.000 + + + + + diff --git a/reference/format0.txt b/reference/format0.txt index 91981bc48..6d10d5cd0 100644 --- a/reference/format0.txt +++ b/reference/format0.txt @@ -36,9 +36,6 @@ html html HTML Output humminbird_ht ht Humminbird tracks (.ht) humminbird hwr Humminbird waypoints and routes (.hwr) lowranceusr usr Lowrance USR -magellanx upt Magellan SD files (as for eXplorist) -magellan Magellan SD files (as for Meridian) -magellan Magellan serial protocol miniHomer MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI) garmin_xt Mobile Garmin XT Track files mtk-bin bin MTK Logger (iBlue 747,...) Binary File Format diff --git a/reference/format1.txt b/reference/format1.txt index 70140cb00..cc4d87520 100644 --- a/reference/format1.txt +++ b/reference/format1.txt @@ -42,9 +42,6 @@ file humminbird_ht ht Humminbird tracks (.ht) file humminbird hwr Humminbird waypoints and routes (.hwr) internal random Internal GPS data generator file lowranceusr usr Lowrance USR -file magellanx upt Magellan SD files (as for eXplorist) -file magellan Magellan SD files (as for Meridian) -serial magellan Magellan serial protocol serial miniHomer MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI) file garmin_xt Mobile Garmin XT Track files file mtk-bin bin MTK Logger (iBlue 747,...) Binary File Format diff --git a/reference/format2.txt b/reference/format2.txt index 70877eecb..713235226 100644 --- a/reference/format2.txt +++ b/reference/format2.txt @@ -42,9 +42,6 @@ file r-rwr- humminbird_ht ht Humminbird tracks (.ht) file rwr-rw humminbird hwr Humminbird waypoints and routes (.hwr) internal r-r-r- random Internal GPS data generator file rwrwrw lowranceusr usr Lowrance USR -file rwrwrw magellanx upt Magellan SD files (as for eXplorist) -file rwrwrw magellan Magellan SD files (as for Meridian) -serial rwrwrw magellan Magellan serial protocol serial r-r--- miniHomer MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI) file --r--- garmin_xt Mobile Garmin XT Track files file r-r--- mtk-bin bin MTK Logger (iBlue 747,...) Binary File Format diff --git a/reference/format3.txt b/reference/format3.txt index f0f8b6081..70596a81f 100644 --- a/reference/format3.txt +++ b/reference/format3.txt @@ -508,30 +508,6 @@ option lowranceusr serialnum (USR output) Device serial number integer 0 https option lowranceusr description (USR output) Output file content description string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_lowranceusr.html#fmt_lowranceusr_o_description -file rwrwrw magellanx upt Magellan SD files (as for eXplorist) magellanx - https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html -option magellanx deficon Default icon name string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html#fmt_magellanx_o_deficon - -option magellanx maxcmts Max number of comments to write (maxcmts=200) integer https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html#fmt_magellanx_o_maxcmts - -file rwrwrw magellan Magellan SD files (as for Meridian) magellan - https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html -option magellan deficon Default icon name string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_deficon - -option magellan maxcmts Max number of comments to write (maxcmts=200) integer https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_maxcmts - -serial rwrwrw magellan Magellan serial protocol magellan - https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html -option magellan deficon Default icon name string https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_deficon - -option magellan maxcmts Max number of comments to write (maxcmts=200) integer 200 https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_maxcmts - -option magellan baud Numeric value of bitrate (baud=4800) integer 4800 https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_baud - -option magellan noack Suppress use of handshaking in name of speed boolean https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_noack - -option magellan nukewpt Delete all waypoints boolean https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_nukewpt - serial r-r--- miniHomer MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI) miniHomer https://www.gpsbabel.org/WEB_DOC_DIR/fmt_miniHomer.html option miniHomer baud Baud rate used for download integer 115200 0 115200 https://www.gpsbabel.org/WEB_DOC_DIR/fmt_miniHomer.html#fmt_miniHomer_o_baud diff --git a/reference/help.txt b/reference/help.txt index f76efa9c0..c787547d2 100644 --- a/reference/help.txt +++ b/reference/help.txt @@ -252,18 +252,6 @@ File Types (-i and -o options): title (USR output) Output file title string serialnum (USR output) Device serial number description (USR output) Output file content description - magellanx Magellan SD files (as for eXplorist) - deficon Default icon name - maxcmts Max number of comments to write (maxcmts=200) - magellan Magellan SD files (as for Meridian) - deficon Default icon name - maxcmts Max number of comments to write (maxcmts=200) - magellan Magellan serial protocol - deficon Default icon name - maxcmts Max number of comments to write (maxcmts=200) - baud Numeric value of bitrate (baud=4800) - noack (0/1) Suppress use of handshaking in name of speed - nukewpt (0/1) Delete all waypoints miniHomer MiniHomer, a skyTraq Venus 6 based logger (downloa baud Baud rate used for download dump-file Dump raw data to this file diff --git a/reference/magfile b/reference/magfile deleted file mode 100644 index 26216d129..000000000 --- a/reference/magfile +++ /dev/null @@ -1,18 +0,0 @@ -$PMGNWPL,2805.200,N,08246.200,W,0000000,M,AL7394,872 6833 A TIDAL,a*43 -$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7485,872 6853 TIDAL 1,a*33 -$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7484,872 6853 TIDAL 2,a*31 -$PMGNWPL,2806.050,N,08246.367,W,0000000,M,AL7482,872 6853 TIDAL 4,a*34 -$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7471,872 6892 TIDAL 1,a*3A -$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7470,872 6892 TIDAL 2,a*38 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7380,872 6899 TIDAL 1,a*35 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7379,872 6899 TIDAL 2,a*30 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7378,872 6899 TIDAL 3,a*30 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7377,872 6899 TIDAL 4,a*38 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7376,872 6899 TIDAL 5,a*38 -$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7386,872 6904 TIDAL 1,a*32 -$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7385,872 6904 TIDAL 2,a*32 -$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7384,872 6904 TIDAL 3,a*32 -$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7383,872 6904 TIDAL 4,a*34 -$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7382,872 6904 TIDAL 5,a*34 -$PMGNWPL,2809.617,N,08245.933,W,0000000,M,AL6625,872 6905 TIDAL 5,a*3B -$PMGNWPL,2809.383,N,08244.666,W,0000000,M,AL7392,872 6906 TIDAL 2,a*31 diff --git a/reference/magxfile b/reference/magxfile deleted file mode 100644 index f9ea738d8..000000000 --- a/reference/magxfile +++ /dev/null @@ -1,19 +0,0 @@ -$PMGNWPL,2805.200,N,08246.200,W,0000000,M,AL7394,872 6833 A TIDAL,a*43 -$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7485,872 6853 TIDAL 1,a*33 -$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7484,872 6853 TIDAL 2,a*31 -$PMGNWPL,2806.050,N,08246.367,W,0000000,M,AL7482,872 6853 TIDAL 4,a*34 -$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7471,872 6892 TIDAL 1,a*3A -$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7470,872 6892 TIDAL 2,a*38 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7380,872 6899 TIDAL 1,a*35 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7379,872 6899 TIDAL 2,a*30 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7378,872 6899 TIDAL 3,a*30 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7377,872 6899 TIDAL 4,a*38 -$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7376,872 6899 TIDAL 5,a*38 -$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7386,872 6904 TIDAL 1,a*32 -$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7385,872 6904 TIDAL 2,a*32 -$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7384,872 6904 TIDAL 3,a*32 -$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7383,872 6904 TIDAL 4,a*34 -$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7382,872 6904 TIDAL 5,a*34 -$PMGNWPL,2809.617,N,08245.933,W,0000000,M,AL6625,872 6905 TIDAL 5,a*3B -$PMGNWPL,2809.383,N,08244.666,W,0000000,M,AL7392,872 6906 TIDAL 2,a*31 -$PMGNCMD,END*3D diff --git a/reference/track/meridian.trk b/reference/track/meridian.trk deleted file mode 100644 index abcb1a40a..000000000 --- a/reference/track/meridian.trk +++ /dev/null @@ -1,64 +0,0 @@ -$PMGNTRK,3003.731,N,09136.621,W,00001,M,170621.25,A,,250502*6A -$PMGNTRK,3003.767,N,09136.634,W,00000,M,170955.19,A,,250502*6F -$PMGNTRK,3003.762,N,09136.496,W,00000,M,171200.20,A,,250502*60 -$PMGNTRK,3003.740,N,09136.443,W,00000,M,171248.75,A,,250502*64 -$PMGNTRK,3003.692,N,09136.317,W,00000,M,171441.20,A,,250502*63 -$PMGNTRK,3003.587,N,09135.964,W,00000,M,171716.20,A,,250502*68 -$PMGNTRK,3003.468,N,09135.801,W,00000,M,171746.20,A,,250502*6F -$PMGNTRK,3003.323,N,09135.694,W,00000,M,171820.81,A,,250502*61 -$PMGNTRK,3003.233,N,09135.557,W,00000,M,171901.20,A,,250502*64 -$PMGNTRK,3002.984,N,09135.385,W,00000,M,172046.25,A,,250502*67 -$PMGNTRK,3002.941,N,09135.393,W,00000,M,172110.25,A,,250502*6B -$PMGNTRK,3002.928,N,09135.576,W,00000,M,172151.37,A,,250502*6F -$PMGNTRK,3002.774,N,09135.787,W,00000,M,172235.20,A,,250502*63 -$PMGNTRK,3002.731,N,09135.923,W,00000,M,172308.56,A,,250502*6C -$PMGNTRK,3002.838,N,09136.016,W,00000,M,180423.93,A,,250502*6C -$PMGNTRK,3002.820,N,09135.978,W,00002,M,180604.92,A,,250502*63 -$PMGNTRK,3002.786,N,09135.968,W,00000,M,180706.92,A,,250502*60 -$PMGNTRK,3002.772,N,09135.937,W,00001,M,180818.92,A,,250502*60 -$PMGNTRK,3002.782,N,09135.864,W,00000,M,181020.92,A,,250502*6B -$PMGNTRK,3002.781,N,09135.830,W,00000,M,181109.93,A,,250502*62 -$PMGNTRK,3002.807,N,09135.780,W,00000,M,181218.92,A,,250502*65 -$PMGNTRK,3002.847,N,09135.712,W,00000,M,181422.93,A,,250502*64 -$PMGNTRK,3002.868,N,09135.686,W,00002,M,181504.93,A,,250502*62 -$PMGNTRK,3002.895,N,09135.645,W,00001,M,181614.93,A,,250502*6E -$PMGNTRK,3002.921,N,09135.628,W,00001,M,181701.93,A,,250502*6E -$PMGNTRK,3002.961,N,09135.631,W,00000,M,181807.94,A,,250502*6D -$PMGNTRK,3003.019,N,09135.639,W,00002,M,181951.94,A,,250502*62 -$PMGNTRK,3003.047,N,09135.647,W,00000,M,182039.94,A,,250502*66 -$PMGNTRK,3003.074,N,09135.662,W,00000,M,182124.93,A,,250502*6B -$PMGNTRK,3003.108,N,09135.662,W,00000,M,182217.94,A,,250502*65 -$PMGNTRK,3003.133,N,09135.680,W,00000,M,182318.93,A,,250502*68 -$PMGNTRK,3003.181,N,09135.681,W,00000,M,182437.94,A,,250502*6D -$PMGNTRK,3003.292,N,09135.712,W,00006,M,182813.95,A,,250502*6A -$PMGNTRK,3003.224,N,09135.696,W,00002,M,183136.94,A,,250502*60 -$PMGNTRK,3003.191,N,09135.687,W,00000,M,183256.95,A,,250502*6B -$PMGNTRK,3003.158,N,09135.690,W,00000,M,183402.95,A,,250502*6F -$PMGNTRK,3003.147,N,09135.726,W,00000,M,183603.95,A,,250502*6E -$PMGNTRK,3003.149,N,09135.758,W,00000,M,183648.96,A,,250502*65 -$PMGNTRK,3003.159,N,09135.807,W,00001,M,183752.96,A,,250502*6A -$PMGNTRK,3003.188,N,09135.871,W,00000,M,183918.95,A,,250502*65 -$PMGNTRK,3003.217,N,09135.878,W,00000,M,184015.96,A,,250502*69 -$PMGNTRK,3003.238,N,09135.866,W,00006,M,184125.96,A,,250502*6F -$PMGNTRK,3003.217,N,09135.885,W,00000,M,184237.96,A,,250502*69 -$PMGNTRK,3003.192,N,09135.875,W,00000,M,184401.96,A,,250502*6B -$PMGNTRK,3003.169,N,09135.851,W,00000,M,184553.96,A,,250502*6F -$PMGNTRK,3003.154,N,09135.816,W,00000,M,184654.96,A,,250502*66 -$PMGNTRK,3003.140,N,09135.786,W,00000,M,184742.97,A,,250502*62 -$PMGNTRK,3003.135,N,09135.741,W,00000,M,184841.96,A,,250502*66 -$PMGNTRK,3003.133,N,09135.701,W,00000,M,184952.97,A,,250502*66 -$PMGNTRK,3003.113,N,09135.682,W,00000,M,185049.97,A,,250502*6C -$PMGNTRK,3003.063,N,09135.664,W,00000,M,185214.97,A,,250502*68 -$PMGNTRK,3003.034,N,09135.654,W,00000,M,185256.98,A,,250502*60 -$PMGNTRK,3003.011,N,09135.646,W,00000,M,185338.98,A,,250502*6D -$PMGNTRK,3002.946,N,09135.623,W,00000,M,185511.97,A,,250502*66 -$PMGNTRK,3002.907,N,09135.655,W,00000,M,185632.98,A,,250502*6F -$PMGNTRK,3002.885,N,09135.685,W,00000,M,185724.97,A,,250502*60 -$PMGNTRK,3002.850,N,09135.727,W,00007,M,185840.98,A,,250502*64 -$PMGNTRK,3002.824,N,09135.760,W,00000,M,185928.98,A,,250502*6C -$PMGNTRK,3002.798,N,09135.796,W,00000,M,190022.98,A,,250502*6A -$PMGNTRK,3002.784,N,09135.859,W,00000,M,190141.98,A,,250502*6F -$PMGNTRK,3002.774,N,09135.908,W,00000,M,190248.99,A,,250502*6E -$PMGNTRK,3002.779,N,09135.938,W,00000,M,190343.98,A,,250502*6B -$PMGNTRK,3002.807,N,09135.957,W,00000,M,190449.99,A,,250502*68 -$PMGNTRK,3002.828,N,09135.980,W,00000,M,190557.98,A,,250502*60 diff --git a/testo.d/classic-1.test b/testo.d/classic-1.test deleted file mode 100644 index 1eb1ea028..000000000 --- a/testo.d/classic-1.test +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - - -# Magellan serial -# TODO - - -# -# GPX routes -- since GPX contains a date stamp, tests will always -# fail, so we use magellan as an interim format... -# -rm -f ${TMPDIR}/gpxroute.gpx ${TMPDIR}/maggpx.rte -gpsbabel -r -i gpx -f ${REFERENCE}/route/route.gpx -o gpx \ - -F ${TMPDIR}/gpxroute.gpx -gpsbabel -r -i gpx -f ${TMPDIR}/gpxroute.gpx -o magellan \ - -F ${TMPDIR}/maggpx.rte -compare ${REFERENCE}/route/magellan.rte ${TMPDIR}/maggpx.rte - -# -# GPX tracks -- since GPX contains a date stamp, tests will always -# fail, so we use magellan as an interim format... -# -rm -f ${TMPDIR}/gpxtrack.gpx ${TMPDIR}/maggpx.trk -gpsbabel -t -i gpx -f ${REFERENCE}/track/tracks.gpx -o gpx \ - -F ${TMPDIR}/gpxtrack.gpx -gpsbabel -t -i magellan -f ${REFERENCE}/track/meridian.trk -o gpx \ - -F ${TMPDIR}/maggpx.trk -compare ${TMPDIR}/maggpx.trk ${TMPDIR}/gpxtrack.gpx - diff --git a/testo.d/classic-2.test b/testo.d/classic-2.test index 8bcaf9115..96f5fc9d7 100644 --- a/testo.d/classic-2.test +++ b/testo.d/classic-2.test @@ -12,7 +12,7 @@ compare ${REFERENCE}/humanread.out ${TMPDIR}/humanread.out # XCSV "path distance" test # rm -f ${TMPDIR}/pathdist.out -gpsbabel -i magellan -f ${REFERENCE}/dusky.trk -o xcsv,style=${REFERENCE}/gnuplot.style -F ${TMPDIR}/pathdist.out +gpsbabel -i gpx -f ${REFERENCE}/dusky.gpx -o xcsv,style=${REFERENCE}/gnuplot.style -F ${TMPDIR}/pathdist.out compare ${REFERENCE}/dusky.gnuplot ${TMPDIR}/pathdist.out # diff --git a/testo.d/magellan.test b/testo.d/magellan.test deleted file mode 100644 index f6f4e80c1..000000000 --- a/testo.d/magellan.test +++ /dev/null @@ -1,22 +0,0 @@ - - -# -# Magellan file format -# -gpsbabel -i magellan -f ${REFERENCE}/magfile -o magellan -F ${TMPDIR}/magfile -compare ${TMPDIR}/magfile ${REFERENCE}/magfile - -# -# Magellanx is just like, but with longer names. (which this admittedly -# doesn't actually exercise...) -# -gpsbabel -i magellan -f ${REFERENCE}/magxfile -o magellanx -F ${TMPDIR}/magxfile -compare ${TMPDIR}/magxfile ${REFERENCE}/magxfile - -# Magellanx routes, however, have an extra 'name' field in them. -gpsbabel -r -i magellanx -f ${REFERENCE}/route/magexplorist.rte -o magellanx -F ${TMPDIR}/magxfile.rte -gpsbabel -r -i magellanx -f ${TMPDIR}/magxfile.rte -o magellanx -F ${TMPDIR}/magxfile2.rte -compare ${REFERENCE}/route/magexplorist.rte ${TMPDIR}/magxfile2.rte - -gpsbabel -t -i gpx -f ${REFERENCE}/track/nmeadate.gpx -o magellanx -F ${TMPDIR}/magellandate.log -compare ${REFERENCE}/track/magellandate.log ${TMPDIR}/magellandate.log diff --git a/testo.d/magellan_sd.test b/testo.d/magellan_sd.test deleted file mode 100644 index db472d58e..000000000 --- a/testo.d/magellan_sd.test +++ /dev/null @@ -1,8 +0,0 @@ -# -# magellan SD card waypoint / route format -# -rm -f ${TMPDIR}/magellan.rte -gpsbabel -r -i magellan -f ${REFERENCE}/route/magellan.rte -o magellan \ - -F ${TMPDIR}/magellan.rte -compare ${REFERENCE}/route/magellan.rte ${TMPDIR}/magellan.rte - diff --git a/vecs.cc b/vecs.cc index 0df9ee641..45f0c1e90 100644 --- a/vecs.cc +++ b/vecs.cc @@ -69,9 +69,6 @@ extern ff_vecs_t geo_vecs; -extern ff_vecs_t mag_svecs; -extern ff_vecs_t mag_fvecs; -extern ff_vecs_t magX_fvecs; extern ff_vecs_t garmin_vecs; extern ff_vecs_t ozi_vecs; #if MAXIMAL_ENABLED @@ -120,9 +117,6 @@ struct Vecs::Impl { #endif // CSVFMTS_ENABLED LegacyFormat geo_fmt {geo_vecs}; GpxFormat gpx_fmt; - LegacyFormat mag_sfmt {mag_svecs}; - LegacyFormat mag_ffmt {mag_fvecs}; - LegacyFormat magX_ffmt {magX_fvecs}; LegacyFormat garmin_fmt {garmin_vecs}; GdbFormat gdb_fmt; NmeaFormat nmea_fmt; @@ -208,27 +202,6 @@ struct Vecs::Impl { "gpx", nullptr, }, - { - &mag_sfmt, - "magellan", - "Magellan serial protocol", - nullptr, - nullptr, - }, - { - &mag_ffmt, - "magellan", - "Magellan SD files (as for Meridian)", - nullptr, - nullptr, - }, - { - &magX_ffmt, - "magellanx", - "Magellan SD files (as for eXplorist)", - "upt", - nullptr, - }, { &garmin_fmt, "garmin", diff --git a/xmldoc/chapters/preface.xml b/xmldoc/chapters/preface.xml index 339b14e29..61e0d6454 100644 --- a/xmldoc/chapters/preface.xml +++ b/xmldoc/chapters/preface.xml @@ -12,7 +12,7 @@ standard in XML to contain all the data, but there are too many programs that don't understand it yet and too much data in alternate formats. Perhaps you have an - Explorist 600 + GPSMap 60CSx and your friend has a StreetPilot 2720 . diff --git a/xmldoc/chapters/use.xml b/xmldoc/chapters/use.xml index 4431ba8eb..1aa0a99ba 100644 --- a/xmldoc/chapters/use.xml +++ b/xmldoc/chapters/use.xml @@ -207,8 +207,6 @@ merged data to multiple destinations. transparently with waypoints, tracks, and routes. Some formats, like garmin - and - magellan require the -t flag to work with tracks and diff --git a/xmldoc/formats/magellan.xml b/xmldoc/formats/magellan.xml deleted file mode 100644 index 9a25f8e6a..000000000 --- a/xmldoc/formats/magellan.xml +++ /dev/null @@ -1,38 +0,0 @@ -GPSBabel supports the following Magellan receivers: - - 310 - 315 - Map330 - SporTrak Map Color - SporTrak Map - SporTrak Map Pro - SporTrak Map Topo - Meridian (green or yellow) - Meridian Gold - Meridian Platinum - Meridian Color - Explorist 100 (with aftermarket cable) - Explorist 200 (with aftermarket cable) - Explorist 300 (with aftermarket cable) - Explorist 210 - Explorist 300 - Explorist 400 - Explorist 500 - Explorist 600 - Explorist XL - - - - - This format is used for both the serial protocol used on the - devices with serial ports such as Map330 and Meridian and for - the files stored either in either the unit's internal memory - (Explorist 210, Explorist 400, Explorist 500, Explorist 600, - Explorist XL) or on removable memory. - - - If you specify a serial port for the file (.e.g. "COM1", "/dev/ttyS0") - to be read or written, GPSBabel will use serial protocol. Specifying - a file, either on local filesystem or on a mounted flash card reader, - will results in the file-based format being used. - diff --git a/xmldoc/formats/magellan1.xml b/xmldoc/formats/magellan1.xml deleted file mode 100644 index e3936649a..000000000 --- a/xmldoc/formats/magellan1.xml +++ /dev/null @@ -1,96 +0,0 @@ -GPSBabel supports the following Magellan receivers: - - 310 - 315 - Map330 - SporTrak Map Color - SporTrak Map - SporTrak Map Pro - SporTrak Map Topo - Meridian (green or yellow) - Meridian Gold - Meridian Platinum - Meridian Color - Explorist 100 (with aftermarket cable) - Explorist 200 (with aftermarket cable) - Explorist 300 (with aftermarket cable) - Explorist 210 - Explorist 300 - Explorist 400 - Explorist 500 - Explorist 600 - Explorist XL - - - -The RoadMate family of products is not supported. - - - - This format is used for both the serial protocol used on the - devices with serial ports such as Map330 and Meridian and for - the files stored either in either the unit's internal memory - (Explorist 210, 400, 500, 600, XL) or on removable memory. - - - If you specify a serial port for the file (.e.g. "COM1", "/dev/ttyS0") - to be read or written, GPSBabel will use serial protocol. Specifying - a file, either on local filesystem or on a mounted flash card reader, - will result in the file-based format being used. - - - Users of the Explorist generation of receivers should probably - prefer to use the magellanx - format over this one. - - - - -This module does not support the units that do not follow Magellan's -documented communications protocols including: - -Maestro 3100 -Maestro 3140 -Maestro 3200 -Maestro 3210 -Maestro 3220 -Maestro 3225 -Maestro 3250 -Maestro 4000 -Maestro 4040 -Maestro 4050 -Maestro 4200 -Maestro 4210 -Maestro 4220 -Maestro 4250 -Maestro 5310 - - RoadMate 300 - RoadMate 360 - RoadMate 500 - RoadMate 700 - RoadMate 760 - RoadMate 800 - RoadMate 860T - RoadMate 1200 - RoadMate 1400 - RoadMate 1412 - RoadMate 1430 - RoadMate 2000 - RoadMate 2000 - RoadMate 2200T - RoadMate 3000T - RoadMate 3050T - RoadMate 6000T - RoadMate AAA - - Triton 200 - Triton 300 - Triton 400 - Triton 500 - Triton 1500 - Triton 2000 - - - - diff --git a/xmldoc/formats/magellanx.xml b/xmldoc/formats/magellanx.xml deleted file mode 100644 index ff0742048..000000000 --- a/xmldoc/formats/magellanx.xml +++ /dev/null @@ -1,13 +0,0 @@ - - This is the SD card format used by the Magellan Explorist 400, - Explorist 500, Explorist 600, and Explorist XL and internally on those devices plus the - Explorist 210. Stored waypoints are identical to the Magellan SD format - used by Meridian, but the newer models allow longer waypoint names. Routes are - subtly different. - - - You should name any file containing waypoints created with - this format with a ".upt" extension so the firmware can read it. - Similarly, routes should be named ".rte" and tracks should be - named ".log". - diff --git a/xmldoc/formats/options/magellan-deficon.xml b/xmldoc/formats/options/magellan-deficon.xml deleted file mode 100644 index 50c507efb..000000000 --- a/xmldoc/formats/options/magellan-deficon.xml +++ /dev/null @@ -1,3 +0,0 @@ - - The deficon option is used to control the icon output when writing to this format. It overrides any icon information that might be present in the source data. - diff --git a/xmldoc/formats/options/magellan-maxcmts.xml b/xmldoc/formats/options/magellan-maxcmts.xml deleted file mode 100644 index 70104bbbe..000000000 --- a/xmldoc/formats/options/magellan-maxcmts.xml +++ /dev/null @@ -1,13 +0,0 @@ - -The maxcmts option allows you to specify the number comments that will -be sent to the unit. - - -Magellan receivers allow a maximum of 200 waypoint comments. Unfortunately, -DirectRoute uses waypoint comments to provide next turn directions for -navigation pop-ups and that comes from that pool of 200 comments. It -is therefore sometimes convenient to limit the number of waypoint -comments written to the receiver. For example, a geocacher might want -to upload 400 waypoints, but only 190 with comments so that DirectRoute -could provide driving directions for the next ten turns. - diff --git a/xmldoc/formats/options/magellan1-baud.xml b/xmldoc/formats/options/magellan1-baud.xml deleted file mode 100644 index c6370d684..000000000 --- a/xmldoc/formats/options/magellan1-baud.xml +++ /dev/null @@ -1,8 +0,0 @@ - - This option causes GPSBabel to use the given baud rate for serial - communications. It must match the given baud rate on the receiver. The - default value matches the default on the receiver, 4800. - - - Valid options are 1200, 2400, 4800, 9600, 19200, 57600, and 115200. - diff --git a/xmldoc/formats/options/magellan1-deficon.xml b/xmldoc/formats/options/magellan1-deficon.xml deleted file mode 100644 index f1d272b05..000000000 --- a/xmldoc/formats/options/magellan1-deficon.xml +++ /dev/null @@ -1,15 +0,0 @@ - -This option specifies the icon or waypoint type to write for each waypoint on -output. - - -If this option is specified, its value will be used for all waypoints, not -just those that do not already have descriptions. That is, this option -overrides any icon description that might be in the input file. - - - -This option has no effect on input. - diff --git a/xmldoc/formats/options/magellan1-maxcmts.xml b/xmldoc/formats/options/magellan1-maxcmts.xml deleted file mode 100644 index 70104bbbe..000000000 --- a/xmldoc/formats/options/magellan1-maxcmts.xml +++ /dev/null @@ -1,13 +0,0 @@ - -The maxcmts option allows you to specify the number comments that will -be sent to the unit. - - -Magellan receivers allow a maximum of 200 waypoint comments. Unfortunately, -DirectRoute uses waypoint comments to provide next turn directions for -navigation pop-ups and that comes from that pool of 200 comments. It -is therefore sometimes convenient to limit the number of waypoint -comments written to the receiver. For example, a geocacher might want -to upload 400 waypoints, but only 190 with comments so that DirectRoute -could provide driving directions for the next ten turns. - diff --git a/xmldoc/formats/options/magellan1-noack.xml b/xmldoc/formats/options/magellan1-noack.xml deleted file mode 100644 index 8727166ec..000000000 --- a/xmldoc/formats/options/magellan1-noack.xml +++ /dev/null @@ -1,17 +0,0 @@ - -Magellan's protocol specification strongly encourages the use of software -acknowledgments on every packets. This is a simple "this is what I think -I heard. If you agree that I heard it correctly, let's go to the next packet" -handshake that is used to ensure the integrity of the data transfer. - - -Certain firmware versions have problems handling this which makes transfers -unnecessarily slow. Transfers on all units at high serial speeds are also -severely restricted by this process. - - -In controlled environments (good cables, low electrical noise, receiving -from the unit, not doing donuts with the unit set to "track up" at a 150 -mile scale with 500 waypoints on the screen) it is sometimes useful to -release that safety belt by using the "noack" suboption. - diff --git a/xmldoc/formats/options/magellan1-nukewpt.xml b/xmldoc/formats/options/magellan1-nukewpt.xml deleted file mode 100644 index 57de6cc86..000000000 --- a/xmldoc/formats/options/magellan1-nukewpt.xml +++ /dev/null @@ -1,10 +0,0 @@ - -This option erases all waypoints in the receiver before doing a transfer. - - -This is a convenient option to use in automated processes where you want -to ensure the units starts with a clean state before sending waypoints to -it. Using this option on transmit is a better idea than doing it on -receive since the latter would erase all the waypoints before asking the -unit to send all the waypoints. - diff --git a/xmldoc/formats/options/magellanx-deficon.xml b/xmldoc/formats/options/magellanx-deficon.xml deleted file mode 100644 index 50c507efb..000000000 --- a/xmldoc/formats/options/magellanx-deficon.xml +++ /dev/null @@ -1,3 +0,0 @@ - - The deficon option is used to control the icon output when writing to this format. It overrides any icon information that might be present in the source data. - diff --git a/xmldoc/formats/options/magellanx-maxcmts.xml b/xmldoc/formats/options/magellanx-maxcmts.xml deleted file mode 100644 index 70104bbbe..000000000 --- a/xmldoc/formats/options/magellanx-maxcmts.xml +++ /dev/null @@ -1,13 +0,0 @@ - -The maxcmts option allows you to specify the number comments that will -be sent to the unit. - - -Magellan receivers allow a maximum of 200 waypoint comments. Unfortunately, -DirectRoute uses waypoint comments to provide next turn directions for -navigation pop-ups and that comes from that pool of 200 comments. It -is therefore sometimes convenient to limit the number of waypoint -comments written to the receiver. For example, a geocacher might want -to upload 400 waypoints, but only 190 with comments so that DirectRoute -could provide driving directions for the next ten turns. -