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 desktop: add divemode as a possible dive list column
profile-widget: Now zomed in profiles can be panned with horizontal scroll. 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 desktop: hide only events with the same severity when 'Hide similar events' is used

View file

@ -1184,15 +1184,12 @@ static void fixup_no_o2sensors(struct divecomputer *dc)
return; return;
for (int i = 0; i < dc->samples; i++) { for (int i = 0; i < dc->samples; i++) {
int nsensor = 0; int nsensor = 0, j;
struct sample *s = dc->sample + i; struct sample *s = dc->sample + i;
// How many o2 sensors can we find in this sample? // How many o2 sensors can we find in this sample?
if (s->o2sensor[0].mbar) for (j = 0; j < MAX_O2_SENSORS; j++)
nsensor++; if (s->o2sensor[j].mbar)
if (s->o2sensor[1].mbar)
nsensor++;
if (s->o2sensor[2].mbar)
nsensor++; nsensor++;
// If we fond more than the previous found max, record it. // If we fond more than the previous found max, record it.
@ -1200,7 +1197,7 @@ static void fixup_no_o2sensors(struct divecomputer *dc)
dc->no_o2sensors = nsensor; dc->no_o2sensors = nsensor;
// Already found the maximum posible amount. // Already found the maximum posible amount.
if (nsensor == 3) if (nsensor == MAX_O2_SENSORS)
return; 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); sample->setpoint.mbar = po2 = lrint(value.setpoint * 1000);
break; break;
case DC_SAMPLE_PPO2: case DC_SAMPLE_PPO2:
if (nsensor < 3) if (nsensor < MAX_O2_SENSORS)
sample->o2sensor[nsensor].mbar = lrint(value.ppo2 * 1000); sample->o2sensor[nsensor].mbar = lrint(value.ppo2 * 1000);
else else
report_error("%d is more o2 sensors than we can handle", nsensor); 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; sample->o2sensor[2].mbar = p.mbar;
return; 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")) { if (!strcmp(key, "o2pressure")) {
pressure_t p = get_pressure(value); pressure_t p = get_pressure(value);
sample->pressure[1].mbar = p.mbar; 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; return;
if (MATCH("sensor2.sample", double_to_o2pressure, &sample->o2sensor[1])) if (MATCH("sensor2.sample", double_to_o2pressure, &sample->o2sensor[1]))
return; 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; return;
if (MATCH("po2.sample", double_to_o2pressure, &sample->setpoint)) if (MATCH("po2.sample", double_to_o2pressure, &sample->setpoint))
return; 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[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[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; 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); sample_end(&state);
break; break;
case 4: case 4:

View file

@ -476,9 +476,9 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
entry->cns = sample->cns; entry->cns = sample->cns;
if (dc->divemode == CCR || (dc->divemode == PSCR && dc->no_o2sensors)) { if (dc->divemode == CCR || (dc->divemode == PSCR && dc->no_o2sensors)) {
entry->o2pressure.mbar = entry->o2setpoint.mbar = sample->setpoint.mbar; // for rebreathers 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 int i;
entry->o2sensor[1].mbar = sample->o2sensor[1].mbar; for (i = 0; i < MAX_O2_SENSORS; i++)
entry->o2sensor[2].mbar = sample->o2sensor[2].mbar; entry->o2sensor[i].mbar = sample->o2sensor[i].mbar;
} else { } else {
entry->pressures.o2 = sample->setpoint.mbar / 1000.0; 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(); 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 /* 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, * 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 * calculates the po2 value from the sensor data. If there are at least 3 sensors, sensors are voted out until
* there are and the differences among the readings from these sensors. * their span is within diff_limit.
*/ */
static int calculate_ccr_po2(struct plot_data *entry, const struct divecomputer *dc) static int calculate_ccr_po2(struct plot_data *entry, const struct divecomputer *dc)
{ {
int sump = 0, minp = 999999, maxp = -999999; int sump = 0, minp = 0, maxp = 0;
int diff_limit = 100; // The limit beyond which O2 sensor differences are considered significant (default = 100 mbar) int sensorn[MAX_O2_SENSORS];
int i, np = 0; 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 if (entry->o2sensor[i].mbar) { // Valid reading
++np; sensorn[np++] = i;
sump += entry->o2sensor[i].mbar; sump += entry->o2sensor[i].mbar;
minp = MIN(minp, entry->o2sensor[i].mbar);
maxp = MAX(maxp, entry->o2sensor[i].mbar);
} }
switch (np) { if (np == 0)
case 0: // Uhoh
return entry->o2pressure.mbar; return entry->o2pressure.mbar;
case 1: // Return what we have else if (np == 1)
return sump; return entry->o2sensor[sensorn[0]].mbar;
case 2: // Take the average
return sump / 2; maxp = np - 1;
case 3: // Voting logic sort_o2_pressures(sensorn, np, entry);
if (2 * maxp - sump + minp < diff_limit) { // Upper difference acceptable...
if (2 * minp - sump + maxp) // ...and lower difference acceptable // This is the Shearwater voting logic: If there are still at least three sensors and one
return sump / 3; // differs by more than 20% from the closest it is voted out.
else while (maxp - minp > 1) {
return (sump - minp) / 2; if (entry->o2sensor[sensorn[minp + 1]].mbar - entry->o2sensor[sensorn[minp]].mbar >
} else { sump / (maxp - minp + 1) / 5) {
if (2 * minp - sump + maxp) // ...but lower difference acceptable sump -= entry->o2sensor[sensorn[minp]].mbar;
return (sump - maxp) / 2; ++minp;
else continue;
return sump / 3;
} }
default: // This should not happen if (entry->o2sensor[sensorn[maxp]].mbar - entry->o2sensor[sensorn[maxp - 1]].mbar >
assert(np <= 3); sump / (maxp - minp +1) / 5) {
return 0; sump -= entry->o2sensor[sensorn[maxp]].mbar;
--maxp;
continue;
} }
break;
}
return sump / (maxp - minp + 1);
} }
static double gas_density(const struct gas_pressures *pressures) static double gas_density(const struct gas_pressures *pressures)

View file

@ -3,6 +3,7 @@
#define PROFILE_H #define PROFILE_H
#include "dive.h" #include "dive.h"
#include "sample.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -53,7 +54,7 @@ struct plot_data {
int running_sum; int running_sum;
struct gas_pressures pressures; struct gas_pressures pressures;
pressure_t o2pressure; // for rebreathers, this is consensus measured po2, or setpoint otherwise. 0 for OC. 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 o2setpoint;
pressure_t scr_OC_pO2; pressure_t scr_OC_pO2;
int mod, ead, end, eadd; int mod, ead, end, eadd;

View file

@ -9,6 +9,7 @@ extern "C" {
#endif #endif
#define MAX_SENSORS 2 #define MAX_SENSORS 2
#define MAX_O2_SENSORS 6
#define NO_SENSOR -1 #define NO_SENSOR -1
struct sample // BASE TYPE BYTES UNITS RANGE DESCRIPTION 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 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) 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[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 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 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 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]; 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) { if (sample->setpoint.mbar != old->setpoint.mbar) {
put_milli(b, " po2=", sample->setpoint.mbar, "bar"); put_milli(b, " po2=", sample->setpoint.mbar, "bar");
old->setpoint = sample->setpoint; 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.n2);
put_double(b, entry->pressures.he); put_double(b, entry->pressures.he);
put_int(b, entry->o2pressure.mbar); put_int(b, entry->o2pressure.mbar);
put_int(b, entry->o2sensor[0].mbar); for (int i = 0; i < MAX_O2_SENSORS; i++)
put_int(b, entry->o2sensor[1].mbar); put_int(b, entry->o2sensor[i].mbar);
put_int(b, entry->o2sensor[2].mbar);
put_int(b, entry->o2setpoint.mbar); put_int(b, entry->o2setpoint.mbar);
put_int(b, entry->scr_OC_pO2.mbar); put_int(b, entry->scr_OC_pO2.mbar);
put_int(b, entry->mod); 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]; 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) { if (sample->setpoint.mbar != old->setpoint.mbar) {
put_milli(b, " po2='", sample->setpoint.mbar, " bar'"); put_milli(b, " po2='", sample->setpoint.mbar, " bar'");
old->setpoint = sample->setpoint; old->setpoint = sample->setpoint;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff