/*
Copyright (C) 2008 Björn Augustsson, oggust@gmail.com
Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org
Copyright (C) 2005-2013 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 "defs.h"
#include <QMap>
#include <cmath>
#include <cstdio>
#define MYNAME "humminbird"
#define WPT_NAME_LEN 12
#define RTE_NAME_LEN 20
#define TRK_NAME_LEN 20
#define MAX_RTE_POINTS 50
#define MAX_ITEMS_PER_GROUP 12
/*
I suspect that these are actually
struct signature {
uint8_t format, // 1 = track, 2 = waypoint, 3 = route, 4 = iTrack
uint8_t version,
gpuint16 record_length
}
The v3 TRK_MAGIC doesn't have a length, probably because it wouldn't fit.
(It would be 0x200008)
Still, they're useful in the code as a plain signature.
*/
#define TRK_MAGIC 0x01030000L
#define TRK_MAGIC2 0x01021F70L
#define WPT_MAGIC 0x02020024L
#define WPT_MAGIC2 0x02030024L // New for 2013. No visible diff?!
#define RTE_MAGIC 0x03030088L
#define EAST_SCALE 20038297.0 /* this is i1924_equ_axis*M_PI */
#define i1924_equ_axis 6378388.0
#define i1924_polar_axis 6356911.946
#define BAD_CHARS "\r\n\t"
/* The hwr data format is records-based, and the records are 36 bytes long. */
struct humminbird_waypt_t {
/* O.K.: the file can also contain routes with a different magic. */
/* uint32_t signature; */ /* Just for error checking(?) */
uint16_t num; /* Always ascending in the file. */
uint16_t zero; /* Always seems to be zero. */
uint8_t status; /* Always seems to be 1. Ends up as <h:status>
in gpx files exported by HumminbirdPC. */
uint8_t icon; /* See below */
uint16_t depth; /* Water depth. These are fishfinders. In centimeters */
uint32_t time; /* This is a time_t. In UTC */
int32_t east;
int32_t north;
char name[WPT_NAME_LEN];
};
struct humminbird_rte_t {
/* O.K.: the file can contain also routes with a different magic. */
/* uint32_t signature; */ /* Just for error checking(?) */
uint16_t num;
uint16_t zero;
uint8_t status;
uint8_t U0;
uint8_t U1;
int8_t count;
uint32_t time;
char name[RTE_NAME_LEN];
uint16_t points[MAX_RTE_POINTS];
};
struct humminbird_trk_header_t { /* 68 bytes, incl signature */
/* uint32_t signature; */
uint16_t trk_num;
uint16_t zero;
uint16_t num_points;
uint16_t unknown; /* Always zero so far. */
uint32_t time; /* a time_t, in UTC */
int32_t start_east; /* Start of track */
int32_t start_north;
int32_t end_east; /* end of track */
int32_t end_north;
int32_t sw_east; /* Bounding box, enclosing the track */
int32_t sw_north; /* sw is the south-west point */
int32_t ne_east; /* ne is the north-east point */
int32_t ne_north;
char name[20];
};
struct humminbird_trk_point_t {
int16_t deltaeast;
int16_t deltanorth;
uint16_t depth; /* in centimeters */
};
struct humminbird_trk_header_old_t { /* 16 bytes, incl signature */
/* uint32_t signature; */
uint16_t trk_num;
uint16_t zero;
uint16_t num_points;
uint16_t unknown; /* Always zero so far. */
uint32_t time; /* a time_t, in UTC */
int32_t start_east; /* Start of track */
int32_t start_north;
int32_t end_east; /* end of track */
int32_t end_north;
};
struct humminbird_trk_point_old_t {
int16_t deltaeast;
int16_t deltanorth;
};
struct group_header_t {
uint8_t status;
uint8_t icon;
uint16_t depth;
uint32_t time; /* a time_t, in UTC */
uint16_t parent_idx;
uint16_t reserved1;
uint16_t first_body_index;
uint16_t reserved2;
char name[WPT_NAME_LEN];
};
struct group_body_t {
uint8_t status;
uint8_t icon;
uint16_t next_idx;
uint16_t item[MAX_ITEMS_PER_GROUP];
};
static const char* humminbird_icons[] = {
"Normal", /* 0 */
"House", /* 1 */
"Red cross", /* 2 */
"Fish", /* 3 */
"Duck", /* 4 */
"Anchor", /* 5 */
"Buoy", /* 6 */
"Airport", /* 7 */
"Camping", /* 8 */
"Danger", /* 9 */
"Fuel", /* 10 */
"Rock", /* 11 */
"Weed", /* 12 */
"Wreck", /* 13 */
"Phone", /* 14 */
"Coffee", /* 15 */
"Beer", /* 16 */
"Mooring", /* 17 */
"Pier", /* 18 */
"Slip", /* 19 */
"Ramp", /* 20 */
"Circle", /* 21 */
"Diamond", /* 22 */
"Flag", /* 23 */
"Pattern", /* 24 */
"Shower", /* 25 */
"Water tap", /* 26 */
"Tree", /* 27 */
"Recording", /* 28 */
"Snapshot" /* 29 */
};
static gbfile* fin_;
static gbfile* fout_;
static int waypoint_num;
static short_handle wptname_sh, rtename_sh, trkname_sh;
static humminbird_rte_t* humrte;
static int rte_num_;
static QMap<QString, Waypoint*> map;
static
QVector<arglist_t> humminbird_args = {
};
/* Takes a latitude in degrees,
* returns a latitude in degrees. */
static double
geodetic_to_geocentric_hwr(const double gd_lat)
{
const double cos_ae = 0.9966349016452;
const double cos2_ae = cos_ae * cos_ae;
const double gdr = gd_lat *M_PI / 180.0;
return atan(cos2_ae * tan(gdr)) * 180.0/M_PI;
}
/* Takes a latitude in degrees,
* returns a latitude in degrees. */
static double
geocentric_to_geodetic_hwr(const double gc_lat)
{
const double cos_ae = 0.9966349016452;
const double cos2_ae = cos_ae * cos_ae;
const double gcr = gc_lat *M_PI / 180.0;
return atan(tan(gcr)/cos2_ae) * 180.0/M_PI;
}
/* Takes a projected "north" value, returns latitude in degrees. */
static double
gudermannian_i1924(const double x)
{
const double norm_x = x/i1924_equ_axis;
return atan(sinh(norm_x)) * 180.0/M_PI;
}
/* Takes latitude in degrees, returns projected "north" value. */
static double
inverse_gudermannian_i1924(const double x)
{
const double x_r = x/180.0 * M_PI;
const double guder = log(tan(M_PI/4.0 + x_r/2.0));
return guder * i1924_equ_axis;
}
/*******************************************************************************
* %%% global callbacks called by gpsbabel main process %%% *
*******************************************************************************/
static void
humminbird_rd_init(const QString& fname)
{
fin_ = gbfopen_be(fname, "rb", MYNAME);
}
static void
humminbird_rd_deinit()
{
gbfclose(fin_);
}
static void
humminbird_read_wpt(gbfile* fin)
{
humminbird_waypt_t w;
if (! gbfread(&w, 1, sizeof(w), fin)) {
fatal(MYNAME ": Unexpected end of file!\n");
}
/* Fix endianness - these are now BE */
w.num = be_read16(&w.num);
w.zero = be_read16(&w.zero);
w.depth = be_read16(&w.depth);
w.time = be_read32(&w.time);
w.north = be_read32(&w.north);
w.east = be_read32(&w.east);
/* All right! Copy the data to the gpsbabel struct... */
auto* wpt = new Waypoint;
// Could probably find a way to eliminate the alloc/copy.
char* s = xstrndup(w.name, sizeof(w.name));
wpt->shortname = s;
xfree(s);
wpt->SetCreationTime(w.time);
double guder = gudermannian_i1924(w.north);
wpt->latitude = geocentric_to_geodetic_hwr(guder);
wpt->longitude = (double)w.east / EAST_SCALE * 180.0;
wpt->altitude = 0.0; /* It's from a fishfinder... */
if (w.depth != 0) {
WAYPT_SET(wpt,depth,(double)w.depth / 100.0);
}
int num_icons = sizeof(humminbird_icons) / sizeof(humminbird_icons[0]);
if (w.icon < num_icons) {
wpt->icon_descr = humminbird_icons[w.icon];
}
// In newer versions, this is an enum (though it looks like a bitfield)
// that describes a sub-status
switch (w.status) {
case 0: // Waypoint not used. So why do we have one?
delete wpt;
break;
case 1: // Waypoint permanent.
case 2: // Waypoint temporary.
case 3: // Waypoint man-overboard.
waypt_add(wpt);
break;
case 16: // Waypoint group header.
case 17: // Waypoint group body.
case 63: // Waypoint group invalid.
default:
delete wpt;
break;
}
/* register the point over his internal Humminbird "Number" */
QString buff = QString::number(w.num);
map[buff] = wpt;
}
static void
humminbird_read_route(gbfile* fin)
{
humminbird_rte_t hrte;
if (! gbfread(&hrte, 1, sizeof(hrte), fin)) {
fatal(MYNAME ": Unexpected end of file!\n");
}
hrte.time = be_read32(&hrte.time);
hrte.num = be_read16(&hrte.num);
if (hrte.count > 0) {
route_head* rte = nullptr;
for (int i = 0; i < hrte.count; i++) {
char buff[10];
hrte.points[i] = be_read16(&hrte.points[i]);
/* locate the point over his internal Humminbird "Number" */
snprintf(buff, sizeof(buff), "%d", hrte.points[i]);
if ((map.value(buff))) {
const Waypoint* wpt = map.value(buff);
if (rte == nullptr) {
rte = new route_head;
route_add_head(rte);
// TODO: find a way to eliminate the copy.
char* s = xstrndup(hrte.name, sizeof(hrte.name));
rte->rte_name = s;
xfree(s);
/* rte->rte_num = hrte.num + 1; only internal number */
}
route_add_wpt(rte, new Waypoint(*wpt));
}
}
}
}
static void
humminbird_read_track(gbfile* fin)
{
humminbird_trk_header_t th;
if (! gbfread(&th, 1, sizeof(th), fin)) {
fatal(MYNAME ": Unexpected end of file reading header!\n");
}
th.trk_num = be_read16(&th.trk_num);
th.num_points = be_read16(&th.num_points);
th.time = be_read32(&th.time);
th.start_east = be_read32(&th.start_east);
th.start_north = be_read32(&th.start_north);
th.end_east = be_read32(&th.end_east);
th.end_north = be_read32(&th.end_north);
th.sw_east = be_read32(&th.sw_east);
th.sw_north = be_read32(&th.sw_north);
th.ne_east = be_read32(&th.ne_east);
th.ne_north = be_read32(&th.ne_north);
int max_points = (131080 - sizeof(uint32_t) - sizeof(th)) / sizeof(humminbird_trk_point_t);
if (th.num_points == max_points + 1) {
th.num_points--;
}
if (th.num_points > max_points) {
fatal(MYNAME ": Too many track points! (%d)\n", th.num_points);
}
/* num_points is actually one too big, because it includes the value in
the header. But we want the extra point at the end because the
freak-value filter below looks at points[i+1] */
auto* points = (humminbird_trk_point_t*) xcalloc(th.num_points, sizeof(humminbird_trk_point_t));
if (! gbfread(points, sizeof(humminbird_trk_point_t), th.num_points-1, fin)) {
fatal(MYNAME ": Unexpected end of file reading points!\n");
}
int32_t accum_east = th.start_east;
int32_t accum_north = th.start_north;
auto* trk = new route_head;
track_add_head(trk);
// TODO: find a way to eliminate the copy.
char* s = xstrndup(th.name, sizeof(th.name));
trk->rte_name = s;
xfree(s);
trk->rte_num = th.trk_num;
/* We create one wpt for the info in the header */
auto* first_wpt = new Waypoint;
double g_lat = gudermannian_i1924(accum_north);
first_wpt->latitude = geocentric_to_geodetic_hwr(g_lat);
first_wpt->longitude = accum_east/EAST_SCALE * 180.0;
first_wpt->altitude = 0.0;
/* No depth info in the header. */
track_add_wpt(trk, first_wpt);
for (int i = 0 ; i<th.num_points-1 ; i++) {
auto* wpt = new Waypoint;
points[i].depth = be_read16(&points[i].depth);
points[i].deltaeast = be_read16(&points[i].deltaeast);
points[i].deltanorth = be_read16(&points[i].deltanorth);
/* Every once in a while the delta values are
32767 followed by -32768. Filter that. */
int16_t next_deltaeast = be_read16(&points[i+1].deltaeast);
if (points[ i ].deltaeast == 32767 &&
next_deltaeast == -32768) {
points[ i ].deltaeast = -1;
points[i+1].deltaeast = 0; /* BE 0 == LE 0 */
}
int16_t next_deltanorth = be_read16(&points[i+1].deltanorth);
if (points[ i ].deltanorth == 32767 &&
next_deltanorth == -32768) {
points[ i ].deltanorth = -1;
points[i+1].deltanorth = 0;
}
accum_east += points[i].deltaeast;
accum_north += points[i].deltanorth;
double guder = gudermannian_i1924(accum_north);
wpt->latitude = geocentric_to_geodetic_hwr(guder);
wpt->longitude = accum_east/EAST_SCALE * 180.0;
wpt->altitude = 0.0;
if (points[i].depth != 0) {
WAYPT_SET(wpt,depth,(double)points[i].depth / 100.0);
}
if (i == th.num_points-2 && th.time != 0) {
/* Last point. Add the date from the header. */
/* Unless it's zero. Sometimes happens, possibly if
the gps didn't have a lock when the track was
saved. */
wpt->SetCreationTime(th.time);
}
track_add_wpt(trk, wpt);
}
xfree(points);
}
static void
humminbird_read_track_old(gbfile* fin)
{
humminbird_trk_header_old_t th;
const int file_len = 8048;
char namebuf[TRK_NAME_LEN];
if (! gbfread(&th, 1, sizeof(th), fin)) {
fatal(MYNAME ": Unexpected end of file reading header!\n");
}
th.trk_num = be_read16(&th.trk_num);
th.num_points = be_read16(&th.num_points);
th.time = be_read32(&th.time);
th.start_east = be_read32(&th.start_east);
th.start_north = be_read32(&th.start_north);
th.end_east = be_read32(&th.end_east);
th.end_north = be_read32(&th.end_north);
// These files are always 8048 bytes long. Note that that's the value
// of the second 16-bit word in the signature.
int max_points = (file_len - (sizeof(th) + sizeof(uint32_t) + TRK_NAME_LEN)) / sizeof(humminbird_trk_point_old_t);
if (th.num_points > max_points) {
fatal(MYNAME ": Too many track points! (%d)\n", th.num_points);
}
/* num_points is actually one too big, because it includes the value in
the header. But we want the extra point at the end because the
freak-value filter below looks at points[i+1] */
auto* points = (humminbird_trk_point_old_t*)xcalloc(th.num_points, sizeof(humminbird_trk_point_old_t));
if (! gbfread(points, sizeof(humminbird_trk_point_old_t), th.num_points-1, fin)) {
fatal(MYNAME ": Unexpected end of file reading points!\n");
}
int32_t accum_east = th.start_east;
int32_t accum_north = th.start_north;
auto* trk = new route_head;
track_add_head(trk);
/* The name is not in the header, but at the end of the file.
(The last 20 bytes.) */
gbfseek(fin, file_len-TRK_NAME_LEN, SEEK_SET);
gbfread(&namebuf, 1, TRK_NAME_LEN, fin);
trk->rte_name = xstrndup(namebuf, sizeof(namebuf));
trk->rte_num = th.trk_num;
/* We create one wpt for the info in the header */
auto* first_wpt = new Waypoint;
double g_lat = gudermannian_i1924(accum_north);
first_wpt->latitude = geocentric_to_geodetic_hwr(g_lat);
first_wpt->longitude = accum_east/EAST_SCALE * 180.0;
first_wpt->altitude = 0.0;
track_add_wpt(trk, first_wpt);
for (int i = 0 ; i<th.num_points-1 ; i++) {
auto* wpt = new Waypoint;
points[i].deltaeast = be_read16(&points[i].deltaeast);
points[i].deltanorth = be_read16(&points[i].deltanorth);
// I've commented this out, don't know if it happens in this
// format. It happens in the newer version though.
// /* Every once in a while the delta values are
// 32767 followed by -32768. Filter that. */
//
// next_deltaeast = be_read16(&points[i+1].deltaeast);
// if (points[ i ].deltaeast == 32767 &&
// next_deltaeast == -32768) {
// points[ i ].deltaeast = -1;
// points[i+1].deltaeast = 0; /* BE 0 == LE 0 */
// }
// next_deltanorth = be_read16(&points[i+1].deltanorth);
// if (points[ i ].deltanorth == 32767 &&
// next_deltanorth == -32768) {
// points[ i ].deltanorth = -1;
// points[i+1].deltanorth = 0;
// }
//
accum_east += points[i].deltaeast;
accum_north += points[i].deltanorth;
double guder = gudermannian_i1924(accum_north);
wpt->latitude = geocentric_to_geodetic_hwr(guder);
wpt->longitude = accum_east/EAST_SCALE * 180.0;
wpt->altitude = 0.0;
if (i == th.num_points-2 && th.time != 0) {
/* Last point. Add the date from the header. */
/* Unless it's zero. Sometimes happens, possibly if
the gps didn't have a lock when the track was
saved. */
wpt->SetCreationTime(th.time);
}
track_add_wpt(trk, wpt);
}
xfree(points);
}
static void
humminbird_read()
{
while (! gbfeof(fin_)) {
uint32_t signature = gbfgetuint32(fin_);
switch (signature) {
case WPT_MAGIC:
case WPT_MAGIC2:
humminbird_read_wpt(fin_);
break;
case RTE_MAGIC:
humminbird_read_route(fin_);
break;
case TRK_MAGIC:
humminbird_read_track(fin_);
return; /* Don't continue. The rest of the file is all zeores */
case TRK_MAGIC2:
humminbird_read_track_old(fin_);
return; /* Don't continue. The rest of the file is all zeores */
default:
fatal(MYNAME ": Invalid record header \"0x%08X\" (no or unknown humminbird file)!\n", signature);
}
}
}
/************************************************************************************************/
static void
humminbird_wr_init(const QString& fname)
{
fout_ = gbfopen_be(fname, "wb", MYNAME);
wptname_sh = mkshort_new_handle();
setshort_length(wptname_sh, WPT_NAME_LEN - 1);
setshort_badchars(wptname_sh, BAD_CHARS);
setshort_mustupper(wptname_sh, 0);
setshort_mustuniq(wptname_sh, 0);
setshort_whitespace_ok(wptname_sh, 1);
setshort_repeating_whitespace_ok(wptname_sh, 1);
setshort_defname(wptname_sh, "WPT");
rtename_sh = mkshort_new_handle();
setshort_length(rtename_sh, RTE_NAME_LEN - 1);
setshort_badchars(rtename_sh, BAD_CHARS);
setshort_mustupper(rtename_sh, 0);
setshort_mustuniq(rtename_sh, 0);
setshort_whitespace_ok(rtename_sh, 1);
setshort_repeating_whitespace_ok(rtename_sh, 1);
setshort_defname(rtename_sh, "Route");
trkname_sh = mkshort_new_handle();
setshort_length(trkname_sh, RTE_NAME_LEN - 1);
setshort_badchars(trkname_sh, BAD_CHARS);
setshort_mustupper(trkname_sh, 0);
setshort_mustuniq(trkname_sh, 0);
setshort_whitespace_ok(trkname_sh, 1);
setshort_repeating_whitespace_ok(trkname_sh, 1);
setshort_defname(trkname_sh, "Track");
waypoint_num = 0;
rte_num_ = 0;
}
static void
humminbird_wr_deinit()
{
mkshort_del_handle(&wptname_sh);
mkshort_del_handle(&rtename_sh);
mkshort_del_handle(&trkname_sh);
gbfclose(fout_);
}
static void
humminbird_write_waypoint(const Waypoint* wpt)
{
humminbird_waypt_t hum;
int num_icons = sizeof(humminbird_icons) / sizeof(humminbird_icons[0]);
be_write16(&hum.num, waypoint_num++);
hum.zero = 0;
hum.status = 1;
hum.icon = 255;
// Icon....
if (!wpt->icon_descr.isNull()) {
for (int i = 0; i < num_icons; i++) {
if (!wpt->icon_descr.compare(humminbird_icons[i], Qt::CaseInsensitive)) {
hum.icon = i;
break;
}
}
if (hum.icon == 255) { /* no success, no try to find the item in a more comlex name */
hum.icon = 0; /* i.e. "Diamond" as part of "Diamond, Green" or "Green Diamond" */
for (int i = 0; i < num_icons; i++) {
char* match;
xasprintf(&match, "*%s*", humminbird_icons[i]);
int j = wpt->icon_descr.compare(match, Qt::CaseInsensitive);
xfree(match);
if (j != 0) {
hum.icon = i;
break;
}
}
}
}
hum.depth = si_round(WAYPT_GET(wpt, depth, 0)*100.0);
be_write16(&hum.depth, hum.depth);
be_write32(&hum.time, wpt->GetCreationTime().toTime_t());
double east = wpt->longitude / 180.0 * EAST_SCALE;
be_write32(&hum.east, si_round((east)));
double lat = geodetic_to_geocentric_hwr(wpt->latitude);
double north = inverse_gudermannian_i1924(lat);
be_write32(&hum.north, si_round(north));
QString name = (global_opts.synthesize_shortnames)
? mkshort_from_wpt(wptname_sh, wpt)
: mkshort(wptname_sh, wpt->shortname);
memset(&hum.name, 0, sizeof(hum.name));
memcpy(&hum.name, CSTR(name), name.length());
gbfputuint32(WPT_MAGIC, fout_);
gbfwrite(&hum, sizeof(hum), 1, fout_);
}
static humminbird_trk_header_t* trk_head;
static humminbird_trk_point_t* trk_points;
static int32_t last_east;
static int32_t last_north;
static uint32_t last_time;
static void
humminbird_track_head(const route_head* trk)
{
int max_points = (131080 - sizeof(uint32_t)- sizeof(humminbird_trk_header_t)) / sizeof(humminbird_trk_point_t);
trk_head = nullptr;
last_time = 0;
if (trk->rte_waypt_ct() > 0) {
trk_head = (humminbird_trk_header_t*) xcalloc(1, sizeof(humminbird_trk_header_t));
trk_points = (humminbird_trk_point_t*) xcalloc(max_points, sizeof(humminbird_trk_point_t));
QString name = mkshort(trkname_sh, trk->rte_name);
strncpy(trk_head->name, CSTR(name), sizeof(trk_head->name)-1);
be_write16(&trk_head->trk_num, trk->rte_num);
}
}
static void
humminbird_track_tail(const route_head*)
{
int max_points = (131080 - sizeof(uint32_t)- sizeof(humminbird_trk_header_t)) / sizeof(humminbird_trk_point_t);
if (trk_head == nullptr) {
return;
}
be_write32(&trk_head->end_east, last_east);
be_write32(&trk_head->end_north, last_north);
be_write32(&trk_head->time, last_time);
/* Fix some endianness */
be_write32(&trk_head->sw_east, trk_head->sw_east);
be_write32(&trk_head->ne_east, trk_head->ne_east);
be_write32(&trk_head->sw_north, trk_head->sw_north);
be_write32(&trk_head->ne_north, trk_head->ne_north);
be_write16(&trk_head->num_points, trk_head->num_points);
/* Actually write it out */
gbfputuint32(TRK_MAGIC, fout_);
gbfwrite(trk_head, 1, sizeof(humminbird_trk_header_t), fout_);
gbfwrite(trk_points, max_points, sizeof(humminbird_trk_point_t), fout_);
gbfputuint16(0, fout_); /* Odd but true. The format doesn't fit an int nr of entries. */
xfree(trk_head);
xfree(trk_points);
trk_head = nullptr;
trk_points = nullptr;
}
static void
humminbird_track_cb(const Waypoint* wpt)
{
if (trk_head == nullptr) {
return;
}
int i = trk_head->num_points;
int32_t east = si_round(wpt->longitude / 180.0 * EAST_SCALE);
double lat = geodetic_to_geocentric_hwr(wpt->latitude);
int32_t north = si_round(inverse_gudermannian_i1924(lat));
if (wpt->creation_time.isValid()) {
last_time = wpt->GetCreationTime().toTime_t();
}
if (i == 0) {
/* It's the first point. That info goes in the header. */
be_write32(&trk_head->start_east, east);
be_write32(&trk_head->start_north, north);
/* Bounding box. Easy for one point. */
/* These are not BE yet, fixed in the end. */
trk_head->sw_east = east;
trk_head->ne_east = east;
trk_head->sw_north = north;
trk_head->ne_north = north;
/* No depth info in the header. */
} else {
/* These points are 16-bit differential. */
int j = i-1;
trk_points[j].deltaeast = east - last_east;
trk_points[j].deltanorth = north - last_north;
trk_points[j].depth = si_round(WAYPT_GET(wpt, depth, 0)*100.0);
/* BE-ify */
be_write16(&trk_points[j].deltaeast, trk_points[j].deltaeast);
be_write16(&trk_points[j].deltanorth, trk_points[j].deltanorth);
be_write16(&trk_points[j].depth, trk_points[j].depth);
/* Update bounding box in header if necessary */
if (east > trk_head->ne_east) {
trk_head->ne_east = east;
}
if (east < trk_head->sw_east) {
trk_head->sw_east = east;
}
if (north > trk_head->ne_north) {
trk_head->ne_north = north;
}
if (north < trk_head->sw_north) {
trk_head->sw_north = north;
}
}
last_east = east;
last_north = north;
trk_head->num_points++;
}
static void
humminbird_track_write()
{
track_disp_all(humminbird_track_head, humminbird_track_tail, humminbird_track_cb);
}
static void
humminbird_rte_head(const route_head* rte)
{
humrte = nullptr;
if (rte->rte_waypt_ct() > 0) {
humrte = (humminbird_rte_t*) xcalloc(1, sizeof(*humrte));
}
}
static void
humminbird_rte_tail(const route_head* rte)
{
if (humrte == nullptr) {
return;
}
if (humrte->count > 0) {
humrte->num = rte_num_++;
humrte->time = gpsbabel_time;
for (int i = 0; i < humrte->count; i++) {
be_write16(&humrte->points[i], humrte->points[i]);
}
be_write16(&humrte->num, humrte->num);
be_write32(&humrte->time, humrte->time);
QString name = mkshort(rtename_sh, rte->rte_name);
strncpy(humrte->name, CSTR(name), sizeof(humrte->name)-1);
gbfputuint32(RTE_MAGIC, fout_);
gbfwrite(humrte, sizeof(*humrte), 1, fout_);
}
xfree(humrte);
humrte = nullptr;
}
static void
humminbird_write_rtept(const Waypoint* wpt)
{
if (humrte == nullptr) {
return;
}
int i = gb_ptr2int(wpt->extra_data);
if (i <= 0) {
return;
}
if (humrte->count < MAX_RTE_POINTS) {
humrte->points[humrte->count] = i - 1;
humrte->count++;
} else {
warning(MYNAME ": Sorry, routes are limited to %d points!\n", MAX_RTE_POINTS);
fatal(MYNAME ": You can use our simplify filter to reduce the number of route points.\n");
}
}
static void
humminbird_write_waypoint_wrapper(const Waypoint* wpt)
{
char* key;
Waypoint* tmpwpt;
xasprintf(&key, "%s\01%.9f\01%.9f", CSTRc(wpt->shortname), wpt->latitude, wpt->longitude);
if (!(tmpwpt = map[key])) {
tmpwpt = const_cast<Waypoint*>(wpt);
map[key] = const_cast<Waypoint*>(wpt);
tmpwpt->extra_data = gb_int2ptr(waypoint_num + 1); /* NOT NULL */
humminbird_write_waypoint(wpt);
} else {
void* p = tmpwpt->extra_data;
tmpwpt = const_cast<Waypoint*>(wpt);
tmpwpt->extra_data = p;
}
xfree(key);
}
static void
humminbird_write()
{
waypt_disp_all(humminbird_write_waypoint_wrapper);
route_disp_all(nullptr, nullptr, humminbird_write_waypoint_wrapper);
route_disp_all(humminbird_rte_head, humminbird_rte_tail, humminbird_write_rtept);
}
/**************************************************************************/
ff_vecs_t humminbird_vecs = {
ff_type_file,
{
(ff_cap)(ff_cap_read | ff_cap_write) /* waypoints */,
ff_cap_read /* tracks */,
(ff_cap)(ff_cap_read | ff_cap_write) /* routes */
},
humminbird_rd_init,
humminbird_wr_init,
humminbird_rd_deinit,
humminbird_wr_deinit,
humminbird_read,
humminbird_write,
nullptr, // humminbird_exit,
&humminbird_args,
CET_CHARSET_ASCII, 1 /* ascii is the expected character set */
/* currently fixed !!! */
, NULL_POS_OPS,
nullptr
};
/**************************************************************************/
/**************************************************************************/
ff_vecs_t humminbird_ht_vecs = {
ff_type_file,
{
ff_cap_read /* waypoints */,
(ff_cap)(ff_cap_read | ff_cap_write) /* tracks */,
ff_cap_read /* routes */
},
humminbird_rd_init,
humminbird_wr_init,
humminbird_rd_deinit,
humminbird_wr_deinit,
humminbird_read,
humminbird_track_write,
nullptr, // humminbird_exit,
&humminbird_args,
CET_CHARSET_ASCII, 1 /* ascii is the expected character set */
/* currently fixed !!! */
, NULL_POS_OPS,
nullptr
};
/**************************************************************************/