Deepblu Cosmiq+: add basic dive information parsing
This at least approximates downloading a dive from the Deepblu Cosmiq+, and gives reasonable profiles for my test-dives (in a dive computer chamber, not real dives). I'm sure there's a lot to be improved here, and this literally only gets depth and water temperature, but that seems to be what the Cosmiq+ gives us. Lots of credit to Michael Werle for bluetooth packet captures, and some basic analysis of the protocol. Packet-logging-by: Michael Werle <mwerle@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
2efa83eeee
commit
eacfe4011a
@ -340,6 +340,9 @@ deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[
|
||||
{
|
||||
deepblu_device_t *device = (deepblu_device_t *)abstract;
|
||||
|
||||
ERROR (device->base.context, "Deepblu Cosmiq+ set_fingerprint called");
|
||||
HEXDUMP(device->base.context, DC_LOGLEVEL_DEBUG, "set_fingerprint", data, size);
|
||||
|
||||
if (size && size != sizeof (device->fingerprint))
|
||||
return DC_STATUS_INVALIDARGS;
|
||||
|
||||
@ -348,7 +351,6 @@ deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[
|
||||
else
|
||||
memset (device->fingerprint, 0, sizeof (device->fingerprint));
|
||||
|
||||
ERROR (device->base.context, "Deepblu Cosmiq+ set_fingerprint called");
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -410,24 +412,28 @@ deepblu_download_dive(deepblu_device_t *device, unsigned char nr, dc_dive_callba
|
||||
status = deepblu_recv_bulk(device, RSP_DIVESTAT, header, header_len);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
memset(header + header_len, 0, 256 - header_len);
|
||||
|
||||
status = deepblu_send_recv(device, CMD_GETPROFILE, &nr, 1, profilebytes, sizeof(profilebytes));
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
profile_len = (profilebytes[0] << 8) | profilebytes[1];
|
||||
|
||||
profile = malloc(profile_len);
|
||||
profile = malloc(256 + profile_len);
|
||||
if (!profile) {
|
||||
ERROR (device->base.context, "Insufficient buffer space available.");
|
||||
return DC_STATUS_NOMEMORY;
|
||||
}
|
||||
|
||||
status = deepblu_recv_bulk(device, RSP_DIVEPROF, profile, profile_len);
|
||||
// We make the dive data be 256 bytes of header, followed by the profile data
|
||||
memcpy(profile, header, 256);
|
||||
|
||||
status = deepblu_recv_bulk(device, RSP_DIVEPROF, profile+256, profile_len);
|
||||
if (status != DC_STATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
if (callback)
|
||||
callback(profile, profile_len, header, header_len, userdata);
|
||||
callback(profile, profile_len+256, header, header_len, userdata);
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -144,26 +144,89 @@ static void add_string_fmt(deepblu_parser_t *deepblu, const char *desc, const ch
|
||||
add_string(deepblu, desc, buffer);
|
||||
}
|
||||
|
||||
|
||||
static double
|
||||
pressure_to_depth(unsigned int mbar)
|
||||
{
|
||||
// Specific weight of seawater (millibar to cm)
|
||||
const double specific_weight = 1.024 * 0.980665;
|
||||
|
||||
// Absolute pressure, subtract surface pressure
|
||||
if (mbar < 1013)
|
||||
return 0.0;
|
||||
mbar -= 1013;
|
||||
return mbar / specific_weight / 100.0;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
|
||||
{
|
||||
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||
const unsigned char *hdr = data;
|
||||
const unsigned char *profile = data + 256;
|
||||
unsigned int divetime, maxpressure;
|
||||
|
||||
if (size < 256)
|
||||
return DC_STATUS_IO;
|
||||
|
||||
divetime = hdr[12] + 256*hdr[13]; // Divetime in minutes
|
||||
maxpressure = hdr[22] + 256*hdr[23]; // Maxpressure in millibar
|
||||
|
||||
deepblu->callback = NULL;
|
||||
deepblu->userdata = NULL;
|
||||
memset(&deepblu->cache, 0, sizeof(deepblu->cache));
|
||||
|
||||
ASSIGN_FIELD(DIVETIME, 60*divetime);
|
||||
ASSIGN_FIELD(MAXDEPTH, pressure_to_depth(maxpressure));
|
||||
|
||||
ERROR (abstract->context, "Deepblu Cosmiq+ parser_set_data() called");
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// The layout of the header in the 'data' is
|
||||
// 0: LE16 dive number
|
||||
// 2: dive type byte?
|
||||
// 3: O2 percentage byte
|
||||
// 4: unknown
|
||||
// 5: unknown
|
||||
// 6: LE16 year
|
||||
// 8: day of month
|
||||
// 9: month
|
||||
// 10: minute
|
||||
// 11: hour
|
||||
// 12: LE16 dive time
|
||||
// 14: LE16 ??
|
||||
// 16: LE16 surface pressure?
|
||||
// 18: LE16 ??
|
||||
// 20: LE16 ??
|
||||
// 22: LE16 max depth pressure
|
||||
// 24: LE16 water temp
|
||||
// 26: LE16 ??
|
||||
// 28: LE16 ??
|
||||
// 30: LE16 ??
|
||||
// 32: LE16 ??
|
||||
// 34: LE16 ??
|
||||
static dc_status_t
|
||||
deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
|
||||
{
|
||||
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||
const unsigned char *data = deepblu->base.data;
|
||||
int len = deepblu->base.size;
|
||||
|
||||
if (len < 256)
|
||||
return DC_STATUS_IO;
|
||||
datetime->year = data[6] + (data[7] << 8);
|
||||
datetime->day = data[8];
|
||||
datetime->month = data[9];
|
||||
datetime->minute = data[10];
|
||||
datetime->hour = data[11];
|
||||
datetime->second = 0;
|
||||
datetime->timezone = DC_TIMEZONE_NONE;
|
||||
|
||||
HEXDUMP(abstract->context, DC_LOGLEVEL_DEBUG, "get_datetile", data, len);
|
||||
|
||||
ERROR (abstract->context, "Deepblu Cosmiq+ parser_get_datetime() called");
|
||||
return DC_STATUS_UNSUPPORTED;
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t get_string_field(dc_field_string_t *strings, unsigned idx, dc_field_string_t *value)
|
||||
@ -231,10 +294,35 @@ static dc_status_t
|
||||
deepblu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract;
|
||||
const unsigned char *data = deepblu->base.data;
|
||||
int len = deepblu->base.size, i;
|
||||
|
||||
deepblu->callback = callback;
|
||||
deepblu->userdata = userdata;
|
||||
|
||||
// Skip the header information
|
||||
if (len < 256)
|
||||
return DC_STATUS_IO;
|
||||
data += 256;
|
||||
len -= 256;
|
||||
|
||||
// The rest should be samples every 20s with temperature and depth
|
||||
for (i = 0; i < len/4; i++) {
|
||||
dc_sample_value_t sample = {0};
|
||||
unsigned int temp = data[0]+256*data[1];
|
||||
unsigned int pressure = data[2]+256*data[3];
|
||||
|
||||
data += 4;
|
||||
sample.time = i*20;
|
||||
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
|
||||
|
||||
sample.depth = pressure_to_depth(pressure);
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
|
||||
|
||||
sample.temperature = temp / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
|
||||
}
|
||||
|
||||
ERROR (abstract->context, "Deepblu Cosmiq+ samples_foreach() called");
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user