diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 5121222cc..3a0806454 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -86,6 +86,8 @@ set(SUBSURFACE_CORE_LIB_SRCS file.h format.cpp format.h + gas.c + gas.h gas-model.c gaspressures.c gaspressures.h diff --git a/core/dive.c b/core/dive.c index 2d82f5565..a54c3aa00 100644 --- a/core/dive.c +++ b/core/dive.c @@ -866,13 +866,6 @@ static void update_min_max_temperatures(struct dive *dive, temperature_t tempera } } -int gas_volume(const cylinder_t *cyl, pressure_t p) -{ - double bar = p.mbar / 1000.0; - double z_factor = gas_compressibility_factor(cyl->gasmix, bar); - return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor); -} - /* * If the cylinder tank pressures are within half a bar * (about 8 PSI) of the sample pressures, we consider it @@ -948,31 +941,6 @@ void update_setpoint_events(const struct dive *dive, struct divecomputer *dc) } } -void sanitize_gasmix(struct gasmix *mix) -{ - unsigned int o2, he; - - o2 = mix->o2.permille; - he = mix->he.permille; - - /* Regular air: leave empty */ - if (!he) { - if (!o2) - return; - /* 20.8% to 21% O2 is just air */ - if (gasmix_is_air(*mix)) { - mix->o2.permille = 0; - return; - } - } - - /* Sane mix? */ - if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000) - return; - fprintf(stderr, "Odd gasmix: %u O2 %u He\n", o2, he); - memset(mix, 0, sizeof(*mix)); -} - /* * See if the size/workingpressure looks like some standard cylinder * size, eg "AL80". @@ -1059,23 +1027,6 @@ static void sanitize_cylinder_info(struct dive *dive) } } -/* Perform isobaric counterdiffusion calculations for gas changes in trimix dives. - * Here we use the rule-of-fifths where, during a change involving trimix gas, the increase in nitrogen - * should not exceed one fifth of the decrease in helium. - * Parameters: 1) pointers to two gas mixes, the gas being switched from and the gas being switched to. - * 2) a pointer to an icd_data structure. - * Output: i) The icd_data stucture is filled with the delta_N2 and delta_He numbers (as permille). - * ii) Function returns a boolean indicating an exceeding of the rule-of-fifths. False = no icd problem. - */ -bool isobaric_counterdiffusion(struct gasmix oldgasmix, struct gasmix newgasmix, struct icd_data *results) -{ - if (!prefs.show_icd) - return false; - results->dN2 = get_he(oldgasmix) + get_o2(oldgasmix) - get_he(newgasmix) - get_o2(newgasmix); - results->dHe = get_he(newgasmix) - get_he(oldgasmix); - return get_he(oldgasmix) > 0 && results->dN2 > 0 && results->dHe < 0 && get_he(oldgasmix) && results->dN2 > 0 && 5 * results->dN2 > -results->dHe; -} - /* some events should never be thrown away */ static bool is_potentially_redundant(const struct event *event) { @@ -1464,29 +1415,6 @@ static void fixup_dive_pressures(struct dive *dive, struct divecomputer *dc) simplify_dc_pressures(dc); } -int find_best_gasmix_match(struct gasmix mix, const cylinder_t array[], unsigned int used) -{ - int i; - int best = -1, score = INT_MAX; - - for (i = 0; i < MAX_CYLINDERS; i++) { - const cylinder_t *match; - int distance; - - if (used & (1 << i)) - continue; - match = array + i; - if (cylinder_nodata(match)) - continue; - distance = gasmix_distance(mix, match->gasmix); - if (distance >= score) - continue; - best = i; - score = distance; - } - return best; -} - /* * Match a gas change event against the cylinders we have */ @@ -2031,17 +1959,6 @@ extern int get_cylinder_idx_by_use(const struct dive *dive, enum cylinderuse cyl return -1; // negative number means cylinder_use_type not found in list of cylinders } -int gasmix_distance(struct gasmix a, struct gasmix b) -{ - int a_o2 = get_o2(a), b_o2 = get_o2(b); - int a_he = get_he(a), b_he = get_he(b); - int delta_o2 = a_o2 - b_o2, delta_he = a_he - b_he; - - delta_he = delta_he * delta_he; - delta_o2 = delta_o2 * delta_o2; - return delta_he + delta_o2; -} - /* fill_pressures(): Compute partial gas pressures in bar from gasmix and ambient pressures, possibly for OC or CCR, to be * extended to PSCT. This function does the calculations of gas pressures applicable to a single point on the dive profile. * The structure "pressures" is used to return calculated gas pressures to the calling software. @@ -2052,7 +1969,7 @@ int gasmix_distance(struct gasmix a, struct gasmix b) * divemode = the dive mode pertaining to this point in the dive profile. * This function called by: calculate_gas_information_new() in profile.c; add_segment() in deco.c. */ -extern void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode) +void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, struct gasmix mix, double po2, enum divemode_t divemode) { if ((divemode != OC) && po2) { // This is a rebreather dive where pressures->o2 is defined if (po2 >= amb_pressure) { @@ -2180,20 +2097,6 @@ void cylinder_renumber(struct dive *dive, int mapping[]) dc_cylinder_renumber(dive, dc, mapping); } -static bool gasmix_is_invalid(struct gasmix mix) -{ - return mix.o2.permille < 0; -} - -int same_gasmix(struct gasmix a, struct gasmix b) -{ - if (gasmix_is_invalid(a) || gasmix_is_invalid(b)) - return 0; - if (gasmix_is_air(a) && gasmix_is_air(b)) - return 1; - return a.o2.permille == b.o2.permille && a.he.permille == b.he.permille; -} - int same_gasmix_cylinder(cylinder_t *cyl, int cylid, struct dive *dive, bool check_unused) { struct gasmix mygas = cyl->gasmix; @@ -3922,13 +3825,6 @@ fraction_t best_he(depth_t depth, const struct dive *dive) return fhe; } -bool gasmix_is_air(struct gasmix gasmix) -{ - int o2 = gasmix.o2.permille; - int he = gasmix.he.permille; - return (he == 0) && (o2 == 0 || ((o2 >= O2_IN_AIR - 1) && (o2 <= O2_IN_AIR + 1))); -} - void invalidate_dive_cache(struct dive *dive) { memset(dive->git_id, 0, 20); diff --git a/core/dive.h b/core/dive.h index c6b08abd4..042f825f4 100644 --- a/core/dive.h +++ b/core/dive.h @@ -27,13 +27,6 @@ extern const char *cylinderuse_text[]; extern const char *divemode_text_ui[]; extern const char *divemode_text[]; -struct icd_data { // This structure provides communication between function isobaric_counterdiffusion() and the calling software. - int dN2; // The change in fraction (permille) of nitrogen during the change - int dHe; // The change in fraction (permille) of helium during the change -}; - -extern bool isobaric_counterdiffusion(struct gasmix oldgasmix, struct gasmix newgasmix, struct icd_data *results); - /* * Events are currently based straight on what libdivecomputer gives us. * We need to wrap these into our own events at some point to remove some of the limitations. @@ -63,35 +56,8 @@ struct event { extern int event_is_gaschange(const struct event *ev); -/* Volume in mliter of a cylinder at pressure 'p' */ -extern int gas_volume(const cylinder_t *cyl, pressure_t p); -extern double gas_compressibility_factor(struct gasmix gas, double bar); -extern double isothermal_pressure(struct gasmix gas, double p1, int volume1, int volume2); -extern double gas_density(struct gasmix gas, int pressure); -extern int same_gasmix(struct gasmix a, struct gasmix b); - -static inline int get_o2(struct gasmix mix) -{ - return mix.o2.permille ?: O2_IN_AIR; -} - -static inline int get_he(struct gasmix mix) -{ - return mix.he.permille; -} - -struct gas_pressures { - double o2, n2, he; -}; - extern void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, struct gasmix mix, double po2, enum divemode_t dctype); -extern void sanitize_gasmix(struct gasmix *mix); -extern int gasmix_distance(struct gasmix a, struct gasmix b); -extern int find_best_gasmix_match(struct gasmix mix, const cylinder_t array[], unsigned int used); - -extern bool gasmix_is_air(struct gasmix gasmix); - /* Linear interpolation between 'a' and 'b', when we are 'part'way into the 'whole' distance from a to b */ static inline int interpolate(int a, int b, int part, int whole) { diff --git a/core/equipment.c b/core/equipment.c index 325c57cb7..81033ace6 100644 --- a/core/equipment.c +++ b/core/equipment.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "equipment.h" #include "gettext.h" #include "dive.h" @@ -101,6 +102,36 @@ const char *gasname(struct gasmix gasmix) return gas; } +int gas_volume(const cylinder_t *cyl, pressure_t p) +{ + double bar = p.mbar / 1000.0; + double z_factor = gas_compressibility_factor(cyl->gasmix, bar); + return lrint(cyl->type.size.mliter * bar_to_atm(bar) / z_factor); +} + +int find_best_gasmix_match(struct gasmix mix, const cylinder_t array[], unsigned int used) +{ + int i; + int best = -1, score = INT_MAX; + + for (i = 0; i < MAX_CYLINDERS; i++) { + const cylinder_t *match; + int distance; + + if (used & (1 << i)) + continue; + match = array + i; + if (cylinder_nodata(match)) + continue; + distance = gasmix_distance(mix, match->gasmix); + if (distance >= score) + continue; + best = i; + score = distance; + } + return best; +} + bool weightsystem_none(const weightsystem_t *ws) { return !ws->weight.grams && !ws->description; diff --git a/core/equipment.h b/core/equipment.h index 73e21b8ed..eb789ede8 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -2,7 +2,7 @@ #ifndef EQUIPMENT_H #define EQUIPMENT_H -#include "units.h" +#include "gas.h" #ifdef __cplusplus extern "C" { @@ -10,15 +10,6 @@ extern "C" { struct dive; -// o2 == 0 && he == 0 -> air -// o2 < 0 -> invalid -struct gasmix { - fraction_t o2; - fraction_t he; -}; -static const struct gasmix gasmix_invalid = { { -1 }, { -1 } }; -static const struct gasmix gasmix_air = { { 0 }, { 0 } }; - enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NOT_USED, NUM_GAS_USE}; // The different uses for cylinders typedef struct @@ -63,6 +54,8 @@ extern bool weightsystem_none(const weightsystem_t *ws); extern void remove_cylinder(struct dive *dive, int idx); extern void remove_weightsystem(struct dive *dive, int idx); extern void reset_cylinders(struct dive *dive, bool track_gas); +extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */ +extern int find_best_gasmix_match(struct gasmix mix, const cylinder_t array[], unsigned int used); #ifdef DEBUG_CYL extern void dump_cylinders(struct dive *dive, bool verbose); #endif diff --git a/core/gas.c b/core/gas.c new file mode 100644 index 000000000..9f67d0b92 --- /dev/null +++ b/core/gas.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "gas.h" +#include "pref.h" +#include +#include + +/* Perform isobaric counterdiffusion calculations for gas changes in trimix dives. + * Here we use the rule-of-fifths where, during a change involving trimix gas, the increase in nitrogen + * should not exceed one fifth of the decrease in helium. + * Parameters: 1) pointers to two gas mixes, the gas being switched from and the gas being switched to. + * 2) a pointer to an icd_data structure. + * Output: i) The icd_data stucture is filled with the delta_N2 and delta_He numbers (as permille). + * ii) Function returns a boolean indicating an exceeding of the rule-of-fifths. False = no icd problem. + */ +bool isobaric_counterdiffusion(struct gasmix oldgasmix, struct gasmix newgasmix, struct icd_data *results) +{ + if (!prefs.show_icd) + return false; + results->dN2 = get_he(oldgasmix) + get_o2(oldgasmix) - get_he(newgasmix) - get_o2(newgasmix); + results->dHe = get_he(newgasmix) - get_he(oldgasmix); + return get_he(oldgasmix) > 0 && results->dN2 > 0 && results->dHe < 0 && get_he(oldgasmix) && results->dN2 > 0 && 5 * results->dN2 > -results->dHe; +} + +static bool gasmix_is_invalid(struct gasmix mix) +{ + return mix.o2.permille < 0; +} + +int same_gasmix(struct gasmix a, struct gasmix b) +{ + if (gasmix_is_invalid(a) || gasmix_is_invalid(b)) + return 0; + if (gasmix_is_air(a) && gasmix_is_air(b)) + return 1; + return a.o2.permille == b.o2.permille && a.he.permille == b.he.permille; +} + +void sanitize_gasmix(struct gasmix *mix) +{ + unsigned int o2, he; + + o2 = mix->o2.permille; + he = mix->he.permille; + + /* Regular air: leave empty */ + if (!he) { + if (!o2) + return; + /* 20.8% to 21% O2 is just air */ + if (gasmix_is_air(*mix)) { + mix->o2.permille = 0; + return; + } + } + + /* Sane mix? */ + if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000) + return; + fprintf(stderr, "Odd gasmix: %u O2 %u He\n", o2, he); + memset(mix, 0, sizeof(*mix)); +} + +int gasmix_distance(struct gasmix a, struct gasmix b) +{ + int a_o2 = get_o2(a), b_o2 = get_o2(b); + int a_he = get_he(a), b_he = get_he(b); + int delta_o2 = a_o2 - b_o2, delta_he = a_he - b_he; + + delta_he = delta_he * delta_he; + delta_o2 = delta_o2 * delta_o2; + return delta_he + delta_o2; +} + +bool gasmix_is_air(struct gasmix gasmix) +{ + int o2 = gasmix.o2.permille; + int he = gasmix.he.permille; + return (he == 0) && (o2 == 0 || ((o2 >= O2_IN_AIR - 1) && (o2 <= O2_IN_AIR + 1))); +} diff --git a/core/gas.h b/core/gas.h new file mode 100644 index 000000000..f5d948da0 --- /dev/null +++ b/core/gas.h @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef GAS_H +#define GAS_H + +#include "units.h" + +#ifdef __cplusplus +extern "C" { +#else +#include +#endif + +// o2 == 0 && he == 0 -> air +// o2 < 0 -> invalid +struct gasmix { + fraction_t o2; + fraction_t he; +}; +static const struct gasmix gasmix_invalid = { { -1 }, { -1 } }; +static const struct gasmix gasmix_air = { { 0 }, { 0 } }; + +struct icd_data { // This structure provides communication between function isobaric_counterdiffusion() and the calling software. + int dN2; // The change in fraction (permille) of nitrogen during the change + int dHe; // The change in fraction (permille) of helium during the change +}; + +extern bool isobaric_counterdiffusion(struct gasmix oldgasmix, struct gasmix newgasmix, struct icd_data *results); + +extern double gas_compressibility_factor(struct gasmix gas, double bar); +extern double isothermal_pressure(struct gasmix gas, double p1, int volume1, int volume2); +extern double gas_density(struct gasmix gas, int pressure); +extern int same_gasmix(struct gasmix a, struct gasmix b); + +static inline int get_o2(struct gasmix mix) +{ + return mix.o2.permille ?: O2_IN_AIR; +} + +static inline int get_he(struct gasmix mix) +{ + return mix.he.permille; +} + +struct gas_pressures { + double o2, n2, he; +}; + +extern void sanitize_gasmix(struct gasmix *mix); +extern int gasmix_distance(struct gasmix a, struct gasmix b); + +extern bool gasmix_is_air(struct gasmix gasmix); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/packaging/ios/Subsurface-mobile.pro b/packaging/ios/Subsurface-mobile.pro index 7d78ade94..02bb4076a 100644 --- a/packaging/ios/Subsurface-mobile.pro +++ b/packaging/ios/Subsurface-mobile.pro @@ -67,6 +67,7 @@ SOURCES += ../../subsurface-mobile-main.cpp \ ../../core/deco.c \ ../../core/divesite.c \ ../../core/equipment.c \ + ../../core/gas.c \ ../../core/membuffer.c \ ../../core/sha1.c \ ../../core/strtod.c \