Skip to content

Commit

Permalink
mimic std::optional for flagged Waypoint fields. (GPSBabel#975)
Browse files Browse the repository at this point in the history
* mimic std::optional for flagged Waypoint fields.

* manual adjustments to waypt access usage.

* split optional Waypoint flags, tune packing.

* bring op_flags into Waypoint.

* add comments about future Waypoint optional fields.

* sort Waypoint class almost normally.

private members refered to with decltype need to be defined
before they are referred to.

* remove extra parens around *has_value().

* remove extra parens around *value_or().

* remove more extra parens.
  • Loading branch information
tsteven4 authored Jan 7, 2023
1 parent 9568674 commit cfe18de
Show file tree
Hide file tree
Showing 32 changed files with 365 additions and 309 deletions.
153 changes: 102 additions & 51 deletions defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,30 +259,12 @@ class wp_flags
wp_flags() :
shortname_is_synthetic(0),
fmt_use(0),
temperature(0),
proximity(0),
course(0),
speed(0),
geoidheight(0),
depth(0),
is_split(0),
new_trkseg(0) {}
unsigned int shortname_is_synthetic:1;
unsigned int fmt_use:2; /* lightweight "extra data" */
/* "flagged fields" */
unsigned int temperature:1; /* temperature field is set */
unsigned int proximity:1; /* proximity field is set */
unsigned int course:1; /* course field is set */
unsigned int speed:1; /* speed field is set */
unsigned int geoidheight:1; /* geoidheight field is set */
unsigned int depth:1; /* depth field is set */
/* !ToDo!
unsigned int altitude:1; /+ altitude field is set +/
... and others
*/
unsigned int is_split:1; /* the waypoint represents a split */
unsigned int new_trkseg:1; /* True if first in new trkseg. */

};

// These are dicey as they're collected on read. Subsequent filters may change
Expand Down Expand Up @@ -319,14 +301,6 @@ struct bounds {
double min_alt; /* -unknown_alt => invalid */
};

#define WAYPT_SET(wpt,member,val) do { (wpt)->member = (val); (wpt)->wpt_flags.member = 1; } while (0)
#define WAYPT_GET(wpt,member,def) (((wpt)->wpt_flags.member) ? ((wpt)->member) : (def))
#define WAYPT_UNSET(wpt,member) wpt->wpt_flags.member = 0
#define WAYPT_HAS(wpt,member) ((wpt)->wpt_flags.member)
#define WAYPT_EQUAL(wpta,wptb,member) (((wpta)->wpt_flags.member && (wptb)->wpt_flags.member && \
((wpta)->member == (wptb)->member)) || \
(!(wpta)->wpt_flags.member && !(wptb)->wpt_flags.member))

/*
* This is a waypoint, as stored in the GPSR. It tries to not
* cater to any specific model or protocol. Anything that needs to
Expand All @@ -336,13 +310,36 @@ struct bounds {
class Waypoint
{
private:
static Geocache empty_gc_data;

public:
/* Types */

class op_flags
{
public:
op_flags() :
temperature(false),
proximity(false),
course(false),
speed(false),
geoidheight(false),
depth(false) {}
bool temperature:1; /* temperature field is set */
bool proximity:1; /* proximity field is set */
bool course:1; /* course field is set */
bool speed:1; /* speed field is set */
bool geoidheight:1; /* geoidheight field is set */
bool depth:1; /* depth field is set */
/* !ToDo!
unsigned int altitude:1; /+ altitude field is set +/
... and hdop,pdop,vdop,fix,sat,heartrate,cadence,power,
odometer_distance
*/
};

/* Data Members */

static Geocache empty_gc_data;

double latitude; /* Degrees */
double longitude; /* Degrees */
double altitude; /* Meters. */
double geoidheight; /* Height (in meters) of geoid (mean sea level) above WGS84 earth ellipsoid. */

/*
Expand All @@ -358,6 +355,79 @@ class Waypoint
* The units are meters.
*/
double proximity;
float course; /* Optional: degrees true */
float speed; /* Optional: meters per second. */
float temperature; /* Degrees celsius */
op_flags opt_flags;

public:

/* Special Member Functions */

Waypoint();
~Waypoint();
Waypoint(const Waypoint& other);
Waypoint& operator=(const Waypoint& other);

/* Member Functions */

bool HasUrlLink() const;
const UrlLink& GetUrlLink() const;
void AddUrlLink(const UrlLink& l);
QString CreationTimeXML() const;
gpsbabel::DateTime GetCreationTime() const;
void SetCreationTime(const gpsbabel::DateTime& t);
void SetCreationTime(qint64 t, qint64 ms = 0);
Geocache* AllocGCData();
int EmptyGCData() const;

// mimic std::optional interface, but use our more space
// efficient wp_flags.
#define GEN_WAYPT_METHODS(field) \
bool field##_has_value() const \
{ \
return opt_flags.field; \
} \
decltype(field) field##_value() const \
{ \
if (!opt_flags.field) { \
throw std::bad_optional_access(); \
} \
return field; \
} \
bool field##s_equal(const Waypoint& other) const \
{ \
return (opt_flags.field && other.opt_flags.field && (field == other.field)) || \
(!opt_flags.field && !other.opt_flags.field); \
} \
decltype(field) field##_value_or(decltype(field) p) const \
{ \
return (opt_flags.field)? field : p; \
} \
void set_##field(decltype(field) p) \
{ \
field = p; \
opt_flags.field = 1; \
} \
void reset_##field() \
{ \
opt_flags.field = 0; \
}

GEN_WAYPT_METHODS(temperature)
GEN_WAYPT_METHODS(proximity)
GEN_WAYPT_METHODS(course)
GEN_WAYPT_METHODS(speed)
GEN_WAYPT_METHODS(geoidheight)
GEN_WAYPT_METHODS(depth)

#undef GEN_WAYPT_METHODS

/* Data Members */

double latitude; /* Degrees */
double longitude; /* Degrees */
double altitude; /* Meters. */

/* shortname is a waypoint name as stored in receiver. It should
* strive to be, well, short, and unique. Enforcing length and
Expand All @@ -382,11 +452,11 @@ class Waypoint

UrlList urls;

wp_flags wpt_flags;
QString icon_descr;

gpsbabel::DateTime creation_time;

wp_flags wpt_flags;
/*
* route priority is for use by the simplify filter. If we have
* some reason to believe that the route point is more important,
Expand All @@ -405,36 +475,17 @@ class Waypoint
float hdop;
float vdop;
float pdop;
float course; /* Optional: degrees true */
float speed; /* Optional: meters per second. */
fix_type fix; /* Optional: 3d, 2d, etc. */
int sat; /* Optional: number of sats used for fix */

unsigned char heartrate; /* Beats/min. likely to get moved to fs. */
unsigned char cadence; /* revolutions per minute */
float power; /* watts, as measured by cyclists */
float temperature; /* Degrees celsius */
float odometer_distance; /* Meters */
Geocache* gc_data;
FormatSpecificDataList fs;
const session_t* session; /* pointer to a session struct */
void* extra_data; /* Extra data added by, say, a filter. */

public:
Waypoint();
~Waypoint();
Waypoint(const Waypoint& other);
Waypoint& operator=(const Waypoint& other);

bool HasUrlLink() const;
const UrlLink& GetUrlLink() const;
void AddUrlLink(const UrlLink& l);
QString CreationTimeXML() const;
gpsbabel::DateTime GetCreationTime() const;
void SetCreationTime(const gpsbabel::DateTime& t);
void SetCreationTime(qint64 t, qint64 ms = 0);
Geocache* AllocGCData();
int EmptyGCData() const;
};

using waypt_cb = void (*)(const Waypoint*);
Expand Down
2 changes: 1 addition & 1 deletion dg-100.cc
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ Dg100Format::process_gpsfile(uint8_t data[], route_head** track) const
* with a scaling factor of 100, in km/h.
* The waypoint struct wants the speed as a
* floating-point number, in m/s. */
WAYPT_SET(wpt, speed, KPH_TO_MPS(be_read32(data + i + 16) / 100.0));
wpt->set_speed(KPH_TO_MPS(be_read32(data + i + 16) / 100.0));
}

if (style >= 2) {
Expand Down
21 changes: 10 additions & 11 deletions exif.cc
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,7 @@ ExifFormat::exif_waypt_from_exif_app(ExifApp* app) const
speed_ref = tag->data.at(0).toByteArray().at(0);
break;
case GPS_IFD_TAG_SPEED:
WAYPT_SET(wpt, speed, exif_read_double(tag, 0));
wpt->set_speed(exif_read_double(tag, 0));
break;
case GPS_IFD_TAG_DATUM:
datum = exif_read_str(tag);
Expand Down Expand Up @@ -854,25 +854,24 @@ ExifFormat::exif_waypt_from_exif_app(ExifApp* app) const
}
}

if WAYPT_HAS(wpt, speed) {
if (wpt->speed_has_value()) {
switch (speed_ref) {
case 'K':
wpt->speed = KPH_TO_MPS(wpt->speed);
wpt->set_speed(KPH_TO_MPS(wpt->speed_value()));
break;
case 'M':
wpt->speed = MPH_TO_MPS(wpt->speed);
wpt->set_speed(MPH_TO_MPS(wpt->speed_value()));
break;
case 'N':
wpt->speed = KNOTS_TO_MPS(wpt->speed);
wpt->set_speed(KNOTS_TO_MPS(wpt->speed_value()));
break;
default:
wpt->speed = 0;
WAYPT_UNSET(wpt, speed);
wpt->reset_speed();
warning(MYNAME ": Unknown GPSSpeedRef unit %c (0x%02x)!\n", speed_ref, speed_ref);
}
if (global_opts.debug_level >= 3) {
if WAYPT_HAS(wpt, speed) {
printf(MYNAME "-GPSSpeed = %12.2f m/s\n", wpt->speed);
if (wpt->speed_has_value()) {
printf(MYNAME "-GPSSpeed = %12.2f m/s\n", wpt->speed_value());
}
}
}
Expand Down Expand Up @@ -1582,9 +1581,9 @@ ExifFormat::write()
exif_remove_tag(GPS_IFD, GPS_IFD_TAG_DOP);
}

if WAYPT_HAS(wpt, speed) {
if (wpt->speed_has_value()) {
exif_put_str(GPS_IFD, GPS_IFD_TAG_SPEEDREF, "K");
exif_put_double(GPS_IFD, GPS_IFD_TAG_SPEED, 0, MPS_TO_KPH(wpt->speed));
exif_put_double(GPS_IFD, GPS_IFD_TAG_SPEED, 0, MPS_TO_KPH(wpt->speed_value()));
} else {
exif_remove_tag(GPS_IFD, GPS_IFD_TAG_SPEEDREF);
exif_remove_tag(GPS_IFD, GPS_IFD_TAG_SPEED);
Expand Down
26 changes: 16 additions & 10 deletions garmin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -560,10 +560,10 @@ track_read()
next_is_new_trkseg = 0;

if (array[i]->dpth < 1.0e25f) {
WAYPT_SET(wpt, depth, array[i]->dpth);
wpt->set_depth(array[i]->dpth);
}
if (array[i]->temperature_populated) {
WAYPT_SET(wpt, temperature, array[i]->temperature);
wpt->set_temperature(array[i]->temperature);
}

track_add_wpt(trk_head, wpt);
Expand Down Expand Up @@ -641,10 +641,10 @@ pvt2wpt(GPS_PPvt_Data pvt, Waypoint* wpt)
wpt->latitude = pvt->lat;
wpt->longitude = pvt->lon;

WAYPT_SET(wpt, course, 180 + DEG(std::atan2(-pvt->east, -pvt->north)));
wpt->set_course(180 + DEG(std::atan2(-pvt->east, -pvt->north)));

/* velocity in m/s */
WAYPT_SET(wpt,speed, std::sqrt(pvt->north*pvt->north + pvt->east*pvt->east));
wpt->set_speed(std::sqrt(pvt->north*pvt->north + pvt->east*pvt->east));
// wpt->vs = pvt->up;

/*
Expand Down Expand Up @@ -1229,13 +1229,13 @@ garmin_fs_garmin_after_read(const GPS_PWay way, Waypoint* wpt, const int protoid
garmin_fs_t::set_category(gmsd, way->category);
}
if (way->dst < 1.0e25f) {
WAYPT_SET(wpt, proximity, way->dst);
wpt->set_proximity(way->dst);
}
if (way->temperature_populated) {
WAYPT_SET(wpt, temperature, way->temperature);
wpt->set_temperature(way->temperature);
}
if (way->dpth < 1.0e25f) {
WAYPT_SET(wpt, depth, way->dpth);
wpt->set_depth(way->dpth);
}
/* will copy until a null character or the end of the fixed length way field is reached, whichever comes first. */
garmin_fs_t::set_cc(gmsd, str_to_unicode(QByteArray(way->cc, qstrnlen(way->cc, sizeof(way->cc)))));
Expand Down Expand Up @@ -1263,9 +1263,15 @@ garmin_fs_garmin_before_write(const Waypoint* wpt, GPS_PWay way, const int proto
way->dspl = gt_switch_display_mode_value(
garmin_fs_t::get_display(gmsd, way->dspl), gps_waypt_type, 0);
way->category = garmin_fs_t::get_category(gmsd, way->category);
way->dpth = WAYPT_GET(wpt, depth, way->dpth);
way->dst = WAYPT_GET(wpt, proximity, way->dpth);
way->temperature = WAYPT_GET(wpt, temperature, way->temperature);
if (wpt->depth_has_value()) {
way->dpth = wpt->depth_value();
}
if (wpt->proximity_has_value()) {
way->dst = wpt->proximity_value();
}
if (wpt->temperature_has_value()) {
way->temperature = wpt->temperature_value();
}

/* destination may not be null terminated, but we will fill with nulls if necessary */
strncpy(way->cc, str_from_unicode(garmin_fs_t::get_cc(gmsd, nullptr)).constData(), sizeof(way->cc));
Expand Down
4 changes: 2 additions & 2 deletions garmin_fit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ GarminFitFormat::fit_parse_data(const fit_message_def& def, int time_offset)
}
waypt->SetCreationTime(GPS_Math_Gtime_To_Utime(timestamp));
if (speed != 0xffff) {
WAYPT_SET(waypt, speed, speed / 1000.0f);
waypt->set_speed(speed / 1000.0f);
}
if (heartrate != 0xff) {
waypt->heartrate = heartrate;
Expand All @@ -708,7 +708,7 @@ GarminFitFormat::fit_parse_data(const fit_message_def& def, int time_offset)
waypt->power = power;
}
if (temperature != 0x7f) {
WAYPT_SET(waypt, temperature, temperature);
waypt->set_temperature(temperature);
}
if (new_trkseg) {
waypt->wpt_flags.new_trkseg = 1;
Expand Down
2 changes: 1 addition & 1 deletion garmin_fit.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class GarminFitFormat : public Format
: lat(wpt.latitude),
lon(wpt.longitude),
altitude(wpt.altitude),
speed(WAYPT_GET(&wpt, speed, -1)),
speed(wpt.speed_value_or(-1)),
odometer_distance(wpt.odometer_distance),
creation_time(wpt.creation_time),
shortname(wpt.shortname),
Expand Down
Loading

0 comments on commit cfe18de

Please sign in to comment.