Skip to content

Commit 1f08de8

Browse files
committed
Shearwater: Add guessing of the calibration for DiveCAN controllers.
If the Shearwater default calibration factor (2100) is returned for the ppO2 calibration factor, we know that the dive computer might be a DiveCAN controller, where the calibration is not available on the dive computer. In this case, calculate the scaling factor that is required to make the averaged sensor reading match the dive computer reported ppO2, and use this instead of the returned calibration factor. This will give us: - individual sensor graphs that show the sensor behaviour, and have the approximately correct ppO2 reading; - a calculated ppO2 used by Subsurface that is close to what the dive computer would have used, without the smoothing seen in the calculated ppO2 returned by the dive computer. Signed-off-by: Subsurface CI <mikeller@042.ch>
1 parent e8efe9b commit 1f08de8

File tree

1 file changed

+88
-19
lines changed

1 file changed

+88
-19
lines changed

src/shearwater_predator_parser.c

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include <stdlib.h>
2323
#include <string.h>
24+
#include <stdbool.h>
2425

2526
#include <libdivecomputer/units.h>
2627

@@ -101,6 +102,8 @@
101102
#define PETREL 3
102103
#define TERIC 8
103104

105+
#define SENSOR_CALIBRATION_DEFAULT 2100
106+
104107
#define UNDEFINED 0xFFFFFFFF
105108

106109
typedef struct shearwater_predator_parser_t shearwater_predator_parser_t;
@@ -149,6 +152,7 @@ struct shearwater_predator_parser_t {
149152
unsigned int hpccr;
150153
unsigned int calibrated;
151154
double calibration[3];
155+
bool needs_divecan_calibration_estimate;
152156
unsigned int divemode;
153157
unsigned int serial;
154158
unsigned int units;
@@ -280,6 +284,7 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, const
280284
for (unsigned int i = 0; i < 3; ++i) {
281285
parser->calibration[i] = 0.0;
282286
}
287+
parser->needs_divecan_calibration_estimate = false;
283288
parser->units = METRIC;
284289
parser->density = DEF_DENSITY_SALT;
285290
parser->atmospheric = DEF_ATMOSPHERIC / (BAR / 1000);
@@ -748,9 +753,17 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
748753
unsigned int base = parser->opening[3] + (pnf ? 6 : 86);
749754
parser->calibrated = data[base];
750755

756+
unsigned int calibration_count = 0;
757+
unsigned int calibration_default_count = 0;
751758
for (size_t i = 0; i < 3; ++i) {
752759
if (parser->calibrated & (1 << i)) {
753760
unsigned int calibration = array_uint16_be(data + base + 1 + i * 2);
761+
762+
calibration_count++;
763+
if (calibration == SENSOR_CALIBRATION_DEFAULT) {
764+
calibration_default_count++;
765+
}
766+
754767
parser->calibration[i] = calibration / 100000.0;
755768
if (parser->model == PREDATOR) {
756769
// The Predator expects the mV output of the cells to be
@@ -759,14 +772,26 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
759772
// sensors lines up and matches the average.
760773
parser->calibration[i] *= 2.2;
761774
}
775+
}
776+
}
762777

763-
static const char *name[] = {
764-
"Sensor 1 calibration [bar / V]",
765-
"Sensor 2 calibration [bar / V]",
766-
"Sensor 3 calibration [bar / V]",
767-
};
768-
dc_field_add_string_fmt(&parser->cache, name[i], "%.2f", parser->calibration[i] * 1000);
769-
778+
if (calibration_count > 0) {
779+
if (calibration_default_count < calibration_count) {
780+
for (size_t i = 0; i < 3; ++i) {
781+
if (parser->calibrated & (1 << i)) {
782+
static const char *name[] = {
783+
"Sensor 1 calibration [bar / V]",
784+
"Sensor 2 calibration [bar / V]",
785+
"Sensor 3 calibration [bar / V]",
786+
};
787+
dc_field_add_string_fmt(&parser->cache, name[i], "%.2f", parser->calibration[i] * 1000);
788+
}
789+
}
790+
} else {
791+
// All calibrated sensors report the default calibration value
792+
// so this could be a DiveCAN controller, where the calibration values
793+
// are stored in the CCR's sensor module.
794+
parser->needs_divecan_calibration_estimate = true;
770795
}
771796
}
772797

@@ -1041,21 +1066,65 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
10411066
if (ccr) {
10421067
// PPO2
10431068
if ((status & PPO2_EXTERNAL) == 0) {
1044-
sample.ppo2.sensor = DC_SENSOR_NONE;
1045-
sample.ppo2.value = data[offset + pnf + 6] / 100.0;
1046-
if (callback) callback (DC_SAMPLE_PPO2, &sample, userdata);
1069+
double calculated_ppo2 = data[offset + pnf + 6] / 100.0;
1070+
1071+
if (parser->needs_divecan_calibration_estimate) {
1072+
double ppo2_sum = 0.0;
1073+
unsigned int ppo2_count = 0;
1074+
if (parser->calibrated & 0x01) {
1075+
ppo2_sum += data[offset + pnf + 12] * SENSOR_CALIBRATION_DEFAULT;
1076+
ppo2_count++;
1077+
}
1078+
1079+
if (parser->calibrated & 0x02) {
1080+
ppo2_sum += data[offset + pnf + 14] * SENSOR_CALIBRATION_DEFAULT;
1081+
ppo2_count++;
1082+
}
10471083

1048-
sample.ppo2.sensor = 0;
1049-
sample.ppo2.value = data[offset + pnf + 12] * parser->calibration[0];
1050-
if (callback && (parser->calibrated & 0x01)) callback (DC_SAMPLE_PPO2, &sample, userdata);
1084+
if (parser->calibrated & 0x04) {
1085+
ppo2_sum += data[offset + pnf + 15] * SENSOR_CALIBRATION_DEFAULT;
1086+
ppo2_count++;
1087+
}
1088+
1089+
double calibration = SENSOR_CALIBRATION_DEFAULT;
1090+
double calibration_scaling_factor = calculated_ppo2 / (ppo2_sum / ppo2_count);
1091+
if (calibration_scaling_factor < 0.95 || calibration_scaling_factor > 1.05) {
1092+
// The calibration scaling is significant, use it.
1093+
calibration *= calibration_scaling_factor;
1094+
}
1095+
1096+
parser->calibration[0] = calibration;
1097+
parser->calibration[1] = calibration;
1098+
parser->calibration[2] = calibration;
1099+
1100+
dc_field_add_string_fmt(&parser->cache, "Estimated DiveCAN calibration [bar / V]", "%.2f", calibration * 1000);
1101+
1102+
parser->needs_divecan_calibration_estimate = false;
1103+
}
10511104

1052-
sample.ppo2.sensor = 1;
1053-
sample.ppo2.value = data[offset + pnf + 14] * parser->calibration[1];
1054-
if (callback && (parser->calibrated & 0x02)) callback (DC_SAMPLE_PPO2, &sample, userdata);
1105+
if (callback) {
1106+
sample.ppo2.sensor = DC_SENSOR_NONE;
1107+
sample.ppo2.value = calculated_ppo2;
1108+
callback(DC_SAMPLE_PPO2, &sample, userdata);
10551109

1056-
sample.ppo2.sensor = 2;
1057-
sample.ppo2.value = data[offset + pnf + 15] * parser->calibration[2];
1058-
if (callback && (parser->calibrated & 0x04)) callback (DC_SAMPLE_PPO2, &sample, userdata);
1110+
if (parser->calibrated & 0x01) {
1111+
sample.ppo2.sensor = 0;
1112+
sample.ppo2.value = data[offset + pnf + 12] * parser->calibration[0];
1113+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1114+
}
1115+
1116+
if (parser->calibrated & 0x02) {
1117+
sample.ppo2.sensor = 1;
1118+
sample.ppo2.value = data[offset + pnf + 14] * parser->calibration[1];
1119+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1120+
}
1121+
1122+
if (parser->calibrated & 0x04) {
1123+
sample.ppo2.sensor = 2;
1124+
sample.ppo2.value = data[offset + pnf + 15] * parser->calibration[2];
1125+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1126+
}
1127+
}
10591128
}
10601129

10611130
// Setpoint

0 commit comments

Comments
 (0)