2121
2222#include <stdlib.h>
2323#include <string.h>
24+ #include <stdbool.h>
2425
2526#include <libdivecomputer/units.h>
2627
101102#define PETREL 3
102103#define TERIC 8
103104
105+ #define SENSOR_CALIBRATION_DEFAULT 2100
106+
104107#define UNDEFINED 0xFFFFFFFF
105108
106109typedef 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+
162172static dc_status_t shearwater_predator_parser_get_datetime (dc_parser_t * abstract , dc_datetime_t * datetime );
163173static dc_status_t shearwater_predator_parser_get_field (dc_parser_t * abstract , dc_field_type_t type , unsigned int flags , void * value );
164174static 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+
415440static dc_status_t
416441shearwater_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