int libambit_pmem20_log_next_header(ambit_object_t *object, ambit_log_header_t *log_header) { int ret = -1; size_t buffer_offset; uint16_t tmp_len; LOG_INFO("Reading header of next log entry"); if (!object->pmem20.log.initialized) { LOG_ERROR("Trying to get next log without initialization"); return -1; } // Check if we reached end of entries if (object->pmem20.log.current.current == object->pmem20.log.current.next) { LOG_INFO("No more entries to read"); return 0; } if (read_upto(object, object->pmem20.log.current.next, PMEM20_LOG_HEADER_MIN_LEN) == 0) { buffer_offset = (object->pmem20.log.current.next - PMEM20_LOG_START); // First check that header seems to be correctly present if (strncmp((char*)object->pmem20.log.buffer + buffer_offset, "PMEM", 4) == 0) { object->pmem20.log.current.current = object->pmem20.log.current.next; buffer_offset += 4; object->pmem20.log.current.next = read32inc(object->pmem20.log.buffer, &buffer_offset); object->pmem20.log.current.prev = read32inc(object->pmem20.log.buffer, &buffer_offset); tmp_len = read16inc(object->pmem20.log.buffer, &buffer_offset); buffer_offset += tmp_len; tmp_len = read16inc(object->pmem20.log.buffer, &buffer_offset); if (libambit_pmem20_log_parse_header(object->pmem20.log.buffer + buffer_offset, tmp_len, log_header) == 0) { LOG_INFO("Log entry header parsed"); ret = 1; } else { LOG_ERROR("Failed to parse log entry header correctly"); } } else { LOG_ERROR("Failed to find valid log entry header start"); } } else { LOG_WARNING("Failed to read log entry header"); } // Unset initialized of something went wrong if (ret < 0) { object->pmem20.log.initialized = false; } return ret; }
static int parse_log_header(const uint8_t *data, ambit3_log_header_t *log_header) { struct tm tm; char *ptr; size_t offset = 0; // Start with parsing the time if ((ptr = libambit_strptime((const char *)data, "%Y-%m-%dT%H:%M:%S", &tm)) == NULL) { return -1; } log_header->header.date_time.year = 1900 + tm.tm_year; log_header->header.date_time.month = tm.tm_mon + 1; log_header->header.date_time.day = tm.tm_mday; log_header->header.date_time.hour = tm.tm_hour; log_header->header.date_time.minute = tm.tm_min; log_header->header.date_time.msec = tm.tm_sec*1000; offset += (size_t)ptr - (size_t)data + 1; log_header->synced = read8inc(data, &offset); log_header->address = read32inc(data, &offset); log_header->end_address = read32inc(data, &offset); offset += 8; // Unknown bytes log_header->header.heartrate_min = read8inc(data, &offset); log_header->header.heartrate_avg = read8inc(data, &offset); log_header->header.heartrate_max = read8inc(data, &offset); log_header->header.heartrate_max_time = read32inc(data, &offset); log_header->header.heartrate_min_time = read32inc(data, &offset); // temperature format is messed up, 1 byte is missing, just skip for now log_header->header.temperature_min = 0; log_header->header.temperature_max = 0; offset += 2; log_header->header.temperature_min_time = read32inc(data, &offset); log_header->header.temperature_max_time = read32inc(data, &offset); log_header->header.altitude_min = read16inc(data, &offset); log_header->header.altitude_max = read16inc(data, &offset); log_header->header.altitude_min_time = read32inc(data, &offset); log_header->header.altitude_max_time = read32inc(data, &offset); log_header->header.cadence_avg = read8inc(data, &offset); log_header->header.cadence_max = read8inc(data, &offset); log_header->header.cadence_max_time = read32inc(data, &offset); log_header->header.speed_avg = read16inc(data, &offset); // 10 m/h log_header->header.speed_max = read16inc(data, &offset); // 10 m/h log_header->header.speed_max_time = read32inc(data, &offset); offset += 4; // Unknown bytes log_header->header.duration = read32inc(data, &offset)*100; // seconds 0.1 log_header->header.ascent = read16inc(data, &offset); log_header->header.descent = read16inc(data, &offset); log_header->header.ascent_time = read32inc(data, &offset)*1000; log_header->header.descent_time = read32inc(data, &offset)*1000; log_header->header.recovery_time = read16inc(data, &offset)*60*1000; log_header->header.peak_training_effect = read8inc(data, &offset); if (log_header->header.activity_name) { free(log_header->header.activity_name); } log_header->header.activity_name = utf8memconv((const char*)(data + offset), 16, "ISO-8859-15"); log_header->header.distance = read32inc(data, &offset); log_header->header.energy_consumption = read16inc(data, &offset); return 0; }
/** * Parse the given sample * \return number of samples added (1 or 0) */ static int parse_sample(uint8_t *buf, size_t offset, uint8_t **spec, ambit_log_entry_t *log_entry, size_t *sample_count) { int ret = 0; size_t int_offset = offset; uint16_t sample_len = read16inc(buf, &int_offset); uint8_t sample_type = read8inc(buf, &int_offset); uint8_t episodic_type; uint16_t spec_count, spec_type, spec_offset; periodic_sample_spec_t *spec_entry; int i; switch (sample_type) { case 0: /* periodic sample specifier */ // Update specifier on input *spec = buf + offset + 2; break; case 2: /* periodic sample */ log_entry->samples[*sample_count].type = ambit_log_sample_type_periodic; log_entry->samples[*sample_count].time = read32(buf, offset + sample_len - 2); // Loop through specifier and set corresponding fields spec_count = read16(*spec, 1); log_entry->samples[*sample_count].u.periodic.value_count = spec_count; log_entry->samples[*sample_count].u.periodic.values = calloc(spec_count, sizeof(ambit_log_sample_periodic_value_t)); for (i=0, spec_entry = (periodic_sample_spec_t*)(*spec + 3); i<spec_count; i++, spec_entry++) { spec_type = le16toh(spec_entry->type); spec_offset = le16toh(spec_entry->offset); switch(spec_type) { case ambit_log_sample_periodic_type_latitude: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_latitude; log_entry->samples[*sample_count].u.periodic.values[i].u.latitude = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_longitude: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_longitude; log_entry->samples[*sample_count].u.periodic.values[i].u.longitude = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_distance: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_distance; log_entry->samples[*sample_count].u.periodic.values[i].u.distance = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_speed: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_speed; log_entry->samples[*sample_count].u.periodic.values[i].u.speed = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_hr: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_hr; log_entry->samples[*sample_count].u.periodic.values[i].u.hr = read8(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_time: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_time; log_entry->samples[*sample_count].u.periodic.values[i].u.time = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_gpsspeed: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_gpsspeed; log_entry->samples[*sample_count].u.periodic.values[i].u.gpsspeed = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_wristaccspeed: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_wristaccspeed; log_entry->samples[*sample_count].u.periodic.values[i].u.wristaccspeed = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_bikepodspeed: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_bikepodspeed; log_entry->samples[*sample_count].u.periodic.values[i].u.bikepodspeed = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_ehpe: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_ehpe; log_entry->samples[*sample_count].u.periodic.values[i].u.ehpe = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_evpe: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_evpe; log_entry->samples[*sample_count].u.periodic.values[i].u.evpe = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_altitude: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_altitude; log_entry->samples[*sample_count].u.periodic.values[i].u.altitude = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_abspressure: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_abspressure; log_entry->samples[*sample_count].u.periodic.values[i].u.abspressure = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_energy: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_energy; log_entry->samples[*sample_count].u.periodic.values[i].u.energy = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_temperature: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_temperature; log_entry->samples[*sample_count].u.periodic.values[i].u.temperature = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_charge: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_charge; log_entry->samples[*sample_count].u.periodic.values[i].u.charge = read8(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_gpsaltitude: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_gpsaltitude; log_entry->samples[*sample_count].u.periodic.values[i].u.gpsaltitude = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_gpsheading: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_gpsheading; log_entry->samples[*sample_count].u.periodic.values[i].u.gpsheading = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_gpshdop: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_gpshdop; log_entry->samples[*sample_count].u.periodic.values[i].u.gpshdop = read8(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_gpsvdop: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_gpsvdop; log_entry->samples[*sample_count].u.periodic.values[i].u.gpsvdop = read8(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_wristcadence: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_wristcadence; log_entry->samples[*sample_count].u.periodic.values[i].u.wristcadence = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_snr: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_snr; memcpy(log_entry->samples[*sample_count].u.periodic.values[i].u.snr, buf + int_offset + spec_offset, 16); break; case ambit_log_sample_periodic_type_noofsatellites: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_noofsatellites; log_entry->samples[*sample_count].u.periodic.values[i].u.noofsatellites = read8(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_sealevelpressure: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_sealevelpressure; log_entry->samples[*sample_count].u.periodic.values[i].u.sealevelpressure = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_verticalspeed: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_verticalspeed; log_entry->samples[*sample_count].u.periodic.values[i].u.verticalspeed = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_cadence: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_cadence; log_entry->samples[*sample_count].u.periodic.values[i].u.cadence = read8(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_bikepower: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_bikepower; log_entry->samples[*sample_count].u.periodic.values[i].u.bikepower = read16(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_swimingstrokecnt: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_swimingstrokecnt; log_entry->samples[*sample_count].u.periodic.values[i].u.swimingstrokecnt = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_ruleoutput1: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_ruleoutput1; log_entry->samples[*sample_count].u.periodic.values[i].u.ruleoutput1 = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_ruleoutput2: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_ruleoutput2; log_entry->samples[*sample_count].u.periodic.values[i].u.ruleoutput2 = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_ruleoutput3: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_ruleoutput3; log_entry->samples[*sample_count].u.periodic.values[i].u.ruleoutput3 = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_ruleoutput4: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_ruleoutput4; log_entry->samples[*sample_count].u.periodic.values[i].u.ruleoutput4 = read32(buf, int_offset + spec_offset); break; case ambit_log_sample_periodic_type_ruleoutput5: log_entry->samples[*sample_count].u.periodic.values[i].type = ambit_log_sample_periodic_type_ruleoutput5; log_entry->samples[*sample_count].u.periodic.values[i].u.ruleoutput5 = read32(buf, int_offset + spec_offset); break; } } ret = 1; break; case 3: // First parameter is relative time log_entry->samples[*sample_count].time = read32inc(buf, &int_offset); episodic_type = read8inc(buf, &int_offset); switch (episodic_type) { case 0x04: log_entry->samples[*sample_count].type = ambit_log_sample_type_logpause; break; case 0x05: log_entry->samples[*sample_count].type = ambit_log_sample_type_logrestart; break; case 0x06: log_entry->samples[*sample_count].type = ambit_log_sample_type_ibi; for (i=0; i<(sample_len - 6)/2; i++) { log_entry->samples[*sample_count].u.ibi.ibi[i] = read16inc(buf, &int_offset); } log_entry->samples[*sample_count].u.ibi.ibi_count = (sample_len - 6)/2; break; case 0x07: log_entry->samples[*sample_count].type = ambit_log_sample_type_ttff; log_entry->samples[*sample_count].u.ttff = read16inc(buf, &int_offset); break; case 0x08: log_entry->samples[*sample_count].type = ambit_log_sample_type_distance_source; log_entry->samples[*sample_count].u.distance_source = read8inc(buf, &int_offset); break; case 0x09: log_entry->samples[*sample_count].type = ambit_log_sample_type_lapinfo; log_entry->samples[*sample_count].u.lapinfo.event_type = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.lapinfo.date_time.year = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.lapinfo.date_time.month = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.lapinfo.date_time.day = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.lapinfo.date_time.hour = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.lapinfo.date_time.minute = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.lapinfo.date_time.msec = read8inc(buf, &int_offset)*1000; log_entry->samples[*sample_count].u.lapinfo.duration = read32inc(buf, &int_offset)*100; log_entry->samples[*sample_count].u.lapinfo.distance = read32inc(buf, &int_offset); break; case 0x0d: log_entry->samples[*sample_count].type = ambit_log_sample_type_altitude_source; log_entry->samples[*sample_count].u.altitude_source.source_type = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.altitude_source.altitude_offset = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.altitude_source.pressure_offset = read16inc(buf, &int_offset); break; case 0x0f: log_entry->samples[*sample_count].type = ambit_log_sample_type_gps_base; log_entry->samples[*sample_count].u.gps_base.navvalid = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.navtype = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.utc_base_time.year = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.utc_base_time.month = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.utc_base_time.day = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.utc_base_time.hour = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.utc_base_time.minute = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.utc_base_time.msec = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.latitude = read32inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.longitude = read32inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.altitude = read32inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.speed = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.heading = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.ehpe = read32inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.noofsatellites = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.hdop = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.satellites = calloc((sample_len - 40)/4, sizeof(ambit_log_gps_satellite_t)); for (i=0; i<(sample_len - 40)/4; i++) { log_entry->samples[*sample_count].u.gps_base.satellites[i].sv = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_base.satellites[i].state = read8inc(buf, &int_offset); int_offset += 1; log_entry->samples[*sample_count].u.gps_base.satellites[i].snr = read8inc(buf, &int_offset); } log_entry->samples[*sample_count].u.gps_base.satellites_count = (sample_len - 40)/4; break; case 0x10: log_entry->samples[*sample_count].type = ambit_log_sample_type_gps_small; log_entry->samples[*sample_count].u.gps_small.latitude = (int16_t)read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_small.longitude = (int16_t)read16inc(buf, &int_offset); int_offset += 2; // Time (seconds) log_entry->samples[*sample_count].u.gps_small.ehpe = read8inc(buf, &int_offset)*100; log_entry->samples[*sample_count].u.gps_small.noofsatellites = read8inc(buf, &int_offset); break; case 0x11: log_entry->samples[*sample_count].type = ambit_log_sample_type_gps_tiny; log_entry->samples[*sample_count].u.gps_tiny.latitude = (int8_t)read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_tiny.longitude = (int8_t)read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.gps_tiny.unknown = read8inc(buf, &int_offset); break; case 0x12: log_entry->samples[*sample_count].type = ambit_log_sample_type_time; log_entry->samples[*sample_count].u.time.hour = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.time.minute = read8inc(buf, &int_offset); log_entry->samples[*sample_count].u.time.second = read8inc(buf, &int_offset); break; case 0x18: log_entry->samples[*sample_count].type = ambit_log_sample_type_activity; log_entry->samples[*sample_count].u.activity.activitytype = read16inc(buf, &int_offset); log_entry->samples[*sample_count].u.activity.custommode = read32inc(buf, &int_offset); break; case 0x1b: log_entry->samples[*sample_count].type = ambit_log_sample_type_position; log_entry->samples[*sample_count].u.position.latitude = read32inc(buf, &int_offset); log_entry->samples[*sample_count].u.position.longitude = read32inc(buf, &int_offset); break; default: LOG_WARNING("Found unknown episodic sample type (0x%02x)", episodic_type); log_entry->samples[*sample_count].type = ambit_log_sample_type_unknown; log_entry->samples[*sample_count].u.unknown.datalen = sample_len; log_entry->samples[*sample_count].u.unknown.data = malloc(sample_len); memcpy(log_entry->samples[*sample_count].u.unknown.data, buf + offset + 2, sample_len); break; } ret = 1; break; default: LOG_WARNING("Found unknown sample type (0x%02x)", sample_type); log_entry->samples[*sample_count].type = ambit_log_sample_type_unknown; log_entry->samples[*sample_count].u.unknown.datalen = sample_len; log_entry->samples[*sample_count].u.unknown.data = malloc(sample_len); memcpy(log_entry->samples[*sample_count].u.unknown.data, buf + offset + 2, sample_len); ret = 1; break; } *sample_count += ret; return ret; }
int libambit_pmem20_log_parse_header(uint8_t *data, size_t datalen, ambit_log_header_t *log_header) { size_t offset = 0; // Check that header is long enough to be parsed correctly if (datalen < 129) { return -1; } offset = 1; log_header->date_time.year = read16inc(data, &offset); log_header->date_time.month = read8inc(data, &offset); log_header->date_time.day = read8inc(data, &offset); log_header->date_time.hour = read8inc(data, &offset); log_header->date_time.minute = read8inc(data, &offset); log_header->date_time.msec = read8inc(data, &offset)*1000; memcpy(log_header->unknown1, data+offset, 5); offset += 5; log_header->duration = read32inc(data, &offset)*100; // seconds 0.1 log_header->ascent = read16inc(data, &offset); log_header->descent = read16inc(data, &offset); log_header->ascent_time = read32inc(data, &offset)*1000; log_header->descent_time = read32inc(data, &offset)*1000; log_header->recovery_time = read16inc(data, &offset)*60*1000; log_header->speed_avg = read16inc(data, &offset)*10; // 10 m/h log_header->speed_max = read16inc(data, &offset)*10; // 10 m/h log_header->altitude_max = read16inc(data, &offset); log_header->altitude_min = read16inc(data, &offset); log_header->heartrate_avg = read8inc(data, &offset); log_header->heartrate_max = read8inc(data, &offset); log_header->peak_training_effect = read8inc(data, &offset); log_header->activity_type = read8inc(data, &offset); memcpy(log_header->activity_name, data + offset, 16); log_header->activity_name[15] = 0; offset += 16; log_header->heartrate_min = read8inc(data, &offset); log_header->unknown2 = read8inc(data, &offset); log_header->temperature_max = read16inc(data, &offset); log_header->temperature_min = read16inc(data, &offset); log_header->distance = read32inc(data, &offset); log_header->samples_count = read32inc(data, &offset); log_header->energy_consumption = read16inc(data, &offset); log_header->cadence_max = read8inc(data, &offset); log_header->cadence_avg = read8inc(data, &offset); memcpy(log_header->unknown3, data+offset, 4); offset += 4; log_header->speed_max_time = read32inc(data, &offset); log_header->altitude_max_time = read32inc(data, &offset); log_header->altitude_min_time = read32inc(data, &offset); log_header->heartrate_max_time = read32inc(data, &offset); log_header->heartrate_min_time = read32inc(data, &offset); log_header->temperature_max_time = read32inc(data, &offset); log_header->temperature_min_time = read32inc(data, &offset); log_header->cadence_max_time = read32inc(data, &offset); memcpy(log_header->unknown4, data+offset, 4); offset += 4; log_header->first_fix_time = read16inc(data, &offset)*1000; log_header->battery_start = read8inc(data, &offset); log_header->battery_end = read8inc(data, &offset); memcpy(log_header->unknown5, data+offset, 4); offset += 4; log_header->distance_before_calib = read32inc(data, &offset); if (datalen >= offset + 24) { memcpy(log_header->unknown6, data+offset, 24); offset += 24; } return 0; }
ambit_log_entry_t *libambit_pmem20_log_read_entry(ambit_object_t *object) { // Note! We assume that the caller has called libambit_pmem20_log_next_header just before uint8_t *periodic_sample_spec; uint16_t tmp_len, sample_len; size_t buffer_offset, sample_count = 0, i; ambit_log_entry_t *log_entry; ambit_log_sample_t *last_periodic = NULL, *utcsource = NULL, *altisource = NULL; ambit_date_time_t utcbase; uint32_t altisource_index = 0; uint32_t last_base_lat = 0, last_base_long = 0; uint32_t last_small_lat = 0, last_small_long = 0; uint32_t last_ehpe = 0; if (!object->pmem20.log.initialized) { LOG_ERROR("Trying to get log entry without initialization"); return NULL; } // Allocate log entry if ((log_entry = calloc(1, sizeof(ambit_log_entry_t))) == NULL) { object->pmem20.log.initialized = false; return NULL; } LOG_INFO("Reading log entry from address=%08x", object->pmem20.log.current.current); buffer_offset = (object->pmem20.log.current.current - PMEM20_LOG_START); buffer_offset += 12; // Read samples content definition tmp_len = read16inc(object->pmem20.log.buffer, &buffer_offset); periodic_sample_spec = object->pmem20.log.buffer + buffer_offset; buffer_offset += tmp_len; // Parse header tmp_len = read16inc(object->pmem20.log.buffer, &buffer_offset); if (libambit_pmem20_log_parse_header(object->pmem20.log.buffer + buffer_offset, tmp_len, &log_entry->header) != 0) { LOG_ERROR("Failed to parse log entry header correctly"); free(log_entry); object->pmem20.log.initialized = false; return NULL; } buffer_offset += tmp_len; // Now that we know number of samples, allocate space for them! if ((log_entry->samples = calloc(log_entry->header.samples_count, sizeof(ambit_log_sample_t))) == NULL) { free(log_entry); object->pmem20.log.initialized = false; return NULL; } log_entry->samples_count = log_entry->header.samples_count; LOG_INFO("Log entry got %d samples, reading", log_entry->samples_count); // OK, so we are at start of samples, get them all! while (sample_count < log_entry->samples_count) { /* NOTE! The double reads below seems a bit unoptimized, but if we need optimization, we should optimize read_upto instead... To ease the pain on wraparound we simply duplicate the sample to the end of the buffer. */ // First check for log area wrap if (buffer_offset >= PMEM20_LOG_SIZE - 1) { read_upto(object, PMEM20_LOG_START + PMEM20_LOG_WRAP_START_OFFSET, 2); sample_len = read16(object->pmem20.log.buffer, PMEM20_LOG_WRAP_START_OFFSET); } else if (buffer_offset == PMEM20_LOG_SIZE - 2) { read_upto(object, PMEM20_LOG_START + PMEM20_LOG_WRAP_START_OFFSET, 1); sample_len = object->pmem20.log.buffer[buffer_offset] | (object->pmem20.log.buffer[PMEM20_LOG_WRAP_START_OFFSET] << 8); } else { read_upto(object, PMEM20_LOG_START + buffer_offset, 2); sample_len = read16(object->pmem20.log.buffer, buffer_offset); } // Read all data if (buffer_offset + 2 < (PMEM20_LOG_SIZE-1)) { read_upto(object, PMEM20_LOG_START + buffer_offset + 2, sample_len); } if (buffer_offset + 2 + sample_len > PMEM20_LOG_SIZE) { read_upto(object, PMEM20_LOG_START + PMEM20_LOG_WRAP_START_OFFSET, (buffer_offset + 2 + sample_len) - PMEM20_LOG_SIZE); memcpy(object->pmem20.log.buffer + PMEM20_LOG_SIZE, object->pmem20.log.buffer + PMEM20_LOG_WRAP_START_OFFSET, (buffer_offset + 2 + sample_len) - PMEM20_LOG_SIZE); } if (parse_sample(object->pmem20.log.buffer, buffer_offset, &periodic_sample_spec, log_entry, &sample_count) == 1) { // Calculate times if (log_entry->samples[sample_count-1].type == ambit_log_sample_type_periodic) { last_periodic = &log_entry->samples[sample_count-1]; } else if (last_periodic != NULL) { log_entry->samples[sample_count-1].time += last_periodic->time; } else { log_entry->samples[sample_count-1].time = 0; } if (utcsource == NULL && log_entry->samples[sample_count-1].type == ambit_log_sample_type_gps_base) { utcsource = &log_entry->samples[sample_count-1]; // Calculate UTC base time add_time(&utcsource->u.gps_base.utc_base_time, 0-utcsource->time, &utcbase); } // Calculate positions if (log_entry->samples[sample_count-1].type == ambit_log_sample_type_gps_base) { last_base_lat = log_entry->samples[sample_count-1].u.gps_base.latitude; last_base_long = log_entry->samples[sample_count-1].u.gps_base.longitude; last_small_lat = log_entry->samples[sample_count-1].u.gps_base.latitude; last_small_long = log_entry->samples[sample_count-1].u.gps_base.longitude; last_ehpe = log_entry->samples[sample_count-1].u.gps_base.ehpe; } else if (log_entry->samples[sample_count-1].type == ambit_log_sample_type_gps_small) { log_entry->samples[sample_count-1].u.gps_small.latitude = last_base_lat + log_entry->samples[sample_count-1].u.gps_small.latitude*10; log_entry->samples[sample_count-1].u.gps_small.longitude = last_base_long + log_entry->samples[sample_count-1].u.gps_small.longitude*10; last_small_lat = log_entry->samples[sample_count-1].u.gps_small.latitude; last_small_long = log_entry->samples[sample_count-1].u.gps_small.longitude; last_ehpe = log_entry->samples[sample_count-1].u.gps_small.ehpe; } else if (log_entry->samples[sample_count-1].type == ambit_log_sample_type_gps_tiny) { log_entry->samples[sample_count-1].u.gps_tiny.latitude = last_small_lat + log_entry->samples[sample_count-1].u.gps_tiny.latitude*10; log_entry->samples[sample_count-1].u.gps_tiny.longitude = last_small_long + log_entry->samples[sample_count-1].u.gps_tiny.longitude*10; log_entry->samples[sample_count-1].u.gps_tiny.ehpe = (last_ehpe > 700 ? 700 : last_ehpe); last_small_lat = log_entry->samples[sample_count-1].u.gps_tiny.latitude; last_small_long = log_entry->samples[sample_count-1].u.gps_tiny.longitude; } if (altisource == NULL && log_entry->samples[sample_count-1].type == ambit_log_sample_type_altitude_source) { altisource = &log_entry->samples[sample_count-1]; altisource_index = sample_count-1; } } buffer_offset += 2 + sample_len; // Wrap if (buffer_offset >= PMEM20_LOG_SIZE) { buffer_offset = PMEM20_LOG_WRAP_START_OFFSET + (buffer_offset - PMEM20_LOG_SIZE); } } // Loop through samples again and correct times etc for (sample_count = 0; sample_count < log_entry->header.samples_count; sample_count++) { // Set UTC times (if UTC source found) if (utcsource != NULL) { add_time(&utcbase, log_entry->samples[sample_count].time, &log_entry->samples[sample_count].utc_time); } // Correct altitude based on altitude offset in altitude source if (altisource != NULL && log_entry->samples[sample_count].type == ambit_log_sample_type_periodic && sample_count < altisource_index) { for (i=0; i<log_entry->samples[sample_count].u.periodic.value_count; i++) { if (log_entry->samples[sample_count].u.periodic.values[i].type == ambit_log_sample_periodic_type_sealevelpressure) { log_entry->samples[sample_count].u.periodic.values[i].u.sealevelpressure += altisource->u.altitude_source.pressure_offset; } if (log_entry->samples[sample_count].u.periodic.values[i].type == ambit_log_sample_periodic_type_altitude) { log_entry->samples[sample_count].u.periodic.values[i].u.altitude += altisource->u.altitude_source.altitude_offset; } } } } return log_entry; }