Skip to content

Commit 482b546

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: Michael Keller <[email protected]>
1 parent e8efe9b commit 482b546

File tree

1 file changed

+92
-19
lines changed

1 file changed

+92
-19
lines changed

src/shearwater_predator_parser.c

Lines changed: 92 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

@@ -858,6 +883,10 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
858883
break;
859884
}
860885

886+
if (parser->needs_divecan_calibration_estimate) {
887+
return shearwater_predator_parser_samples_foreach(abstract, NULL, NULL);
888+
}
889+
861890
return DC_STATUS_SUCCESS;
862891
}
863892

@@ -1041,21 +1070,65 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
10411070
if (ccr) {
10421071
// PPO2
10431072
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);
1073+
double calculated_ppo2 = data[offset + pnf + 6] / 100.0;
1074+
1075+
if (parser->needs_divecan_calibration_estimate) {
1076+
double ppo2_sum = 0.0;
1077+
unsigned int ppo2_count = 0;
1078+
if (parser->calibrated & 0x01) {
1079+
ppo2_sum += data[offset + pnf + 12] * SENSOR_CALIBRATION_DEFAULT;
1080+
ppo2_count++;
1081+
}
1082+
1083+
if (parser->calibrated & 0x02) {
1084+
ppo2_sum += data[offset + pnf + 14] * SENSOR_CALIBRATION_DEFAULT;
1085+
ppo2_count++;
1086+
}
1087+
1088+
if (parser->calibrated & 0x04) {
1089+
ppo2_sum += data[offset + pnf + 15] * SENSOR_CALIBRATION_DEFAULT;
1090+
ppo2_count++;
1091+
}
1092+
1093+
double calibration = SENSOR_CALIBRATION_DEFAULT;
1094+
double calibration_scaling_factor = calculated_ppo2 / (ppo2_sum / ppo2_count);
1095+
if (calibration_scaling_factor < 0.95 || calibration_scaling_factor > 1.05) {
1096+
// The calibration scaling is significant, use it.
1097+
calibration *= calibration_scaling_factor;
1098+
}
1099+
1100+
parser->calibration[0] = calibration;
1101+
parser->calibration[1] = calibration;
1102+
parser->calibration[2] = calibration;
1103+
1104+
dc_field_add_string_fmt(&parser->cache, "Estimated DiveCAN calibration [bar / V]", "%.2f", calibration * 1000);
10471105

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);
1106+
parser->needs_divecan_calibration_estimate = false;
1107+
}
1108+
1109+
if (callback) {
1110+
sample.ppo2.sensor = DC_SENSOR_NONE;
1111+
sample.ppo2.value = calculated_ppo2;
1112+
callback(DC_SAMPLE_PPO2, &sample, userdata);
10511113

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);
1114+
if (parser->calibrated & 0x01) {
1115+
sample.ppo2.sensor = 0;
1116+
sample.ppo2.value = data[offset + pnf + 12] * parser->calibration[0];
1117+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1118+
}
10551119

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);
1120+
if (parser->calibrated & 0x02) {
1121+
sample.ppo2.sensor = 1;
1122+
sample.ppo2.value = data[offset + pnf + 14] * parser->calibration[1];
1123+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1124+
}
1125+
1126+
if (parser->calibrated & 0x04) {
1127+
sample.ppo2.sensor = 2;
1128+
sample.ppo2.value = data[offset + pnf + 15] * parser->calibration[2];
1129+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1130+
}
1131+
}
10591132
}
10601133

10611134
// Setpoint

0 commit comments

Comments
 (0)