Allow for more O2 sensors

The Divesoft Liberty has four O2 sensors. So far, we had a hard coded
limit of three sensors and crashed with a failed assert when we
encoutered more than three. This allows for up to
MAX_O2_SENSORS which is currently 6. The voting logic is adapted
accordingly: We sort the values and we keep deleting the values that
differ more than 20% by value from the closest. This follows what
Shearwater implements on their computers.

In some of the import/export functions the value is still hard
coded to 6 thanks to explicit field names.

Signed-off-by: Robert C. Helling <helling@lmu.de>
This commit is contained in:
Robert C. Helling 2024-01-21 00:35:44 +01:00 committed by Robert C. Helling
parent e00e1bb9f7
commit e794efaba6
13 changed files with 31329 additions and 31257 deletions

View file

@ -1,3 +1,4 @@
core: allow of up to 6 O2 sensors (and corresponding voting logic)
desktop: add divemode as a possible dive list column
profile-widget: Now zomed in profiles can be panned with horizontal scroll.
desktop: hide only events with the same severity when 'Hide similar events' is used

View file

@ -1184,23 +1184,20 @@ static void fixup_no_o2sensors(struct divecomputer *dc)
return;
for (int i = 0; i < dc->samples; i++) {
int nsensor = 0;
int nsensor = 0, j;
struct sample *s = dc->sample + i;
// How many o2 sensors can we find in this sample?
if (s->o2sensor[0].mbar)
nsensor++;
if (s->o2sensor[1].mbar)
nsensor++;
if (s->o2sensor[2].mbar)
nsensor++;
for (j = 0; j < MAX_O2_SENSORS; j++)
if (s->o2sensor[j].mbar)
nsensor++;
// If we fond more than the previous found max, record it.
if (nsensor > dc->no_o2sensors)
dc->no_o2sensors = nsensor;
// Already found the maximum posible amount.
if (nsensor == 3)
if (nsensor == MAX_O2_SENSORS)
return;
}
}

View file

@ -456,7 +456,7 @@ sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata)
sample->setpoint.mbar = po2 = lrint(value.setpoint * 1000);
break;
case DC_SAMPLE_PPO2:
if (nsensor < 3)
if (nsensor < MAX_O2_SENSORS)
sample->o2sensor[nsensor].mbar = lrint(value.ppo2 * 1000);
else
report_error("%d is more o2 sensors than we can handle", nsensor);

View file

@ -589,6 +589,21 @@ static void parse_sample_keyvalue(void *_sample, const char *key, const char *va
sample->o2sensor[2].mbar = p.mbar;
return;
}
if (!strcmp(key, "sensor4")) {
pressure_t p = get_pressure(value);
sample->o2sensor[3].mbar = p.mbar;
return;
}
if (!strcmp(key, "sensor5")) {
pressure_t p = get_pressure(value);
sample->o2sensor[4].mbar = p.mbar;
return;
}
if (!strcmp(key, "sensor6")) {
pressure_t p = get_pressure(value);
sample->o2sensor[5].mbar = p.mbar;
return;
}
if (!strcmp(key, "o2pressure")) {
pressure_t p = get_pressure(value);
sample->pressure[1].mbar = p.mbar;

View file

@ -932,7 +932,13 @@ static void try_to_fill_sample(struct sample *sample, const char *name, char *bu
return;
if (MATCH("sensor2.sample", double_to_o2pressure, &sample->o2sensor[1]))
return;
if (MATCH("sensor3.sample", double_to_o2pressure, &sample->o2sensor[2])) // up to 3 CCR sensors
if (MATCH("sensor3.sample", double_to_o2pressure, &sample->o2sensor[2]))
return;
if (MATCH("sensor4.sample", double_to_o2pressure, &sample->o2sensor[3]))
return;
if (MATCH("sensor5.sample", double_to_o2pressure, &sample->o2sensor[4]))
return;
if (MATCH("sensor6.sample", double_to_o2pressure, &sample->o2sensor[5])) // up to 6 CCR sensors
return;
if (MATCH("po2.sample", double_to_o2pressure, &sample->setpoint))
return;
@ -2230,8 +2236,7 @@ int parse_dlf_buffer(unsigned char *buffer, size_t size, struct divelog *log)
state.cur_sample->o2sensor[0].mbar = ( ((ptr[5] << 8) + ptr[4]) * o2_sensor_calibration_values[0]) / 10000;
state.cur_sample->o2sensor[1].mbar = ( ((ptr[7] << 8) + ptr[6]) * o2_sensor_calibration_values[1]) / 10000;
state.cur_sample->o2sensor[2].mbar = ( ((ptr[9] << 8) + ptr[8]) * o2_sensor_calibration_values[2]) / 10000;
// Subsurface only handles 3 o2 sensors.
//state.cur_sample->o2sensor[3].mbar = ( ((ptr[11] << 8) + ptr[10]) * o2_sensor_calibration_values[3]) / 10000;
state.cur_sample->o2sensor[3].mbar = ( ((ptr[11] << 8) + ptr[10]) * o2_sensor_calibration_values[3]) / 10000;
sample_end(&state);
break;
case 4:

View file

@ -476,9 +476,9 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
entry->cns = sample->cns;
if (dc->divemode == CCR || (dc->divemode == PSCR && dc->no_o2sensors)) {
entry->o2pressure.mbar = entry->o2setpoint.mbar = sample->setpoint.mbar; // for rebreathers
entry->o2sensor[0].mbar = sample->o2sensor[0].mbar; // for up to three rebreather O2 sensors
entry->o2sensor[1].mbar = sample->o2sensor[1].mbar;
entry->o2sensor[2].mbar = sample->o2sensor[2].mbar;
int i;
for (i = 0; i < MAX_O2_SENSORS; i++)
entry->o2sensor[i].mbar = sample->o2sensor[i].mbar;
} else {
entry->pressures.o2 = sample->setpoint.mbar / 1000.0;
}
@ -1117,48 +1117,71 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_
unlock_planner();
}
/* Sort the o2 pressure values. There are so few that a simple bubble sort
* will do */
void sort_o2_pressures(int *sensorn, int np, struct plot_data *entry)
{
int smallest, position, old;
for (int i = 0; i < np - 1; i++) {
position = i;
smallest = entry->o2sensor[sensorn[i]].mbar;
for (int j = i+1; j < np; j++)
if (entry->o2sensor[sensorn[j]].mbar < smallest) {
position = j;
smallest = entry->o2sensor[sensorn[j]].mbar;
}
old = sensorn[i];
sensorn[i] = position;
sensorn[position] = old;
}
}
/* Function calculate_ccr_po2: This function takes information from one plot_data structure (i.e. one point on
* the dive profile), containing the oxygen sensor values of a CCR system and, for that plot_data structure,
* calculates the po2 value from the sensor data. Several rules are applied, depending on how many o2 sensors
* there are and the differences among the readings from these sensors.
* calculates the po2 value from the sensor data. If there are at least 3 sensors, sensors are voted out until
* their span is within diff_limit.
*/
static int calculate_ccr_po2(struct plot_data *entry, const struct divecomputer *dc)
{
int sump = 0, minp = 999999, maxp = -999999;
int diff_limit = 100; // The limit beyond which O2 sensor differences are considered significant (default = 100 mbar)
int sump = 0, minp = 0, maxp = 0;
int sensorn[MAX_O2_SENSORS];
int i, np = 0;
for (i = 0; i < dc->no_o2sensors; i++)
for (i = 0; i < dc->no_o2sensors && i < MAX_O2_SENSORS; i++)
if (entry->o2sensor[i].mbar) { // Valid reading
++np;
sensorn[np++] = i;
sump += entry->o2sensor[i].mbar;
minp = MIN(minp, entry->o2sensor[i].mbar);
maxp = MAX(maxp, entry->o2sensor[i].mbar);
}
switch (np) {
case 0: // Uhoh
if (np == 0)
return entry->o2pressure.mbar;
case 1: // Return what we have
return sump;
case 2: // Take the average
return sump / 2;
case 3: // Voting logic
if (2 * maxp - sump + minp < diff_limit) { // Upper difference acceptable...
if (2 * minp - sump + maxp) // ...and lower difference acceptable
return sump / 3;
else
return (sump - minp) / 2;
} else {
if (2 * minp - sump + maxp) // ...but lower difference acceptable
return (sump - maxp) / 2;
else
return sump / 3;
else if (np == 1)
return entry->o2sensor[sensorn[0]].mbar;
maxp = np - 1;
sort_o2_pressures(sensorn, np, entry);
// This is the Shearwater voting logic: If there are still at least three sensors and one
// differs by more than 20% from the closest it is voted out.
while (maxp - minp > 1) {
if (entry->o2sensor[sensorn[minp + 1]].mbar - entry->o2sensor[sensorn[minp]].mbar >
sump / (maxp - minp + 1) / 5) {
sump -= entry->o2sensor[sensorn[minp]].mbar;
++minp;
continue;
}
default: // This should not happen
assert(np <= 3);
return 0;
if (entry->o2sensor[sensorn[maxp]].mbar - entry->o2sensor[sensorn[maxp - 1]].mbar >
sump / (maxp - minp +1) / 5) {
sump -= entry->o2sensor[sensorn[maxp]].mbar;
--maxp;
continue;
}
break;
}
return sump / (maxp - minp + 1);
}
static double gas_density(const struct gas_pressures *pressures)

View file

@ -3,6 +3,7 @@
#define PROFILE_H
#include "dive.h"
#include "sample.h"
#ifdef __cplusplus
extern "C" {
@ -53,7 +54,7 @@ struct plot_data {
int running_sum;
struct gas_pressures pressures;
pressure_t o2pressure; // for rebreathers, this is consensus measured po2, or setpoint otherwise. 0 for OC.
pressure_t o2sensor[3]; //for rebreathers with up to 3 PO2 sensors
pressure_t o2sensor[MAX_O2_SENSORS]; //for rebreathers with several sensors
pressure_t o2setpoint;
pressure_t scr_OC_pO2;
int mod, ead, end, eadd;

View file

@ -9,6 +9,7 @@ extern "C" {
#endif
#define MAX_SENSORS 2
#define MAX_O2_SENSORS 6
#define NO_SENSOR -1
struct sample // BASE TYPE BYTES UNITS RANGE DESCRIPTION
@ -23,7 +24,7 @@ struct sample // BASE TYPE BYTES UNITS RANGE
temperature_t temperature; // uint32_t 4 mK (0-4 MK) ambient temperature
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[3]; // uint16_t 3x2 mbar (0-65 bar) Up to 3 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; // 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; // uint16_t 2 % (0-64k %) cns% accumulated

View file

@ -338,6 +338,21 @@ static void save_sample(struct membuffer *b, struct sample *sample, struct sampl
old->o2sensor[2] = sample->o2sensor[2];
}
if ((sample->o2sensor[3].mbar) && (sample->o2sensor[3].mbar != old->o2sensor[3].mbar)) {
put_milli(b, " sensor4=", sample->o2sensor[3].mbar, "bar");
old->o2sensor[3] = sample->o2sensor[3];
}
if ((sample->o2sensor[4].mbar) && (sample->o2sensor[4].mbar != old->o2sensor[4].mbar)) {
put_milli(b, " sensor5=", sample->o2sensor[4].mbar, "bar");
old->o2sensor[4] = sample->o2sensor[4];
}
if ((sample->o2sensor[5].mbar) && (sample->o2sensor[5].mbar != old->o2sensor[5].mbar)) {
put_milli(b, " sensor6=", sample->o2sensor[5].mbar, "bar");
old->o2sensor[5] = sample->o2sensor[5];
}
if (sample->setpoint.mbar != old->setpoint.mbar) {
put_milli(b, " po2=", sample->setpoint.mbar, "bar");
old->setpoint = sample->setpoint;

View file

@ -71,9 +71,8 @@ static void put_pd(struct membuffer *b, const struct plot_info *pi, int idx)
put_double(b, entry->pressures.n2);
put_double(b, entry->pressures.he);
put_int(b, entry->o2pressure.mbar);
put_int(b, entry->o2sensor[0].mbar);
put_int(b, entry->o2sensor[1].mbar);
put_int(b, entry->o2sensor[2].mbar);
for (int i = 0; i < MAX_O2_SENSORS; i++)
put_int(b, entry->o2sensor[i].mbar);
put_int(b, entry->o2setpoint.mbar);
put_int(b, entry->scr_OC_pO2.mbar);
put_int(b, entry->mod);

View file

@ -328,6 +328,21 @@ static void save_sample(struct membuffer *b, struct sample *sample, struct sampl
old->o2sensor[2] = sample->o2sensor[2];
}
if ((sample->o2sensor[3].mbar) && (sample->o2sensor[3].mbar != old->o2sensor[3].mbar)) {
put_milli(b, " sensor4='", sample->o2sensor[3].mbar, " bar'");
old->o2sensor[3] = sample->o2sensor[3];
}
if ((sample->o2sensor[4].mbar) && (sample->o2sensor[4].mbar != old->o2sensor[4].mbar)) {
put_milli(b, " sensor5='", sample->o2sensor[4].mbar, " bar'");
old->o2sensor[4] = sample->o2sensor[4];
}
if ((sample->o2sensor[5].mbar) && (sample->o2sensor[5].mbar != old->o2sensor[5].mbar)) {
put_milli(b, " sensor6='", sample->o2sensor[5].mbar, " bar'");
old->o2sensor[5] = sample->o2sensor[5];
}
if (sample->setpoint.mbar != old->setpoint.mbar) {
put_milli(b, " po2='", sample->setpoint.mbar, " bar'");
old->setpoint = sample->setpoint;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff