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(dive *source) : d(current_dive),
when(0),
maxdepth({0}),
meandepth({0}),
surface_pressure({0}),
duration({0}),
salinity(0)
{
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),
dcNr(dcNr),
maxdepth({0}),
meandepth({0}),
dcmaxdepth({0}),
duration({0})
dcNr(dcNr)
{
const struct divecomputer *sdc = source->get_dc(dcNr);
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]);
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));
}

View file

@ -2248,7 +2248,7 @@ duration_t dive::totaltime() const
time = dc_time;
}
}
return { time };
return { .seconds = time };
}
timestamp_t dive::endtime() const
@ -2367,7 +2367,7 @@ bool dive::cache_is_valid() const
pressure_t dive::get_surface_pressure() const
{
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
@ -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
{
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 */
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)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
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
@ -2677,7 +2677,7 @@ temperature_t dive::dc_watertemp() const
}
if (!nr)
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)
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
return std::accumulate(weightsystems.begin(), weightsystems.end(), weight_t(),
[] (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)
{
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)
size.mliter = info.ml;
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)
{ return info.name == name; });
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)
@ -182,7 +182,7 @@ volume_t cylinder_t::gas_volume(pressure_t p) const
{
double bar = p.mbar / 1000.0;
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)

View file

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

View file

@ -217,7 +217,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
// is not repeated ad-nauseum for broken images.
if (numPics == 0 && prefs.extract_video_thumbnails) {
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
@ -231,7 +231,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
res = videoImage; // No picture -> show dummy-icon
else
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.

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]));
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));
}

View file

@ -160,7 +160,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_
}
}
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) {
bottom_gas = gasmix_air;
}

View file

@ -41,7 +41,7 @@ preferences::preferences() :
ascratestops(9000 / 60),
ascrate50(9000 / 60),
ascrate75(9000 / 60),
bestmixend({ 30000 }),
bestmixend({ .mm = 30'000 }),
bottompo2(1400),
bottomsac(20000),
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);
// TODO: Implement addition/subtraction on units.h types
volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ last_pressures[cylinder_index] }).mliter -
cyl->gas_volume((pressure_t){ next_pressure }).mliter;
volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ .mbar = last_pressures[cylinder_index] }).mliter -
cyl->gas_volume((pressure_t){ .mbar = next_pressure }).mliter;
}
// check if the gas in this cylinder is being used

View file

@ -786,33 +786,6 @@ int parseTemperatureToMkelvin(const QString &text)
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 mbar;

View file

@ -69,7 +69,6 @@ timestamp_t dateTimeToTimestamp(const QDateTime &t);
int parseDurationToSeconds(const QString &text);
int parseLengthToMm(const QString &text);
int parseTemperatureToMkelvin(const QString &text);
int parseWeightToGrams(const QString &text);
int parsePressureToMbar(const QString &text);
int parseGasMixO2(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 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 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
@ -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)
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)
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
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

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))
return { volume_t() , volume_t() };
volume_t air = { (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 o2 = { vol.mliter - he.mliter - air.mliter };
volume_t air { .mliter = (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) };
volume_t he { .mliter = (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) };
volume_t o2 { .mliter = vol.mliter - he.mliter - air.mliter };
return std::make_pair(o2, he);
}

View file

@ -67,62 +67,66 @@
*/
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
};
struct offset_t
struct offset_t : public unit_base<offset_t>
{
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;
};
struct pressure_t
struct pressure_t : public unit_base<pressure_t>
{
int32_t mbar = 0; // pressure up to 2000 bar
};
struct o2pressure_t
struct o2pressure_t : public unit_base<o2pressure_t>
{
uint16_t mbar = 0;
};
struct bearing_t
struct bearing_t : public unit_base<bearing_t>
{
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)
};
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)
};
struct volume_t
struct volume_t : public unit_base<volume_t>
{
int mliter = 0;
};
struct fraction_t
struct fraction_t : public unit_base<fraction_t>
{
int permille = 0;
};
struct weight_t
struct weight_t : public unit_base<weight_t>
{
int grams = 0;
};
struct degrees_t
struct degrees_t : public unit_base<degrees_t>
{
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)
{
location_t location = {
{ (int) lrint(lat * 1000000) },
{ (int) lrint(lon * 1000000) }
{ .udeg = (int) lrint(lat * 1000000) },
{ .udeg = (int) lrint(lon * 1000000) }
};
return location;
}
@ -255,7 +259,7 @@ static inline double mbar_to_atm(int mbar)
static inline double mbar_to_PSI(int mbar)
{
pressure_t p = { mbar };
pressure_t p = { .mbar = mbar };
return to_PSI(p);
}

View file

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

View file

@ -62,7 +62,8 @@ RenumberDialog::RenumberDialog(bool selectedOnlyIn, QWidget *parent) : QDialog(p
void SetpointDialog::buttonClicked(QAbstractButton *button)
{
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()),

View file

@ -1162,6 +1162,29 @@ bool QMLManager::checkDepth(dive *d, QString depth)
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
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,
@ -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
// defined - for now just ignore that case
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));
} else if (d->weightsystems.size() == 1) {
d->weightsystems[0].weight.grams = parseWeightToGrams(weight);
d->weightsystems[0].weight = parseWeight(weight);
}
}
// 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.
qDeleteAll(eventItems);
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)) {
// 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)
return;
Command::splitDives(mutable_dive(), duration_t{ seconds });
Command::splitDives(mutable_dive(), duration_t{ .seconds = 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.
ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const std::string &filenameIn, ProfileWidget2 *profile, bool synchronous) : offset(offsetIn),
duration(duration_t {0}),
filename(filenameIn),
thumbnail(new DivePictureItem)
{
@ -1289,7 +1288,7 @@ void ProfileWidget2::dropEvent(QDropEvent *event)
QString filename;
dataStream >> filename;
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);
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 end = cyl->gas_volume(endp);
// 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)
return gas_wp_tooltip(cyl);

View file

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

View file

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

View file

@ -1001,7 +1001,7 @@ void smartrak_import(const char *file, struct divelog *log)
/* 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);
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->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);

View file

@ -47,10 +47,10 @@ diveplan setupPlan()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{150}, {450}};
struct gasmix ean36 = {{360}, {0}};
struct gasmix oxygen = {{1000}, {0}};
pressure_t po2 = {1600};
struct gasmix bottomgas = {{.permille = 150}, {.permille = 450}};
struct gasmix ean36 = {{.permille = 360}, {}};
struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -82,10 +82,10 @@ diveplan setupPlanVpmb45m30mTx()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {350}};
struct gasmix ean50 = {{500}, {0}};
struct gasmix oxygen = {{1000}, {0}};
pressure_t po2 = {1600};
struct gasmix bottomgas = {{.permille = 210}, {.permille = 350}};
struct gasmix ean50 = {{.permille = 500}, {}};
struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -117,10 +117,10 @@ diveplan setupPlanVpmb60m10mTx()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{180}, {450}};
struct gasmix tx50_15 = {{500}, {150}};
struct gasmix oxygen = {{1000}, {0}};
pressure_t po2 = {1600};
struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}};
struct gasmix tx50_15 = {{.permille = 500}, {.permille = 150}};
struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -150,7 +150,7 @@ diveplan setupPlanVpmb60m30minAir()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {0}};
struct gasmix bottomgas = {{.permille = 210}, {}};
cylinder_t *cyl0 = dive.get_or_create_cylinder(0);
cyl0->gasmix = bottomgas;
cyl0->type.size.mliter = 100000;
@ -172,9 +172,9 @@ diveplan setupPlanVpmb60m30minEan50()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {0}};
struct gasmix ean50 = {{500}, {0}};
pressure_t po2 = {1600};
struct gasmix bottomgas = {{.permille = 210}, {}};
struct gasmix ean50 = {{.permille = 500}, {}};
pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -202,9 +202,9 @@ diveplan setupPlanVpmb60m30minTx()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{180}, {450}};
struct gasmix ean50 = {{500}, {0}};
pressure_t po2 = {1600};
struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}};
struct gasmix ean50 = {{.permille = 500}, {}};
pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -232,7 +232,7 @@ diveplan setupPlanVpmbMultiLevelAir()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {0}};
struct gasmix bottomgas = {{.permille = 210}, {}};
cylinder_t *cyl0 = dive.get_or_create_cylinder(0);
cyl0->gasmix = bottomgas;
cyl0->type.size.mliter = 200000;
@ -256,10 +256,10 @@ diveplan setupPlanVpmb100m60min()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{180}, {450}};
struct gasmix ean50 = {{500}, {0}};
struct gasmix oxygen = {{1000}, {0}};
pressure_t po2 = {1600};
struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}};
struct gasmix ean50 = {{.permille = 500}, {}};
struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -290,10 +290,10 @@ diveplan setupPlanVpmb100m10min()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{180}, {450}};
struct gasmix ean50 = {{500}, {0}};
struct gasmix oxygen = {{1000}, {0}};
pressure_t po2 = {1600};
struct gasmix bottomgas = {{.permille = 180}, {.permille = 450}};
struct gasmix ean50 = {{.permille = 500}, {}};
struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -324,7 +324,7 @@ diveplan setupPlanVpmb30m20min()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{210}, {0}};
struct gasmix bottomgas = {{.permille = 210}, {}};
cylinder_t *cyl0 = dive.get_or_create_cylinder(0);
cyl0->gasmix = bottomgas;
cyl0->type.size.mliter = 36000;
@ -346,11 +346,11 @@ diveplan setupPlanVpmb100mTo70m30min()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix bottomgas = {{120}, {650}};
struct gasmix tx21_35 = {{210}, {350}};
struct gasmix ean50 = {{500}, {0}};
struct gasmix oxygen = {{1000}, {0}};
pressure_t po2 = {1600};
struct gasmix bottomgas = {{.permille = 120}, {.permille = 650}};
struct gasmix tx21_35 = {{.permille = 210}, {.permille = 350}};
struct gasmix ean50 = {{.permille = 500}, {}};
struct gasmix oxygen = {{.permille = 1000}, {}};
pressure_t po2 = {.mbar = 1600};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -388,8 +388,8 @@ diveplan setupPlanSeveralGases()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
struct gasmix ean36 = {{360}, {0}};
struct gasmix tx11_50 = {{110}, {500}};
struct gasmix ean36 = {{.permille = 360}, {}};
struct gasmix tx11_50 = {{.permille = 110}, {.permille = 500}};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
@ -420,10 +420,10 @@ diveplan setupPlanCcr()
dp.bottomsac = prefs.bottomsac;
dp.decosac = prefs.decosac;
pressure_t po2 = {1600};
struct gasmix diluent = {{200}, {210}};
struct gasmix ean53 = {{530}, {0}};
struct gasmix tx19_33 = {{190}, {330}};
pressure_t po2 = {.mbar = 1600};
struct gasmix diluent = {{.permille = 200}, {.permille = 210}};
struct gasmix ean53 = {{.permille = 530}, {}};
struct gasmix tx19_33 = {{.permille = 190}, {.permille = 330}};
// Note: we add the highest-index cylinder first, because
// pointers to cylinders are not stable when reallocating.
// For testing OK - don't do this in actual code!
@ -754,7 +754,7 @@ void TestPlan::testMultipleGases()
save_dive(stdout, dive, false);
#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);
QVERIFY(compareDecoTime(dive.dcs[0].duration.seconds, 2480u, 2480u));
}
@ -934,17 +934,17 @@ void TestPlan::testCcrBailoutGasSelection()
#endif
// 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(get_o2(cylinder->gasmix), 200);
// 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(get_o2(cylinder->gasmix), 190);
// 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(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(mm_to_feet(1000), 3.280840), true);
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_F(647000), 704.93), true);
QCOMPARE(F_to_mkelvin(704.93), 647000UL);
QCOMPARE(C_to_mkelvin(373.85), 647000UL);
QCOMPARE(nearly_equal(psi_to_bar(14.6959488), 1.01325), true);
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(mbar_to_atm(1013), 1.0), true);
QCOMPARE(nearly_equal(mbar_to_PSI(1013), 14.6923228594), true);