2020-10-25 12:28:55 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
|
|
|
|
#include "divecomputer.h"
|
2024-05-25 06:16:57 +00:00
|
|
|
#include "errorhelper.h"
|
2020-10-25 12:28:55 +00:00
|
|
|
#include "event.h"
|
|
|
|
#include "extradata.h"
|
|
|
|
#include "pref.h"
|
|
|
|
#include "sample.h"
|
|
|
|
#include "subsurface-string.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2024-08-24 19:13:53 +00:00
|
|
|
#include <tuple>
|
2020-10-25 12:28:55 +00:00
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
divecomputer::divecomputer() = default;
|
|
|
|
divecomputer::~divecomputer() = default;
|
2024-05-27 15:09:48 +00:00
|
|
|
divecomputer::divecomputer(const divecomputer &) = default;
|
2024-05-18 19:04:58 +00:00
|
|
|
divecomputer::divecomputer(divecomputer &&) = default;
|
2024-05-19 10:38:38 +00:00
|
|
|
divecomputer &divecomputer::operator=(const divecomputer &) = default;
|
2024-05-18 19:04:58 +00:00
|
|
|
|
2020-10-25 12:28:55 +00:00
|
|
|
/*
|
|
|
|
* Good fake dive profiles are hard.
|
|
|
|
*
|
|
|
|
* "depthtime" is the integral of the dive depth over
|
|
|
|
* time ("area" of the dive profile). We want that
|
|
|
|
* area to match the average depth (avg_d*max_t).
|
|
|
|
*
|
|
|
|
* To do that, we generate a 6-point profile:
|
|
|
|
*
|
|
|
|
* (0, 0)
|
|
|
|
* (t1, max_d)
|
|
|
|
* (t2, max_d)
|
|
|
|
* (t3, d)
|
|
|
|
* (t4, d)
|
|
|
|
* (max_t, 0)
|
|
|
|
*
|
|
|
|
* with the same ascent/descent rates between the
|
|
|
|
* different depths.
|
|
|
|
*
|
|
|
|
* NOTE: avg_d, max_d and max_t are given constants.
|
|
|
|
* The rest we can/should play around with to get a
|
|
|
|
* good-looking profile.
|
|
|
|
*
|
|
|
|
* That six-point profile gives a total area of:
|
|
|
|
*
|
|
|
|
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3)
|
|
|
|
*
|
|
|
|
* And the "same ascent/descent rates" requirement
|
|
|
|
* gives us (time per depth must be same):
|
|
|
|
*
|
|
|
|
* t1 / max_d = (t3-t2) / (max_d-d)
|
|
|
|
* t1 / max_d = (max_t-t4) / d
|
|
|
|
*
|
|
|
|
* We also obviously require:
|
|
|
|
*
|
|
|
|
* 0 <= t1 <= t2 <= t3 <= t4 <= max_t
|
|
|
|
*
|
|
|
|
* Let us call 'd_frac = d / max_d', and we get:
|
|
|
|
*
|
|
|
|
* Total area must match average depth-time:
|
|
|
|
*
|
|
|
|
* (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) = avg_d*max_t
|
|
|
|
* max_d*(max_t-t1-(1-d_frac)*(t4-t3)) = avg_d*max_t
|
|
|
|
* max_t-t1-(1-d_frac)*(t4-t3) = avg_d*max_t/max_d
|
|
|
|
* t1+(1-d_frac)*(t4-t3) = max_t*(1-avg_d/max_d)
|
|
|
|
*
|
|
|
|
* and descent slope must match ascent slopes:
|
|
|
|
*
|
|
|
|
* t1 / max_d = (t3-t2) / (max_d*(1-d_frac))
|
|
|
|
* t1 = (t3-t2)/(1-d_frac)
|
|
|
|
*
|
|
|
|
* and
|
|
|
|
*
|
|
|
|
* t1 / max_d = (max_t-t4) / (max_d*d_frac)
|
|
|
|
* t1 = (max_t-t4)/d_frac
|
|
|
|
*
|
|
|
|
* In general, we have more free variables than we have constraints,
|
|
|
|
* but we can aim for certain basics, like a good ascent slope.
|
|
|
|
*/
|
2024-05-19 10:38:38 +00:00
|
|
|
static int fill_samples(std::vector<sample> &s, int max_d, int avg_d, int max_t, double slope, double d_frac)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
|
|
|
double t_frac = max_t * (1 - avg_d / (double)max_d);
|
|
|
|
int t1 = lrint(max_d / slope);
|
|
|
|
int t4 = lrint(max_t - t1 * d_frac);
|
|
|
|
int t3 = lrint(t4 - (t_frac - t1) / (1 - d_frac));
|
|
|
|
int t2 = lrint(t3 - t1 * (1 - d_frac));
|
|
|
|
|
|
|
|
if (t1 < 0 || t1 > t2 || t2 > t3 || t3 > t4 || t4 > max_t)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
s[1].time.seconds = t1;
|
|
|
|
s[1].depth.mm = max_d;
|
|
|
|
s[2].time.seconds = t2;
|
|
|
|
s[2].depth.mm = max_d;
|
|
|
|
s[3].time.seconds = t3;
|
|
|
|
s[3].depth.mm = lrint(max_d * d_frac);
|
|
|
|
s[4].time.seconds = t4;
|
|
|
|
s[4].depth.mm = lrint(max_d * d_frac);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we have no average depth; instead of making up a random average depth
|
|
|
|
* we should assume either a PADI rectangular profile (for short and/or
|
|
|
|
* shallow dives) or more reasonably a six point profile with a 3 minute
|
|
|
|
* safety stop at 5m */
|
2024-05-19 10:38:38 +00:00
|
|
|
static void fill_samples_no_avg(std::vector<sample> &s, int max_d, int max_t, double slope)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
|
|
|
// shallow or short dives are just trapecoids based on the given slope
|
|
|
|
if (max_d < 10000 || max_t < 600) {
|
|
|
|
s[1].time.seconds = lrint(max_d / slope);
|
|
|
|
s[1].depth.mm = max_d;
|
|
|
|
s[2].time.seconds = max_t - lrint(max_d / slope);
|
|
|
|
s[2].depth.mm = max_d;
|
|
|
|
} else {
|
|
|
|
s[1].time.seconds = lrint(max_d / slope);
|
|
|
|
s[1].depth.mm = max_d;
|
|
|
|
s[2].time.seconds = max_t - lrint(max_d / slope) - 180;
|
|
|
|
s[2].depth.mm = max_d;
|
|
|
|
s[3].time.seconds = max_t - lrint(5000 / slope) - 180;
|
2024-09-03 15:04:48 +00:00
|
|
|
s[3].depth = 5_m;
|
2020-10-25 12:28:55 +00:00
|
|
|
s[4].time.seconds = max_t - lrint(5000 / slope);
|
2024-09-03 15:04:48 +00:00
|
|
|
s[4].depth = 5_m;
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
void fake_dc(struct divecomputer *dc)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
|
|
|
/* The dive has no samples, so create a few fake ones */
|
|
|
|
int max_t = dc->duration.seconds;
|
|
|
|
int max_d = dc->maxdepth.mm;
|
|
|
|
int avg_d = dc->meandepth.mm;
|
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
if (!max_t || !max_d) {
|
|
|
|
dc->samples.clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<struct sample> &fake = dc->samples;
|
|
|
|
fake.resize(6);
|
|
|
|
|
2020-10-25 12:28:55 +00:00
|
|
|
fake[5].time.seconds = max_t;
|
2024-05-19 10:38:38 +00:00
|
|
|
for (int i = 0; i < 6; i++) {
|
2020-10-25 12:28:55 +00:00
|
|
|
fake[i].bearing.degrees = -1;
|
|
|
|
fake[i].ndl.seconds = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set last manually entered time to the total dive length */
|
|
|
|
dc->last_manual_time = dc->duration;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We want to fake the profile so that the average
|
|
|
|
* depth ends up correct. However, in the absence of
|
|
|
|
* a reasonable average, let's just make something
|
|
|
|
* up. Note that 'avg_d == max_d' is _not_ a reasonable
|
|
|
|
* average.
|
|
|
|
* We explicitly treat avg_d == 0 differently */
|
|
|
|
if (avg_d == 0) {
|
|
|
|
/* we try for a sane slope, but bow to the insanity of
|
|
|
|
* the user supplied data */
|
2024-05-01 16:21:06 +00:00
|
|
|
fill_samples_no_avg(fake, max_d, max_t, std::max(2.0 * max_d / max_t, (double)prefs.ascratelast6m));
|
2020-10-25 12:28:55 +00:00
|
|
|
if (fake[3].time.seconds == 0) { // just a 4 point profile
|
2024-05-19 10:38:38 +00:00
|
|
|
dc->samples.resize(4);
|
2020-10-25 12:28:55 +00:00
|
|
|
fake[3].time.seconds = max_t;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (avg_d < max_d / 10 || avg_d >= max_d) {
|
|
|
|
avg_d = (max_d + 10000) / 3;
|
|
|
|
if (avg_d > max_d)
|
|
|
|
avg_d = max_d * 2 / 3;
|
|
|
|
}
|
|
|
|
if (!avg_d)
|
|
|
|
avg_d = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, first we try a basic profile with a specific ascent
|
|
|
|
* rate (5 meters per minute) and d_frac (1/3).
|
|
|
|
*/
|
|
|
|
if (fill_samples(fake, max_d, avg_d, max_t, (double)prefs.ascratelast6m, 0.33))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, assume that didn't work because we cannot make the
|
|
|
|
* average come out right because it was a quick deep dive
|
|
|
|
* followed by a much shallower region
|
|
|
|
*/
|
|
|
|
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0 / 60, 0.10))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Uhhuh. That didn't work. We'd need to find a good combination that
|
|
|
|
* satisfies our constraints. Currently, we don't, we just give insane
|
|
|
|
* slopes.
|
|
|
|
*/
|
|
|
|
if (fill_samples(fake, max_d, avg_d, max_t, 10000.0, 0.01))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Even that didn't work? Give up, there's something wrong */
|
|
|
|
}
|
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
divemode_loop::divemode_loop(const struct divecomputer &dc) :
|
2024-09-03 07:48:49 +00:00
|
|
|
last(dc.divemode), loop("modechange", dc)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-05-25 06:16:57 +00:00
|
|
|
/* on first invocation, get first event (if any) */
|
2024-09-03 07:48:49 +00:00
|
|
|
ev = loop.next();
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
2024-09-03 07:48:49 +00:00
|
|
|
divemode_t divemode_loop::at(int time)
|
2024-05-25 06:16:57 +00:00
|
|
|
{
|
|
|
|
while (ev && ev->time.seconds <= time) {
|
|
|
|
last = static_cast<divemode_t>(ev->value);
|
2024-09-03 07:48:49 +00:00
|
|
|
ev = loop.next();
|
2024-05-25 06:16:57 +00:00
|
|
|
}
|
|
|
|
return last;
|
|
|
|
}
|
2020-10-25 12:28:55 +00:00
|
|
|
|
|
|
|
/* helper function to make it easier to work with our structures
|
|
|
|
* we don't interpolate here, just use the value from the last sample up to that time */
|
2024-05-04 16:45:55 +00:00
|
|
|
int get_depth_at_time(const struct divecomputer *dc, unsigned int time)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
|
|
|
int depth = 0;
|
2024-05-19 10:38:38 +00:00
|
|
|
if (dc) {
|
|
|
|
for (const auto &sample: dc->samples) {
|
|
|
|
if (sample.time.seconds > (int)time)
|
2020-10-25 12:28:55 +00:00
|
|
|
break;
|
2024-05-19 10:38:38 +00:00
|
|
|
depth = sample.depth.mm;
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
2024-05-19 10:38:38 +00:00
|
|
|
}
|
2020-10-25 12:28:55 +00:00
|
|
|
return depth;
|
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
struct sample *prepare_sample(struct divecomputer *dc)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
|
|
|
if (dc) {
|
2024-05-19 10:38:38 +00:00
|
|
|
dc->samples.emplace_back();
|
|
|
|
auto &sample = dc->samples.back();
|
2020-10-25 12:28:55 +00:00
|
|
|
|
|
|
|
// Copy the sensor numbers - but not the pressure values
|
|
|
|
// from the previous sample if any.
|
2024-05-19 10:38:38 +00:00
|
|
|
if (dc->samples.size() >= 2) {
|
|
|
|
auto &prev = dc->samples[dc->samples.size() - 2];
|
2020-10-25 12:28:55 +00:00
|
|
|
for (int idx = 0; idx < MAX_SENSORS; idx++)
|
2024-05-19 10:38:38 +00:00
|
|
|
sample.sensor[idx] = prev.sensor[idx];
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
// Init some values with -1
|
2024-05-19 10:38:38 +00:00
|
|
|
sample.bearing.degrees = -1;
|
|
|
|
sample.ndl.seconds = -1;
|
2020-10-25 12:28:55 +00:00
|
|
|
|
2024-05-19 10:38:38 +00:00
|
|
|
return &sample;
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2024-05-25 06:50:20 +00:00
|
|
|
void append_sample(const struct sample &sample, struct divecomputer *dc)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-05-25 06:50:20 +00:00
|
|
|
dc->samples.push_back(sample);
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate how long we were actually under water, and the average
|
|
|
|
* depth while under water.
|
|
|
|
*
|
|
|
|
* This ignores any surface time in the middle of the dive.
|
|
|
|
*/
|
2024-05-27 15:09:48 +00:00
|
|
|
void fixup_dc_duration(struct divecomputer &dc)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-05-19 10:38:38 +00:00
|
|
|
int duration = 0;
|
|
|
|
int lasttime = 0, lastdepth = 0, depthtime = 0;
|
|
|
|
|
2024-05-27 15:09:48 +00:00
|
|
|
for (const auto &sample: dc.samples) {
|
2024-05-19 10:38:38 +00:00
|
|
|
int time = sample.time.seconds;
|
|
|
|
int depth = sample.depth.mm;
|
2020-10-25 12:28:55 +00:00
|
|
|
|
2024-08-26 04:48:19 +00:00
|
|
|
/* Do we *have* a depth? */
|
|
|
|
if (depth < 0)
|
|
|
|
continue;
|
|
|
|
|
2020-10-25 12:28:55 +00:00
|
|
|
/* We ignore segments at the surface */
|
|
|
|
if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) {
|
|
|
|
duration += time - lasttime;
|
|
|
|
depthtime += (time - lasttime) * (depth + lastdepth) / 2;
|
|
|
|
}
|
|
|
|
lastdepth = depth;
|
|
|
|
lasttime = time;
|
|
|
|
}
|
|
|
|
if (duration) {
|
2024-05-27 15:09:48 +00:00
|
|
|
dc.duration.seconds = duration;
|
|
|
|
dc.meandepth.mm = (depthtime + duration / 2) / duration;
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
static bool operator<(const event &ev1, const event &ev2)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-08-17 13:18:51 +00:00
|
|
|
return std::tie(ev1.time.seconds, ev1.name) <
|
|
|
|
std::tie(ev2.time.seconds, ev2.name);
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
int add_event_to_dc(struct divecomputer *dc, struct event ev)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-05-25 06:16:57 +00:00
|
|
|
// Do a binary search for insertion point
|
|
|
|
auto it = std::lower_bound(dc->events.begin(), dc->events.end(), ev);
|
|
|
|
int idx = it - dc->events.begin();
|
|
|
|
dc->events.insert(it, ev);
|
|
|
|
return idx;
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
2024-05-04 20:17:07 +00:00
|
|
|
struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const std::string &name)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-05-25 06:16:57 +00:00
|
|
|
struct event ev(time, type, flags, value, name);
|
|
|
|
int idx = add_event_to_dc(dc, std::move(ev));
|
2020-10-25 12:28:55 +00:00
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
return &dc->events[idx];
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
/* Remove given event from dive computer. Returns the removed event. */
|
|
|
|
struct event remove_event_from_dc(struct divecomputer *dc, int idx)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-05-25 06:16:57 +00:00
|
|
|
if (idx < 0 || static_cast<size_t>(idx) > dc->events.size()) {
|
|
|
|
report_info("removing invalid event %d", idx);
|
|
|
|
return event();
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
2024-05-25 06:16:57 +00:00
|
|
|
event res = std::move(dc->events[idx]);
|
|
|
|
dc->events.erase(dc->events.begin() + idx);
|
|
|
|
return res;
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
2024-05-25 06:16:57 +00:00
|
|
|
struct event *get_event(struct divecomputer *dc, int idx)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-05-25 06:16:57 +00:00
|
|
|
if (idx < 0 || static_cast<size_t>(idx) > dc->events.size()) {
|
|
|
|
report_info("accessing invalid event %d", idx);
|
|
|
|
return nullptr;
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
2024-05-25 06:16:57 +00:00
|
|
|
return &dc->events[idx];
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
2024-05-18 19:04:58 +00:00
|
|
|
void add_extra_data(struct divecomputer *dc, const std::string &key, const std::string &value)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
2024-05-18 19:04:58 +00:00
|
|
|
if (key == "Serial") {
|
|
|
|
dc->deviceid = calculate_string_hash(value.c_str());
|
2024-05-18 15:03:19 +00:00
|
|
|
dc->serial = value;
|
Update the serial number and deviceid in sync when loading
When we save the divecomputer data, we never actually save the serial
value as a field. We used to rely on saving the very dodgy 'deviceid',
and then look up the serial number from there. And that never really
worked reliably, but we didn't really notice, because we never really
_used_ the serial number anywhere.
The only place the serial number is actually reliably displayed is in
the "Extra data" tab, which contains the key value pairs, and that's
where the original dive download code got the serial number from.
So just parse that at load time too, the same way we parsed it at dive
download time.
In fact, do the firmware version the same way, and remove the code from
the downloader, since it too can rely on 'add_extra_data()' just picking
up the information directly.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-08-17 06:52:00 +00:00
|
|
|
}
|
2024-05-18 19:04:58 +00:00
|
|
|
if (key == "FW Version")
|
2024-05-18 15:03:19 +00:00
|
|
|
dc->fw_version = value;
|
Clean up divecomputer 'device' handling
We have this odd legacy notion of a divecomputer 'device', that was
originally just basically the libdivecomputer 'EVENT_DEVINFO' report
that was associated with each dive. So it had firmware version,
deviceid, and serial number.
It had also gotten extended to do 'nickname' handling, and it was all
confusing, ugly and bad. It was particularly bad because it wasn't
actually a 'per device' thing at all: due to the firmware field, a dive
computer that got a firmware update forced a new 'device'.
To make matters worse, the 'deviceid' was also almost random, because
we've calculated it a couple of different ways, and libdivecomputer
itself has changed how the legacy 32-bit 'serial number' is expressed.
Finally, because of all these issues, we didn't even try to make the
thing unique, so it really ended up being a random snapshot of the state
of the dive computer at the time of a dive, and sometimes we'd pick one,
and sometimes another, since they weren't really well-defined.
So get rid of all this confusion.
The new rules:
- the actual random dive computer state at the time of a dive is kept
in the dive data. So if you want to know the firmware version, it
should be in the 'extra data'
- the only serial number that matters is the string one in the extra
data, because that's the one that actually matches what the dive
computer reports, and isn't some random 32-bit integer with ambiguous
formatting.
- the 'device id' - the thing we match with (together with the model
name, eg "Suunto EON Steel") is purely a hash of the real serial
number.
The device ID that libdivecomputer reports in EVENT_DEVINFO is
ignored, as is the device ID we've saved in the XML or git files. If
we have a serial number, the device ID will be uniquely associated
with that serial number, and if we don't have one, the device ID will
be zero (for 'match anything').
So now 'deviceid' is literally just a shorthand for the serial number
string, and the two are joined at the hip.
- the 'device' managament is _only_ used to track devices that have
serial numbers _and_ nicknames. So no more different device
structures just because one had a nickname and the other didn't etc.
Without a serial number, the device is 'anonymous' and fundamentally
cannot be distinguished from other devices of the same model, so a
nickname is meaningless. And without a nickname, there is no point in
creating a device data structure, since all the data is in the dive
itself and the device structure wouldn't add any value..
These rules mean that we no longer have ambiguous 'device' structures,
and we can never have duplicates that can confuse us.
This does mean that you can't give a nickname to a device that cannot be
uniquely identified with a serial number, but those are happily fairly
rare (and mostly older ones). Dirk said he'd look at what it takes to
give more dive computers proper serial numbers, and I already did it for
the Garmin Descent family yesterday.
(Honesty in advertizing: right now you can't add a nickname to a dive
computer that doesn't already have one, because such a dive computer
will not have a device structure. But that's a UI issue, and I'll sort
that out separately)
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-08-16 22:50:11 +00:00
|
|
|
|
2024-05-18 19:04:58 +00:00
|
|
|
dc->extra_data.push_back(extra_data { key, value });
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Match two dive computer entries against each other, and
|
|
|
|
* tell if it's the same dive. Return 0 if "don't know",
|
|
|
|
* positive for "same dive" and negative for "definitely
|
|
|
|
* not the same dive"
|
|
|
|
*/
|
2024-05-27 15:09:48 +00:00
|
|
|
int match_one_dc(const struct divecomputer &a, const struct divecomputer &b)
|
2020-10-25 12:28:55 +00:00
|
|
|
{
|
|
|
|
/* Not same model? Don't know if matching.. */
|
2024-05-27 15:09:48 +00:00
|
|
|
if (a.model.empty() || b.model.empty())
|
2020-10-25 12:28:55 +00:00
|
|
|
return 0;
|
2024-05-27 15:09:48 +00:00
|
|
|
if (strcasecmp(a.model.c_str(), b.model.c_str()))
|
2020-10-25 12:28:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Different device ID's? Don't know */
|
2024-05-27 15:09:48 +00:00
|
|
|
if (a.deviceid != b.deviceid)
|
2020-10-25 12:28:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Do we have dive IDs? */
|
2024-05-27 15:09:48 +00:00
|
|
|
if (!a.diveid || !b.diveid)
|
2020-10-25 12:28:55 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If they have different dive ID's on the same
|
|
|
|
* dive computer, that's a definite "same or not"
|
|
|
|
*/
|
2024-05-27 15:09:48 +00:00
|
|
|
return a.diveid == b.diveid && a.when == b.when ? 1 : -1;
|
2020-10-25 12:28:55 +00:00
|
|
|
}
|
|
|
|
|
2024-05-23 04:06:23 +00:00
|
|
|
static const char *planner_dc_name = "planned dive";
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
bool is_dc_planner(const struct divecomputer *dc)
|
2024-05-23 04:06:23 +00:00
|
|
|
{
|
2024-05-18 15:03:19 +00:00
|
|
|
return dc->model == planner_dc_name;
|
2024-05-23 04:06:23 +00:00
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
void make_planner_dc(struct divecomputer *dc)
|
2024-05-23 04:06:23 +00:00
|
|
|
{
|
2024-05-18 15:03:19 +00:00
|
|
|
dc->model = planner_dc_name;
|
2024-05-23 04:06:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *manual_dc_name = "manually added dive";
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
bool is_dc_manually_added_dive(const struct divecomputer *dc)
|
2022-10-20 22:39:30 +00:00
|
|
|
{
|
2024-05-18 15:03:19 +00:00
|
|
|
return dc->model == manual_dc_name;
|
2022-10-21 16:12:11 +00:00
|
|
|
}
|
|
|
|
|
2024-05-04 16:45:55 +00:00
|
|
|
void make_manually_added_dive_dc(struct divecomputer *dc)
|
2022-10-21 16:12:11 +00:00
|
|
|
{
|
2024-05-18 15:03:19 +00:00
|
|
|
dc->model = manual_dc_name;
|
2022-10-20 22:39:30 +00:00
|
|
|
}
|