Skip to content

Commit f65ef04

Browse files
authored
Merge pull request #81 from mikeller/add_divecan_calibration_guessing
2 parents e8efe9b + 8c52ffd commit f65ef04

File tree

1 file changed

+121
-19
lines changed

1 file changed

+121
-19
lines changed

src/shearwater_predator_parser.c

Lines changed: 121 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;
@@ -159,6 +163,12 @@ struct shearwater_predator_parser_t {
159163
struct dc_field_cache cache;
160164
};
161165

166+
struct dc_parser_sensor_calibration_t {
167+
double sum_ppo2;
168+
double sum_calculated_ppo2;
169+
unsigned int ppo2_sample_count;
170+
};
171+
162172
static dc_status_t shearwater_predator_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
163173
static dc_status_t shearwater_predator_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
164174
static dc_status_t shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@@ -280,6 +290,7 @@ shearwater_common_parser_create (dc_parser_t **out, dc_context_t *context, const
280290
for (unsigned int i = 0; i < 3; ++i) {
281291
parser->calibration[i] = 0.0;
282292
}
293+
parser->needs_divecan_calibration_estimate = false;
283294
parser->units = METRIC;
284295
parser->density = DEF_DENSITY_SALT;
285296
parser->atmospheric = DEF_ATMOSPHERIC / (BAR / 1000);
@@ -412,6 +423,20 @@ add_battery_type(shearwater_predator_parser_t *parser, const unsigned char *data
412423
}
413424
}
414425

426+
static void print_calibration(shearwater_predator_parser_t *parser)
427+
{
428+
for (size_t i = 0; i < 3; ++i) {
429+
if (parser->calibrated & (1 << i)) {
430+
static const char *name[] = {
431+
"Sensor 1 calibration [bar / V]",
432+
"Sensor 2 calibration [bar / V]",
433+
"Sensor 3 calibration [bar / V]",
434+
};
435+
dc_field_add_string_fmt(&parser->cache, name[i], "%.2f", parser->calibration[i] * 1000);
436+
}
437+
}
438+
}
439+
415440
static dc_status_t
416441
shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
417442
{
@@ -748,9 +773,17 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
748773
unsigned int base = parser->opening[3] + (pnf ? 6 : 86);
749774
parser->calibrated = data[base];
750775

776+
unsigned int calibration_count = 0;
777+
unsigned int calibration_default_count = 0;
751778
for (size_t i = 0; i < 3; ++i) {
752779
if (parser->calibrated & (1 << i)) {
753780
unsigned int calibration = array_uint16_be(data + base + 1 + i * 2);
781+
782+
calibration_count++;
783+
if (calibration == SENSOR_CALIBRATION_DEFAULT) {
784+
calibration_default_count++;
785+
}
786+
754787
parser->calibration[i] = calibration / 100000.0;
755788
if (parser->model == PREDATOR) {
756789
// The Predator expects the mV output of the cells to be
@@ -759,14 +792,17 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
759792
// sensors lines up and matches the average.
760793
parser->calibration[i] *= 2.2;
761794
}
795+
}
796+
}
762797

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-
798+
if (calibration_count > 0) {
799+
if (calibration_default_count < calibration_count) {
800+
print_calibration(parser);
801+
} else {
802+
// All calibrated sensors report the default calibration value
803+
// so this could be a DiveCAN controller, where the calibration values
804+
// are stored in the CCR's sensor module.
805+
parser->needs_divecan_calibration_estimate = true;
770806
}
771807
}
772808

@@ -858,6 +894,35 @@ shearwater_predator_parser_cache (shearwater_predator_parser_t *parser)
858894
break;
859895
}
860896

897+
dc_status_t rc = DC_STATUS_SUCCESS;
898+
if (parser->needs_divecan_calibration_estimate) {
899+
struct dc_parser_sensor_calibration_t data = { 0 };
900+
901+
rc = shearwater_predator_parser_samples_foreach(abstract, NULL, (void *)&data);
902+
903+
bool calibrated = false;
904+
if (data.sum_ppo2 != 0) {
905+
double calibration = data.sum_calculated_ppo2 / data.sum_ppo2;
906+
if (calibration < 0.98 || calibration > 1.02) {
907+
// The calibration scaling is significant, use it.
908+
calibration *= SENSOR_CALIBRATION_DEFAULT / 100000.0;
909+
parser->calibration[0] = calibration;
910+
parser->calibration[1] = calibration;
911+
parser->calibration[2] = calibration;
912+
913+
dc_field_add_string_fmt(&parser->cache, "Estimated (DiveCAN?) sensor calibration [bar / V]", "%.2f", calibration * 1000);
914+
915+
calibrated = true;
916+
}
917+
}
918+
919+
if (!calibrated) {
920+
print_calibration(parser);
921+
}
922+
923+
parser->needs_divecan_calibration_estimate = false;
924+
}
925+
861926
return DC_STATUS_SUCCESS;
862927
}
863928

@@ -1041,21 +1106,58 @@ shearwater_predator_parser_samples_foreach (dc_parser_t *abstract, dc_sample_cal
10411106
if (ccr) {
10421107
// PPO2
10431108
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);
1109+
double calculated_ppo2 = data[offset + pnf + 6] / 100.0;
10471110

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);
1111+
if (parser->needs_divecan_calibration_estimate) {
1112+
struct dc_parser_sensor_calibration_t *out = (struct dc_parser_sensor_calibration_t *)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+
double ppo2_sum = 0.0;
1115+
unsigned int ppo2_count = 0;
1116+
if (parser->calibrated & 0x01) {
1117+
ppo2_sum += data[offset + pnf + 12] * SENSOR_CALIBRATION_DEFAULT / 100000.0;
1118+
ppo2_count++;
1119+
}
10551120

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);
1121+
if (parser->calibrated & 0x02) {
1122+
ppo2_sum += data[offset + pnf + 14] * SENSOR_CALIBRATION_DEFAULT / 100000.0;
1123+
ppo2_count++;
1124+
}
1125+
1126+
if (parser->calibrated & 0x04) {
1127+
ppo2_sum += data[offset + pnf + 15] * SENSOR_CALIBRATION_DEFAULT / 100000.0;
1128+
ppo2_count++;
1129+
}
1130+
1131+
double ppo2 = ppo2_sum / ppo2_count;
1132+
1133+
out->sum_ppo2 += ppo2;
1134+
out->sum_calculated_ppo2 += calculated_ppo2;
1135+
out->ppo2_sample_count++;
1136+
}
1137+
1138+
if (callback) {
1139+
sample.ppo2.sensor = DC_SENSOR_NONE;
1140+
sample.ppo2.value = calculated_ppo2;
1141+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1142+
1143+
if (parser->calibrated & 0x01) {
1144+
sample.ppo2.sensor = 0;
1145+
sample.ppo2.value = data[offset + pnf + 12] * parser->calibration[0];
1146+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1147+
}
1148+
1149+
if (parser->calibrated & 0x02) {
1150+
sample.ppo2.sensor = 1;
1151+
sample.ppo2.value = data[offset + pnf + 14] * parser->calibration[1];
1152+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1153+
}
1154+
1155+
if (parser->calibrated & 0x04) {
1156+
sample.ppo2.sensor = 2;
1157+
sample.ppo2.value = data[offset + pnf + 15] * parser->calibration[2];
1158+
callback(DC_SAMPLE_PPO2, &sample, userdata);
1159+
}
1160+
}
10591161
}
10601162

10611163
// Setpoint

0 commit comments

Comments
 (0)