core: add CRTP base class to unit types

The goal here is to add general addition and scalar multiplication
functions to the unit types.

Thereto, we need a CRTP
(https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
base class.

However, this breaks compound initialization, so we have to use
named initializers:
	weight_t { 2000 } -> weight_t { .grams = 2000 }
The good thing is that this is exactly how these classes were
supposed to be used: make the unit explicit!

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2024-09-01 21:48:31 +02:00 committed by bstoeger
parent 696ba61eef
commit 12ca172a9e
26 changed files with 127 additions and 138 deletions

View file

@ -790,10 +790,6 @@ void PasteDives::redo()
// ***** ReplanDive ***** // ***** ReplanDive *****
ReplanDive::ReplanDive(dive *source) : d(current_dive), ReplanDive::ReplanDive(dive *source) : d(current_dive),
when(0), when(0),
maxdepth({0}),
meandepth({0}),
surface_pressure({0}),
duration({0}),
salinity(0) salinity(0)
{ {
if (!d) if (!d)
@ -870,11 +866,7 @@ QString editProfileTypeToString(EditProfileType type, int count)
} }
EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int count) : d(current_dive), EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int count) : d(current_dive),
dcNr(dcNr), dcNr(dcNr)
maxdepth({0}),
meandepth({0}),
dcmaxdepth({0}),
duration({0})
{ {
const struct divecomputer *sdc = source->get_dc(dcNr); const struct divecomputer *sdc = source->get_dc(dcNr);
if (!sdc) if (!sdc)

View file

@ -618,7 +618,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
*/ */
tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]); tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]);
if (tmp != 0x7fff) { if (tmp != 0x7fff) {
weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false }; weightsystem_t ws = { {.grams = tmp * 10}, translate("gettextFromC", "unknown"), false };
dt_dive->weightsystems.push_back(std::move(ws)); dt_dive->weightsystems.push_back(std::move(ws));
} }

View file

@ -2248,7 +2248,7 @@ duration_t dive::totaltime() const
time = dc_time; time = dc_time;
} }
} }
return { time }; return { .seconds = time };
} }
timestamp_t dive::endtime() const timestamp_t dive::endtime() const
@ -2367,7 +2367,7 @@ bool dive::cache_is_valid() const
pressure_t dive::get_surface_pressure() const pressure_t dive::get_surface_pressure() const
{ {
return surface_pressure.mbar > 0 ? surface_pressure return surface_pressure.mbar > 0 ? surface_pressure
: pressure_t { SURFACE_PRESSURE }; : pressure_t { .mbar = SURFACE_PRESSURE };
} }
/* This returns the conversion factor that you need to multiply /* This returns the conversion factor that you need to multiply
@ -2459,13 +2459,13 @@ int dive::mbar_to_depth(int mbar) const
depth_t dive::gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const depth_t dive::gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const
{ {
double depth = (double) mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix)); double depth = (double) mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix));
return depth_t { (int)lrint(depth / roundto) * roundto }; return depth_t { .mm = (int)lrint(depth / roundto) * roundto };
} }
/* Maximum narcotic depth rounded to multiples of roundto mm */ /* Maximum narcotic depth rounded to multiples of roundto mm */
depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const
{ {
pressure_t ppo2n2 { depth_to_mbar(end.mm) }; pressure_t ppo2n2 { .mbar = depth_to_mbar(end.mm) };
int maxambient = prefs.o2narcotic ? int maxambient = prefs.o2narcotic ?
(int)lrint(ppo2n2.mbar / (1 - get_he(mix) / 1000.0)) (int)lrint(ppo2n2.mbar / (1 - get_he(mix) / 1000.0))
@ -2475,7 +2475,7 @@ depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const
: :
// Actually: Infinity // Actually: Infinity
1000000; 1000000;
return depth_t { (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto }; return depth_t { .mm = (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto };
} }
std::string dive::get_country() const std::string dive::get_country() const
@ -2677,7 +2677,7 @@ temperature_t dive::dc_watertemp() const
} }
if (!nr) if (!nr)
return temperature_t(); return temperature_t();
return temperature_t{ static_cast<uint32_t>((sum + nr / 2) / nr) }; return temperature_t{ .mkelvin = static_cast<uint32_t>((sum + nr / 2) / nr) };
} }
/* /*
@ -2695,7 +2695,7 @@ temperature_t dive::dc_airtemp() const
} }
if (!nr) if (!nr)
return temperature_t(); return temperature_t();
return temperature_t{ static_cast<uint32_t>((sum + nr / 2) / nr) }; return temperature_t{ .mkelvin = static_cast<uint32_t>((sum + nr / 2) / nr) };
} }
/* /*
@ -2782,5 +2782,5 @@ weight_t dive::total_weight() const
// TODO: implement addition for units.h types // TODO: implement addition for units.h types
return std::accumulate(weightsystems.begin(), weightsystems.end(), weight_t(), return std::accumulate(weightsystems.begin(), weightsystems.end(), weight_t(),
[] (weight_t w, const weightsystem_t &ws) [] (weight_t w, const weightsystem_t &ws)
{ return weight_t{ w.grams + ws.weight.grams }; }); { return weight_t{ .grams = w.grams + ws.weight.grams }; });
} }

View file

@ -112,9 +112,9 @@ void set_tank_info_data(std::vector<tank_info> &table, const std::string &name,
std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info) std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info)
{ {
pressure_t working_pressure { pressure_t working_pressure {
static_cast<int32_t>(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi)) .mbar = static_cast<int32_t>(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi))
}; };
volume_t size { 0 }; volume_t size;
if (info.ml != 0) if (info.ml != 0)
size.mliter = info.ml; size.mliter = info.ml;
else if (working_pressure.mbar != 0) else if (working_pressure.mbar != 0)
@ -128,7 +128,7 @@ std::pair<volume_t, pressure_t> get_tank_info_data(const std::vector<tank_info>
auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info) auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info)
{ return info.name == name; }); { return info.name == name; });
return it != table.end() ? extract_tank_info(*it) return it != table.end() ? extract_tank_info(*it)
: std::make_pair(volume_t{0}, pressure_t{0}); : std::make_pair(volume_t(), pressure_t());
} }
void add_cylinder_description(const cylinder_type_t &type) void add_cylinder_description(const cylinder_type_t &type)
@ -182,7 +182,7 @@ volume_t cylinder_t::gas_volume(pressure_t p) const
{ {
double bar = p.mbar / 1000.0; double bar = p.mbar / 1000.0;
double z_factor = gas_compressibility_factor(gasmix, bar); double z_factor = gas_compressibility_factor(gasmix, bar);
return volume_t { static_cast<int>(lrint(type.size.mliter * bar_to_atm(bar) / z_factor)) }; return volume_t { .mliter = static_cast<int>(lrint(type.size.mliter * bar_to_atm(bar) / z_factor)) };
} }
int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders) int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders)

View file

@ -16,8 +16,8 @@ struct gasmix {
fraction_t he; fraction_t he;
std::string name() const; std::string name() const;
}; };
static const struct gasmix gasmix_invalid = { { -1 }, { -1 } }; static const struct gasmix gasmix_invalid = { { .permille = -1 }, { .permille = -1 } };
static const struct gasmix gasmix_air = { { 0 }, { 0 } }; static const struct gasmix gasmix_air = { { .permille = 0 }, { .permille = 0 } };
enum gastype { enum gastype {
GASTYPE_AIR, GASTYPE_AIR,

View file

@ -217,7 +217,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
// is not repeated ad-nauseum for broken images. // is not repeated ad-nauseum for broken images.
if (numPics == 0 && prefs.extract_video_thumbnails) { if (numPics == 0 && prefs.extract_video_thumbnails) {
QMetaObject::invokeMethod(VideoFrameExtractor::instance(), "extract", Qt::AutoConnection, QMetaObject::invokeMethod(VideoFrameExtractor::instance(), "extract", Qt::AutoConnection,
Q_ARG(QString, filename), Q_ARG(QString, filename), Q_ARG(duration_t, duration_t{(int32_t)duration})); Q_ARG(QString, filename), Q_ARG(QString, filename), Q_ARG(duration_t, duration_t{ .seconds = (int32_t)duration}));
} }
// Currently, we support only one picture // Currently, we support only one picture
@ -231,7 +231,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
res = videoImage; // No picture -> show dummy-icon res = videoImage; // No picture -> show dummy-icon
else else
markVideoThumbnail(res); // We got an image -> place our video marker on top of it markVideoThumbnail(res); // We got an image -> place our video marker on top of it
return { res, MEDIATYPE_VIDEO, { (int32_t)duration } }; return { res, MEDIATYPE_VIDEO, { .seconds = (int32_t)duration } };
} }
// Fetch a thumbnail from cache. // Fetch a thumbnail from cache.

View file

@ -309,7 +309,7 @@ static int divinglog_dive(void *param, int, char **data, char **)
state->cur_dive->watertemp.mkelvin = C_to_mkelvin(atol(data[9])); state->cur_dive->watertemp.mkelvin = C_to_mkelvin(atol(data[9]));
if (data[10]) { if (data[10]) {
weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false }; weightsystem_t ws = { { .grams = atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false };
state->cur_dive->weightsystems.push_back(std::move(ws)); state->cur_dive->weightsystems.push_back(std::move(ws));
} }

View file

@ -160,7 +160,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_
} }
} }
bool no_volume = true; bool no_volume = true;
struct gasmix bottom_gas = { {1000}, {0} }; /* Default to pure O2, or air if there are no mixes defined */ struct gasmix bottom_gas = { { .permille = 1000}, {} }; /* Default to pure O2, or air if there are no mixes defined */
if (ngases == 0) { if (ngases == 0) {
bottom_gas = gasmix_air; bottom_gas = gasmix_air;
} }

View file

@ -41,7 +41,7 @@ preferences::preferences() :
ascratestops(9000 / 60), ascratestops(9000 / 60),
ascrate50(9000 / 60), ascrate50(9000 / 60),
ascrate75(9000 / 60), ascrate75(9000 / 60),
bestmixend({ 30000 }), bestmixend({ .mm = 30'000 }),
bottompo2(1400), bottompo2(1400),
bottomsac(20000), bottomsac(20000),
decopo2(1600), decopo2(1600),

View file

@ -1504,8 +1504,8 @@ std::vector<std::string> compare_samples(const struct dive *d, const struct plot
const cylinder_t *cyl = d->get_cylinder(cylinder_index); const cylinder_t *cyl = d->get_cylinder(cylinder_index);
// TODO: Implement addition/subtraction on units.h types // TODO: Implement addition/subtraction on units.h types
volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ last_pressures[cylinder_index] }).mliter - volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ .mbar = last_pressures[cylinder_index] }).mliter -
cyl->gas_volume((pressure_t){ next_pressure }).mliter; cyl->gas_volume((pressure_t){ .mbar = next_pressure }).mliter;
} }
// check if the gas in this cylinder is being used // check if the gas in this cylinder is being used

View file

@ -786,33 +786,6 @@ int parseTemperatureToMkelvin(const QString &text)
return mkelvin; return mkelvin;
} }
int parseWeightToGrams(const QString &text)
{
int grams;
QString numOnly = text;
numOnly.replace(",", ".").remove(QRegularExpression("[^0-9.]"));
if (numOnly.isEmpty())
return 0;
double number = numOnly.toDouble();
if (text.contains(gettextFromC::tr("kg"), Qt::CaseInsensitive)) {
grams = lrint(number * 1000);
} else if (text.contains(gettextFromC::tr("lbs"), Qt::CaseInsensitive)) {
grams = lbs_to_grams(number);
} else {
switch (prefs.units.weight) {
case units::KG:
grams = lrint(number * 1000);
break;
case units::LBS:
grams = lbs_to_grams(number);
break;
default:
grams = 0;
}
}
return grams;
}
int parsePressureToMbar(const QString &text) int parsePressureToMbar(const QString &text)
{ {
int mbar; int mbar;

View file

@ -69,7 +69,6 @@ timestamp_t dateTimeToTimestamp(const QDateTime &t);
int parseDurationToSeconds(const QString &text); int parseDurationToSeconds(const QString &text);
int parseLengthToMm(const QString &text); int parseLengthToMm(const QString &text);
int parseTemperatureToMkelvin(const QString &text); int parseTemperatureToMkelvin(const QString &text);
int parseWeightToGrams(const QString &text);
int parsePressureToMbar(const QString &text); int parsePressureToMbar(const QString &text);
int parseGasMixO2(const QString &text); int parseGasMixO2(const QString &text);
int parseGasMixHE(const QString &text); int parseGasMixHE(const QString &text);

View file

@ -12,7 +12,7 @@ struct sample // BASE TYPE BYTES UNITS RANGE
{ // --------- ----- ----- ----- ----------- { // --------- ----- ----- ----- -----------
duration_t time; // int32_t 4 seconds (0-34 yrs) elapsed dive time up to this sample duration_t time; // int32_t 4 seconds (0-34 yrs) elapsed dive time up to this sample
duration_t stoptime; // int32_t 4 seconds (0-34 yrs) time duration of next deco stop duration_t stoptime; // int32_t 4 seconds (0-34 yrs) time duration of next deco stop
duration_t ndl = { -1 }; // int32_t 4 seconds (-1 no val, 0-34 yrs) time duration before no-deco limit duration_t ndl = { .seconds = -1 };// int32_t 4 seconds (-1 no val, 0-34 yrs) time duration before no-deco limit
duration_t tts; // int32_t 4 seconds (0-34 yrs) time duration to reach the surface duration_t tts; // int32_t 4 seconds (0-34 yrs) time duration to reach the surface
duration_t rbt; // int32_t 4 seconds (0-34 yrs) remaining bottom time duration_t rbt; // int32_t 4 seconds (0-34 yrs) remaining bottom time
depth_t depth; // int32_t 4 mm (0-2000 km) dive depth of this sample depth_t depth; // int32_t 4 mm (0-2000 km) dive depth of this sample
@ -21,7 +21,7 @@ struct sample // BASE TYPE BYTES UNITS RANGE
pressure_t pressure[MAX_SENSORS]; // int32_t 2x4 mbar (0-2 Mbar) cylinder pressures (main and CCR o2) pressure_t pressure[MAX_SENSORS]; // int32_t 2x4 mbar (0-2 Mbar) cylinder pressures (main and CCR o2)
o2pressure_t setpoint; // uint16_t 2 mbar (0-65 bar) O2 partial pressure (will be setpoint) o2pressure_t setpoint; // uint16_t 2 mbar (0-65 bar) O2 partial pressure (will be setpoint)
o2pressure_t o2sensor[MAX_O2_SENSORS];// uint16_t 6x2 mbar (0-65 bar) Up to 6 PO2 sensor values (rebreather) o2pressure_t o2sensor[MAX_O2_SENSORS];// uint16_t 6x2 mbar (0-65 bar) Up to 6 PO2 sensor values (rebreather)
bearing_t bearing = { -1 }; // int16_t 2 degrees (-1 no val, 0-360 deg) compass bearing bearing_t bearing = { .degrees = -1 };// int16_t 2 degrees (-1 no val, 0-360 deg) compass bearing
int16_t sensor[MAX_SENSORS] = {}; // int16_t 2x2 sensorID (0-16k) ID of cylinder pressure sensor int16_t sensor[MAX_SENSORS] = {}; // int16_t 2x2 sensorID (0-16k) ID of cylinder pressure sensor
uint16_t cns = 0; // uint16_t 2 % (0-64k %) cns% accumulated uint16_t cns = 0; // uint16_t 2 % (0-64k %) cns% accumulated
uint8_t heartbeat = 0; // uint8_t 1 beats/m (0-255) heart rate measurement uint8_t heartbeat = 0; // uint8_t 1 beats/m (0-255) heart rate measurement

View file

@ -275,9 +275,9 @@ static std::pair<volume_t, volume_t> get_gas_parts(struct gasmix mix, volume_t v
if (gasmix_is_air(mix)) if (gasmix_is_air(mix))
return { volume_t() , volume_t() }; return { volume_t() , volume_t() };
volume_t air = { (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) }; volume_t air { .mliter = (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) };
volume_t he = { (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) }; volume_t he { .mliter = (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) };
volume_t o2 = { vol.mliter - he.mliter - air.mliter }; volume_t o2 { .mliter = vol.mliter - he.mliter - air.mliter };
return std::make_pair(o2, he); return std::make_pair(o2, he);
} }

View file

@ -67,62 +67,66 @@
*/ */
using timestamp_t = int64_t; using timestamp_t = int64_t;
struct duration_t template <typename T>
struct unit_base {
};
struct duration_t : public unit_base<duration_t>
{ {
int32_t seconds = 0; // durations up to 34 yrs int32_t seconds = 0; // durations up to 34 yrs
}; };
struct offset_t struct offset_t : public unit_base<offset_t>
{ {
int32_t seconds = 0; // offsets up to +/- 34 yrs int32_t seconds = 0; // offsets up to +/- 34 yrs
}; };
struct depth_t // depth to 2000 km struct depth_t : public unit_base<depth_t> // depth to 2000 km
{ {
int32_t mm = 0; int32_t mm = 0;
}; };
struct pressure_t struct pressure_t : public unit_base<pressure_t>
{ {
int32_t mbar = 0; // pressure up to 2000 bar int32_t mbar = 0; // pressure up to 2000 bar
}; };
struct o2pressure_t struct o2pressure_t : public unit_base<o2pressure_t>
{ {
uint16_t mbar = 0; uint16_t mbar = 0;
}; };
struct bearing_t struct bearing_t : public unit_base<bearing_t>
{ {
int16_t degrees = 0; int16_t degrees = 0;
}; };
struct temperature_t struct temperature_t : public unit_base<temperature_t>
{ {
uint32_t mkelvin = 0; // up to 4 MK (temperatures in K are always positive) uint32_t mkelvin = 0; // up to 4 MK (temperatures in K are always positive)
}; };
struct temperature_sum_t struct temperature_sum_t : public unit_base<temperature_sum_t>
{ {
uint64_t mkelvin = 0; // up to 18446744073 MK (temperatures in K are always positive) uint64_t mkelvin = 0; // up to 18446744073 MK (temperatures in K are always positive)
}; };
struct volume_t struct volume_t : public unit_base<volume_t>
{ {
int mliter = 0; int mliter = 0;
}; };
struct fraction_t struct fraction_t : public unit_base<fraction_t>
{ {
int permille = 0; int permille = 0;
}; };
struct weight_t struct weight_t : public unit_base<weight_t>
{ {
int grams = 0; int grams = 0;
}; };
struct degrees_t struct degrees_t : public unit_base<degrees_t>
{ {
int udeg = 0; int udeg = 0;
}; };
@ -152,8 +156,8 @@ static inline bool operator!=(const location_t &a, const location_t &b)
static inline location_t create_location(double lat, double lon) static inline location_t create_location(double lat, double lon)
{ {
location_t location = { location_t location = {
{ (int) lrint(lat * 1000000) }, { .udeg = (int) lrint(lat * 1000000) },
{ (int) lrint(lon * 1000000) } { .udeg = (int) lrint(lon * 1000000) }
}; };
return location; return location;
} }
@ -255,7 +259,7 @@ static inline double mbar_to_atm(int mbar)
static inline double mbar_to_PSI(int mbar) static inline double mbar_to_PSI(int mbar)
{ {
pressure_t p = { mbar }; pressure_t p = { .mbar = mbar };
return to_PSI(p); return to_PSI(p);
} }

View file

@ -596,7 +596,7 @@ void DiveListView::mergeDives()
void DiveListView::splitDives() void DiveListView::splitDives()
{ {
for (struct dive *d: getDiveSelection()) for (struct dive *d: getDiveSelection())
Command::splitDives(d, duration_t{-1}); Command::splitDives(d, duration_t{ .seconds = -1});
} }
void DiveListView::addDivesToTrip() void DiveListView::addDivesToTrip()

View file

@ -62,7 +62,8 @@ RenumberDialog::RenumberDialog(bool selectedOnlyIn, QWidget *parent) : QDialog(p
void SetpointDialog::buttonClicked(QAbstractButton *button) void SetpointDialog::buttonClicked(QAbstractButton *button)
{ {
if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
Command::addEventSetpointChange(d, dcNr, time, pressure_t { (int)(1000.0 * ui.spinbox->value()) }); Command::addEventSetpointChange(d, dcNr, time,
pressure_t { .mbar = (int)(1000.0 * ui.spinbox->value()) });
} }
SetpointDialog::SetpointDialog(struct dive *dIn, int dcNrIn, int seconds) : QDialog(MainWindow::instance()), SetpointDialog::SetpointDialog(struct dive *dIn, int dcNrIn, int seconds) : QDialog(MainWindow::instance()),

View file

@ -1162,6 +1162,29 @@ bool QMLManager::checkDepth(dive *d, QString depth)
return false; return false;
} }
static weight_t parseWeight(const QString &text)
{
QString numOnly = text;
numOnly.replace(",", ".").remove(QRegularExpression("[^0-9.]"));
if (numOnly.isEmpty())
return {};
double number = numOnly.toDouble();
if (text.contains(gettextFromC::tr("kg"), Qt::CaseInsensitive)) {
return { .grams = static_cast<int>(lrint(number * 1000)) };
} else if (text.contains(gettextFromC::tr("lbs"), Qt::CaseInsensitive)) {
return { .grams = lbs_to_grams(number) };
} else {
switch (prefs.units.weight) {
case units::KG:
return { .grams = static_cast<int>(lrint(number * 1000)) };
case units::LBS:
return { .grams = lbs_to_grams(number) };
default:
return {};
}
}
}
// update the dive and return the notes field, stripped of the HTML junk // update the dive and return the notes field, stripped of the HTML junk
void QMLManager::commitChanges(QString diveId, QString number, QString date, QString location, QString gps, QString duration, QString depth, void QMLManager::commitChanges(QString diveId, QString number, QString date, QString location, QString gps, QString duration, QString depth,
QString airtemp, QString watertemp, QString suit, QString buddy, QString diveGuide, QString tags, QString weight, QString notes, QString airtemp, QString watertemp, QString suit, QString buddy, QString diveGuide, QString tags, QString weight, QString notes,
@ -1228,10 +1251,10 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt
// not sure what we'd do if there was more than one weight system // not sure what we'd do if there was more than one weight system
// defined - for now just ignore that case // defined - for now just ignore that case
if (d->weightsystems.size() == 0) { if (d->weightsystems.size() == 0) {
weightsystem_t ws = { { parseWeightToGrams(weight) } , tr("weight").toStdString(), false }; weightsystem_t ws = { parseWeight(weight), tr("weight").toStdString(), false };
d->weightsystems.add(0, std::move(ws)); d->weightsystems.add(0, std::move(ws));
} else if (d->weightsystems.size() == 1) { } else if (d->weightsystems.size() == 1) {
d->weightsystems[0].weight.grams = parseWeightToGrams(weight); d->weightsystems[0].weight = parseWeight(weight);
} }
} }
// start and end pressures // start and end pressures

View file

@ -551,7 +551,7 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, DivePlannerPointsM
// while all other items are up there on the constructor. // while all other items are up there on the constructor.
qDeleteAll(eventItems); qDeleteAll(eventItems);
eventItems.clear(); eventItems.clear();
struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, duration_t{1}); struct gasmix lastgasmix = d->get_gasmix_at_time(*currentdc, duration_t{ .seconds = 1 });
for (auto [idx, event]: enumerated_range(currentdc->events)) { for (auto [idx, event]: enumerated_range(currentdc->events)) {
// if print mode is selected only draw headings, SP change, gas events or bookmark event // if print mode is selected only draw headings, SP change, gas events or bookmark event

View file

@ -753,7 +753,7 @@ void ProfileWidget2::splitDive(int seconds)
{ {
if (!d) if (!d)
return; return;
Command::splitDives(mutable_dive(), duration_t{ seconds }); Command::splitDives(mutable_dive(), duration_t{ .seconds = seconds });
} }
void ProfileWidget2::addGasSwitch(int tank, int seconds) void ProfileWidget2::addGasSwitch(int tank, int seconds)
@ -1103,7 +1103,6 @@ void ProfileWidget2::updateThumbnail(QString filenameIn, QImage thumbnail, durat
// Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown. // Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown.
ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const std::string &filenameIn, ProfileWidget2 *profile, bool synchronous) : offset(offsetIn), ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const std::string &filenameIn, ProfileWidget2 *profile, bool synchronous) : offset(offsetIn),
duration(duration_t {0}),
filename(filenameIn), filename(filenameIn),
thumbnail(new DivePictureItem) thumbnail(new DivePictureItem)
{ {
@ -1289,7 +1288,7 @@ void ProfileWidget2::dropEvent(QDropEvent *event)
QString filename; QString filename;
dataStream >> filename; dataStream >> filename;
QPointF mappedPos = mapToScene(event->pos()); QPointF mappedPos = mapToScene(event->pos());
offset_t offset { (int32_t)lrint(profileScene->timeAxis->valueAt(mappedPos)) }; offset_t offset { .seconds = (int32_t)lrint(profileScene->timeAxis->valueAt(mappedPos)) };
Command::setPictureOffset(mutable_dive(), filename, offset); Command::setPictureOffset(mutable_dive(), filename, offset);
if (event->source() == this) { if (event->source() == this) {

View file

@ -86,7 +86,7 @@ static QVariant gas_usage_tooltip(const cylinder_t *cyl)
volume_t start = cyl->gas_volume(startp); volume_t start = cyl->gas_volume(startp);
volume_t end = cyl->gas_volume(endp); volume_t end = cyl->gas_volume(endp);
// TOOO: implement comparison and subtraction on units.h types. // TOOO: implement comparison and subtraction on units.h types.
volume_t used = (end.mliter && start.mliter > end.mliter) ? volume_t { start.mliter - end.mliter } : volume_t(); volume_t used = (end.mliter && start.mliter > end.mliter) ? volume_t { .mliter = start.mliter - end.mliter } : volume_t();
if (!used.mliter) if (!used.mliter)
return gas_wp_tooltip(cyl); return gas_wp_tooltip(cyl);

View file

@ -303,7 +303,6 @@ bool GPSLocationInformationModel::filterAcceptsRow(int sourceRow, const QModelIn
GPSLocationInformationModel::GPSLocationInformationModel(QObject *parent) : QSortFilterProxyModel(parent), GPSLocationInformationModel::GPSLocationInformationModel(QObject *parent) : QSortFilterProxyModel(parent),
ignoreDs(nullptr), ignoreDs(nullptr),
location({{0},{0}}),
distance(0) distance(0)
{ {
setSourceModel(LocationInformationModel::instance()); setSourceModel(LocationInformationModel::instance());

View file

@ -15,8 +15,7 @@
PictureEntry::PictureEntry(dive *dIn, const picture &p) : d(dIn), PictureEntry::PictureEntry(dive *dIn, const picture &p) : d(dIn),
filename(p.filename), filename(p.filename),
offsetSeconds(p.offset.seconds), offsetSeconds(p.offset.seconds)
length({ 0 })
{ {
} }

View file

@ -1001,7 +1001,7 @@ void smartrak_import(const char *file, struct divelog *log)
/* No DC related data */ /* No DC related data */
smtkdive->visibility = strtod((char *)col[coln(VISIBILITY)]->bind_ptr, NULL) > 25 ? 5 : lrint(strtod((char *)col[13]->bind_ptr, NULL) / 5); smtkdive->visibility = strtod((char *)col[coln(VISIBILITY)]->bind_ptr, NULL) > 25 ? 5 : lrint(strtod((char *)col[13]->bind_ptr, NULL) / 5);
weightsystem_t ws = { {(int)lrint(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, std::string(), false }; weightsystem_t ws = { { .grams = (int)lrint(strtod((char *)col[coln(WEIGHT)]->bind_ptr, NULL) * 1000)}, std::string(), false };
smtkdive->weightsystems.push_back(std::move(ws)); smtkdive->weightsystems.push_back(std::move(ws));
smtkdive->suit = get(suit_list, atoi((char *)col[coln(SUITIDX)]->bind_ptr) - 1); smtkdive->suit = get(suit_list, atoi((char *)col[coln(SUITIDX)]->bind_ptr) - 1);
smtk_build_location(mdb_clon, (char *)col[coln(SITEIDX)]->bind_ptr, &smtkdive->dive_site, log); smtk_build_location(mdb_clon, (char *)col[coln(SITEIDX)]->bind_ptr, &smtkdive->dive_site, log);

View file

@ -47,10 +47,10 @@ diveplan setupPlan()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{150}, {450}}; struct gasmix bottomgas = {{.permille = 150}, {.permille = 450}};
struct gasmix ean36 = {{360}, {0}}; struct gasmix ean36 = {{.permille = 360}, {}};
struct gasmix oxygen = {{1000}, {0}}; struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -82,10 +82,10 @@ diveplan setupPlanVpmb45m30mTx()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {350}}; struct gasmix bottomgas = {{.permille = 210}, {.permille = 350}};
struct gasmix ean50 = {{500}, {0}}; struct gasmix ean50 = {{.permille = 500}, {}};
struct gasmix oxygen = {{1000}, {0}}; struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -117,10 +117,10 @@ diveplan setupPlanVpmb60m10mTx()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{180}, {450}}; struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}};
struct gasmix tx50_15 = {{500}, {150}}; struct gasmix tx50_15 = {{.permille = 500}, {.permille = 150}};
struct gasmix oxygen = {{1000}, {0}}; struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -150,7 +150,7 @@ diveplan setupPlanVpmb60m30minAir()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {0}}; struct gasmix bottomgas = {{.permille = 210}, {}};
cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cylinder_t *cyl0 = dive.get_or_create_cylinder(0);
cyl0->gasmix = bottomgas; cyl0->gasmix = bottomgas;
cyl0->type.size.mliter = 100000; cyl0->type.size.mliter = 100000;
@ -172,9 +172,9 @@ diveplan setupPlanVpmb60m30minEan50()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {0}}; struct gasmix bottomgas = {{.permille = 210}, {}};
struct gasmix ean50 = {{500}, {0}}; struct gasmix ean50 = {{.permille = 500}, {}};
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -202,9 +202,9 @@ diveplan setupPlanVpmb60m30minTx()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{180}, {450}}; struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}};
struct gasmix ean50 = {{500}, {0}}; struct gasmix ean50 = {{.permille = 500}, {}};
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -232,7 +232,7 @@ diveplan setupPlanVpmbMultiLevelAir()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {0}}; struct gasmix bottomgas = {{.permille = 210}, {}};
cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cylinder_t *cyl0 = dive.get_or_create_cylinder(0);
cyl0->gasmix = bottomgas; cyl0->gasmix = bottomgas;
cyl0->type.size.mliter = 200000; cyl0->type.size.mliter = 200000;
@ -256,10 +256,10 @@ diveplan setupPlanVpmb100m60min()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{180}, {450}}; struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}};
struct gasmix ean50 = {{500}, {0}}; struct gasmix ean50 = {{.permille = 500}, {}};
struct gasmix oxygen = {{1000}, {0}}; struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -290,10 +290,10 @@ diveplan setupPlanVpmb100m10min()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{180}, {450}}; struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}};
struct gasmix ean50 = {{500}, {0}}; struct gasmix ean50 = {{.permille = 500}, {}};
struct gasmix oxygen = {{1000}, {0}}; struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -324,7 +324,7 @@ diveplan setupPlanVpmb30m20min()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {0}}; struct gasmix bottomgas = {{.permille = 210}, {}};
cylinder_t *cyl0 = dive.get_or_create_cylinder(0); cylinder_t *cyl0 = dive.get_or_create_cylinder(0);
cyl0->gasmix = bottomgas; cyl0->gasmix = bottomgas;
cyl0->type.size.mliter = 36000; cyl0->type.size.mliter = 36000;
@ -346,11 +346,11 @@ diveplan setupPlanVpmb100mTo70m30min()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{120}, {650}}; struct gasmix bottomgas = {{.permille = 120}, {.permille = 650}};
struct gasmix tx21_35 = {{210}, {350}}; struct gasmix tx21_35 = {{.permille = 210}, {.permille = 350}};
struct gasmix ean50 = {{500}, {0}}; struct gasmix ean50 = {{.permille = 500}, {}};
struct gasmix oxygen = {{1000}, {0}}; struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -388,8 +388,8 @@ diveplan setupPlanSeveralGases()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
struct gasmix ean36 = {{360}, {0}}; struct gasmix ean36 = {{.permille = 360}, {}};
struct gasmix tx11_50 = {{110}, {500}}; struct gasmix tx11_50 = {{.permille = 110}, {.permille = 500}};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
@ -420,10 +420,10 @@ diveplan setupPlanCcr()
dp.bottomsac = prefs.bottomsac; dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac; dp.decosac = prefs.decosac;
pressure_t po2 = {1600}; pressure_t po2 = {.mbar = 1600};
struct gasmix diluent = {{200}, {210}}; struct gasmix diluent = {{.permille = 200}, {.permille = 210}};
struct gasmix ean53 = {{530}, {0}}; struct gasmix ean53 = {{.permille = 530}, {}};
struct gasmix tx19_33 = {{190}, {330}}; struct gasmix tx19_33 = {{.permille = 190}, {.permille = 330}};
// Note: we add the highest-index cylinder first, because // Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating. // pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code! // For testing OK - don't do this in actual code!
@ -754,7 +754,7 @@ void TestPlan::testMultipleGases()
save_dive(stdout, dive, false); save_dive(stdout, dive, false);
#endif #endif
gasmix gas = dive.get_gasmix_at_time(dive.dcs[0], {20 * 60 + 1}); gasmix gas = dive.get_gasmix_at_time(dive.dcs[0], {.seconds = 20 * 60 + 1});
QCOMPARE(get_o2(gas), 110); QCOMPARE(get_o2(gas), 110);
QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 2480u, 2480u)); QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 2480u, 2480u));
} }
@ -934,17 +934,17 @@ void TestPlan::testCcrBailoutGasSelection()
#endif #endif
// check diluent used // check diluent used
cylinder_t *cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 - 1 })); cylinder_t *cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 20 * 60 - 1 }));
QCOMPARE(cylinder->cylinder_use, DILUENT); QCOMPARE(cylinder->cylinder_use, DILUENT);
QCOMPARE(get_o2(cylinder->gasmix), 200); QCOMPARE(get_o2(cylinder->gasmix), 200);
// check deep bailout used // check deep bailout used
cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 20 * 60 + 1 })); cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 20 * 60 + 1 }));
QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(cylinder->cylinder_use, OC_GAS);
QCOMPARE(get_o2(cylinder->gasmix), 190); QCOMPARE(get_o2(cylinder->gasmix), 190);
// check shallow bailout used // check shallow bailout used
cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { 30 * 60 })); cylinder = dive.get_cylinder(get_cylinderid_at_time(&dive, &dive.dcs[0], { .seconds = 30 * 60 }));
QCOMPARE(cylinder->cylinder_use, OC_GAS); QCOMPARE(cylinder->cylinder_use, OC_GAS);
QCOMPARE(get_o2(cylinder->gasmix), 530); QCOMPARE(get_o2(cylinder->gasmix), 530);

View file

@ -11,14 +11,14 @@ void TestUnitConversion::testUnitConversions()
QCOMPARE(nearly_equal(cuft_to_l(1), 28.316847), true); QCOMPARE(nearly_equal(cuft_to_l(1), 28.316847), true);
QCOMPARE(nearly_equal(mm_to_feet(1000), 3.280840), true); QCOMPARE(nearly_equal(mm_to_feet(1000), 3.280840), true);
QCOMPARE(feet_to_mm(1), 305L); QCOMPARE(feet_to_mm(1), 305L);
QCOMPARE(to_feet((depth_t){1000}), 3); QCOMPARE(to_feet(depth_t{ .mm = 1'000}), 3);
QCOMPARE(nearly_equal(mkelvin_to_C(647000), 373.85), true); QCOMPARE(nearly_equal(mkelvin_to_C(647000), 373.85), true);
QCOMPARE(nearly_equal(mkelvin_to_F(647000), 704.93), true); QCOMPARE(nearly_equal(mkelvin_to_F(647000), 704.93), true);
QCOMPARE(F_to_mkelvin(704.93), 647000UL); QCOMPARE(F_to_mkelvin(704.93), 647000UL);
QCOMPARE(C_to_mkelvin(373.85), 647000UL); QCOMPARE(C_to_mkelvin(373.85), 647000UL);
QCOMPARE(nearly_equal(psi_to_bar(14.6959488), 1.01325), true); QCOMPARE(nearly_equal(psi_to_bar(14.6959488), 1.01325), true);
QCOMPARE(psi_to_mbar(14.6959488), 1013L); QCOMPARE(psi_to_mbar(14.6959488), 1013L);
QCOMPARE(nearly_equal(to_PSI((pressure_t){1013}), 14.6923228594), true); QCOMPARE(nearly_equal(to_PSI(pressure_t{ .mbar = 1013}), 14.6923228594), true);
QCOMPARE(nearly_equal(bar_to_atm(1.013), 1.0), true); QCOMPARE(nearly_equal(bar_to_atm(1.013), 1.0), true);
QCOMPARE(nearly_equal(mbar_to_atm(1013), 1.0), true); QCOMPARE(nearly_equal(mbar_to_atm(1013), 1.0), true);
QCOMPARE(nearly_equal(mbar_to_PSI(1013), 14.6923228594), true); QCOMPARE(nearly_equal(mbar_to_PSI(1013), 14.6923228594), true);