Esempio n. 1
0
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;
}
Esempio n. 2
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;
}
Esempio n. 3
0
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;
}