diff --git a/src/DownloadRideDialog.cpp b/src/DownloadRideDialog.cpp index 2aceb4417..a3cc29246 100644 --- a/src/DownloadRideDialog.cpp +++ b/src/DownloadRideDialog.cpp @@ -30,7 +30,7 @@ DownloadRideDialog::DownloadRideDialog(MainWindow *mainWindow, const QDir &home) : mainWindow(mainWindow), home(home), cancelled(false), - downloadInProgress(false), recInt(0), endingOffset(0) + downloadInProgress(false), recIntSecs(0.0), endingOffset(0) { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle("Download Ride Data"); @@ -130,17 +130,18 @@ DownloadRideDialog::statusCallback(PowerTap::State state) else { assert(state == PowerTap::STATE_DATA_AVAILABLE); unsigned char *buf = records.data(); - if (recInt == 0.0) { + bool bIsVer81 = PowerTap::is_Ver81(buf); + if (recIntSecs == 0.0) { for (int i = 0; i < records.size(); i += 6) { - if (PowerTap::is_config(buf + i)) { + if (PowerTap::is_config(buf + i, bIsVer81)) { unsigned unused1, unused2, unused3; PowerTap::unpack_config(buf + i, &unused1, &unused2, - &recInt, &unused3); + &recIntSecs, &unused3, bIsVer81); } } } - if (recInt != 0.0) { - int min = (int) round(records.size() / 6 * recInt * 0.021); + if (recIntSecs != 0.0) { + int min = (int) round(records.size() / 6 * recIntSecs); QString existing = label->text(); existing.chop(existing.size() - endingOffset); existing.append(QString("Ride data read: %1:%2").arg(min / 60) @@ -150,8 +151,8 @@ DownloadRideDialog::statusCallback(PowerTap::State state) if (filename == "") { struct tm time; for (int i = 0; i < records.size(); i += 6) { - if (PowerTap::is_time(buf + i)) { - PowerTap::unpack_time(buf + i, &time); + if (PowerTap::is_time(buf + i, bIsVer81)) { + PowerTap::unpack_time(buf + i, &time, bIsVer81); char tmp[32]; sprintf(tmp, "%04d_%02d_%02d_%02d_%02d_%02d.raw", time.tm_year + 1900, time.tm_mon + 1, @@ -241,8 +242,10 @@ DownloadRideDialog::downloadClicked() struct tm time; bool time_set = false; unsigned char *data = records.data(); + bool bIsVer81 = PowerTap::is_Ver81(data); + for (int i = 0; i < records.size(); i += 6) { - if (data[i] == 0) + if (data[i] == 0 && !bIsVer81) continue; for (int j = 0; j < 6; ++j) { os.setFieldWidth(2); @@ -250,8 +253,8 @@ DownloadRideDialog::downloadClicked() os.setFieldWidth(1); os << ((j == 5) ? "\n" : " "); } - if (!time_set && PowerTap::is_time(data + i)) { - PowerTap::unpack_time(data + i, &time); + if (!time_set && PowerTap::is_time(data + i, bIsVer81)) { + PowerTap::unpack_time(data + i, &time, bIsVer81); time_set = true; } } diff --git a/src/DownloadRideDialog.h b/src/DownloadRideDialog.h index 6cd04466b..caf0492cb 100644 --- a/src/DownloadRideDialog.h +++ b/src/DownloadRideDialog.h @@ -51,7 +51,7 @@ class DownloadRideDialog : public QDialog QVector devList; bool cancelled, downloadInProgress; - unsigned recInt; + double recIntSecs; int endingOffset; QVector records; QString filename, filepath; diff --git a/src/PowerTap.cpp b/src/PowerTap.cpp index 911ceb2e5..f7fb11db6 100644 --- a/src/PowerTap.cpp +++ b/src/PowerTap.cpp @@ -227,14 +227,29 @@ PowerTap::download(DevicePtr dev, QByteArray &version, return true; } -int -PowerTap::is_time(unsigned char *buf) +bool +PowerTap::is_ignore_record(unsigned char *buf, bool bVer81) { - return buf[0] == 0x60; + if (bVer81) + return buf[0]==0 && buf[1]==0 && buf[2]==0; + else + return buf[0]==0; +} + +bool +PowerTap::is_Ver81(unsigned char *buf) +{ + return buf[3] == 0x81; +} + +int +PowerTap::is_time(unsigned char *buf, bool bVer81) +{ + return (bVer81 && buf[0] == 0x10) || (!bVer81 && buf[0] == 0x60); } time_t -PowerTap::unpack_time(unsigned char *buf, struct tm *time) +PowerTap::unpack_time(unsigned char *buf, struct tm *time, bool bVer81) { memset(time, 0, sizeof(*time)); time->tm_year = 2000 + buf[1] - 1900; @@ -248,15 +263,18 @@ PowerTap::unpack_time(unsigned char *buf, struct tm *time) } int -PowerTap::is_config(unsigned char *buf) +PowerTap::is_config(unsigned char *buf, bool bVer81) { - return buf[0] == 0x40; + return (bVer81 && buf[0] == 0x00) || (!bVer81 && buf[0] == 0x40); } +const double TIME_UNIT_SEC = 0.021*60.0; +const double TIME_UNIT_SEC_V81 = 0.01; + int PowerTap::unpack_config(unsigned char *buf, unsigned *interval, - unsigned *last_interval, unsigned *rec_int, - unsigned *wheel_sz_mm) + unsigned *last_interval, double *rec_int_secs, + unsigned *wheel_sz_mm, bool bVer81) { *wheel_sz_mm = (buf[1] << 8) | buf[2]; /* Data from device wraps interval after 9... */ @@ -264,14 +282,27 @@ PowerTap::unpack_config(unsigned char *buf, unsigned *interval, *last_interval = buf[3]; ++*interval; } - *rec_int = buf[4] + 1; + + *rec_int_secs = buf[4]; + if (bVer81) + { + *rec_int_secs *= TIME_UNIT_SEC_V81; + } + else + { + *rec_int_secs += 1; + *rec_int_secs *= TIME_UNIT_SEC; + } return 0; } int -PowerTap::is_data(unsigned char *buf) +PowerTap::is_data(unsigned char *buf, bool bVer81) { - return (buf[0] & 0x80) == 0x80; + if (bVer81) + return (buf[0] & 0x40) == 0x40; + else + return (buf[0] & 0x80) == 0x80; } static double @@ -287,7 +318,6 @@ my_round(double x) #define MAGIC_CONSTANT 147375.0 #define PI 3.14159265 -#define TIME_UNIT_MIN 0.021 #define LBFIN_TO_NM 0.11298483 #define KM_TO_MI 0.62137119 @@ -297,60 +327,98 @@ my_round(double x) #define BAD_KM_TO_MI 0.62 void -PowerTap::unpack_data(unsigned char *buf, int compat, unsigned rec_int, +PowerTap::unpack_data(unsigned char *buf, int compat, double rec_int_secs, unsigned wheel_sz_mm, double *time_secs, double *torque_Nm, double *mph, double *watts, - double *dist_m, unsigned *cad, unsigned *hr) + double *dist_m, unsigned *cad, unsigned *hr, bool bVer81) { - double kph10; - unsigned speed; - unsigned torque_inlbs; - double rotations; - double radians; - double joules; + if (bVer81) + { + const double CLOCK_TICK_TIME = 0.000512; + const double METERS_PER_SEC_TO_MPH = 2.23693629; - *time_secs += rec_int * TIME_UNIT_MIN * 60.0; - torque_inlbs = ((buf[1] & 0xf0) << 4) | buf[2]; - if (torque_inlbs == 0xfff) - torque_inlbs = 0; - speed = ((buf[1] & 0x0f) << 8) | buf[3]; - if ((speed < 100) || (speed == 0xfff)) { - if ((speed != 0) && (speed < 1000)) { - fprintf(stderr, "possible error: speed=%.1f; ignoring it\n", - MAGIC_CONSTANT / speed / 10.0); + *time_secs += rec_int_secs; + int rotations = buf[0] & 0x0f; + int ticks_for_1_rotation = (buf[1]<<4) | (buf[2]>>4); + + if (ticks_for_1_rotation==0xff0 || ticks_for_1_rotation==0) + { + *watts = 0; + *cad = 0; + *mph = 0; + *torque_Nm = 0; } - *mph = -1.0; - *watts = -1.0; - } - else { - if (compat) - *torque_Nm = torque_inlbs * BAD_LBFIN_TO_NM_2; else - *torque_Nm = torque_inlbs * LBFIN_TO_NM; - kph10 = MAGIC_CONSTANT / speed; - if (compat) - *mph = my_round(kph10) / 10.0 * BAD_KM_TO_MI; - else - *mph = kph10 / 10.0 * KM_TO_MI; - rotations = rec_int * TIME_UNIT_MIN * 100000.0 * kph10 - / wheel_sz_mm / 60.0; - radians = rotations * 2.0 * PI; - joules = *torque_Nm * radians; - *watts = joules / (rec_int * TIME_UNIT_MIN * 60); - if (compat) - *watts = my_round(*watts); - else - *watts = round(*watts); + { + *watts = ((buf[2] & 0x0f) << 8) | buf[3]; + *cad = buf[4]; + if (*cad == 0xff) + *cad = 0; + + double wheel_sz_meters = wheel_sz_mm / 1000.0; + *dist_m += rotations * wheel_sz_meters; + double seconds_for_1_rotation = ticks_for_1_rotation * CLOCK_TICK_TIME; + double meters_per_sec = wheel_sz_meters / seconds_for_1_rotation; + *mph = meters_per_sec * METERS_PER_SEC_TO_MPH; + + *torque_Nm = (*watts * seconds_for_1_rotation)/(2.0*PI); + } + *hr = buf[5]; + if (*hr == 0xff) + *hr = 0; + + } + else + { + double kph10; + unsigned speed; + unsigned torque_inlbs; + + *time_secs += rec_int_secs; + torque_inlbs = ((buf[1] & 0xf0) << 4) | buf[2]; + if (torque_inlbs == 0xfff) + torque_inlbs = 0; + speed = ((buf[1] & 0x0f) << 8) | buf[3]; + if ((speed < 100) || (speed == 0xfff)) { + if ((speed != 0) && (speed < 1000)) { + fprintf(stderr, "possible error: speed=%.1f; ignoring it\n", + MAGIC_CONSTANT / speed / 10.0); + } + *mph = -1.0; + *watts = -1.0; + } + else { + if (compat) + *torque_Nm = torque_inlbs * BAD_LBFIN_TO_NM_2; + else + *torque_Nm = torque_inlbs * LBFIN_TO_NM; + kph10 = MAGIC_CONSTANT / speed; + if (compat) + *mph = my_round(kph10) / 10.0 * BAD_KM_TO_MI; + else + *mph = kph10 / 10.0 * KM_TO_MI; + + // from http://en.wikipedia.org/wiki/Torque#Conversion_to_other_units + double dMetersPerMinute = (kph10 / 10.0) * 1000.0 / 60.0; + double dWheelSizeMeters = wheel_sz_mm / 1000.0; + double rpm = dMetersPerMinute/dWheelSizeMeters; + *watts = *torque_Nm * rpm * 2.0 * PI /60.0; + + if (compat) + *watts = my_round(*watts); + else + *watts = round(*watts); + } + if (compat) + *torque_Nm = torque_inlbs * BAD_LBFIN_TO_NM_1; + *dist_m += (buf[0] & 0x7f) * wheel_sz_mm / 1000.0; + *cad = buf[4]; + if (*cad == 0xff) + *cad = 0; + *hr = buf[5]; + if (*hr == 0xff) + *hr = 0; } - if (compat) - *torque_Nm = torque_inlbs * BAD_LBFIN_TO_NM_1; - *dist_m += (buf[0] & 0x7f) * wheel_sz_mm / 1000.0; - *cad = buf[4]; - if (*cad == 0xff) - *cad = 0; - *hr = buf[5]; - if (*hr == 0xff) - *hr = 0; } diff --git a/src/PowerTap.h b/src/PowerTap.h index 21aeeb84c..fbf662230 100644 --- a/src/PowerTap.h +++ b/src/PowerTap.h @@ -37,19 +37,23 @@ struct PowerTap QVector &records, StatusCallback statusCallback, QString &err); - static int is_time(unsigned char *buf); - static time_t unpack_time(unsigned char *buf, struct tm *time); + static bool is_Ver81(unsigned char *bufHeader); - static int is_config(unsigned char *buf); + static bool is_ignore_record(unsigned char *buf, bool bVer81); + + static int is_time(unsigned char *buf, bool bVer81); + static time_t unpack_time(unsigned char *buf, struct tm *time, bool bVer81); + + static int is_config(unsigned char *buf, bool bVer81); static int unpack_config(unsigned char *buf, unsigned *interval, - unsigned *last_interval, unsigned *rec_int, - unsigned *wheel_sz_mm); + unsigned *last_interval, double *rec_int_secs, + unsigned *wheel_sz_mm, bool bVer81); - static int is_data(unsigned char *buf); - static void unpack_data(unsigned char *buf, int compat, unsigned rec_int, + static int is_data(unsigned char *buf, bool bVer81); + static void unpack_data(unsigned char *buf, int compat, double rec_int_secs, unsigned wheel_sz_mm, double *time_secs, double *torque_Nm, double *mph, double *watts, - double *dist_m, unsigned *cad, unsigned *hr); + double *dist_m, unsigned *cad, unsigned *hr, bool bVer81); }; #endif // _GC_PT_PowerTap_h diff --git a/src/RawRideFile.cpp b/src/RawRideFile.cpp index 8dab1e5ff..2a21bdda6 100644 --- a/src/RawRideFile.cpp +++ b/src/RawRideFile.cpp @@ -35,25 +35,26 @@ struct ReadState double last_secs, last_miles; unsigned last_interval; time_t start_since_epoch; - unsigned rec_int; + // this seems to not be used + //unsigned rec_int; ReadState(RideFile *rideFile, QStringList &errors) : rideFile(rideFile), errors(errors), last_secs(0.0), - last_miles(0.0), last_interval(0), start_since_epoch(0), rec_int(0) {} + last_miles(0.0), last_interval(0), start_since_epoch(0)/*, rec_int(0)*/ {} }; static void -config_cb(unsigned interval, unsigned rec_int, +config_cb(unsigned interval, double rec_int_secs, unsigned wheel_sz_mm, void *context) { (void) interval; (void) wheel_sz_mm; ReadState *state = (ReadState*) context; // Assume once set, rec_int should never change. - double recIntSecs = rec_int * 1.26; + //double recIntSecs = rec_int * 1.26; assert((state->rideFile->recIntSecs() == 0.0) - || (state->rideFile->recIntSecs() == recIntSecs)); - state->rideFile->setRecIntSecs(recIntSecs); + || (state->rideFile->recIntSecs() == rec_int_secs)); + state->rideFile->setRecIntSecs(rec_int_secs); } static void @@ -94,7 +95,7 @@ error_cb(const char *msg, void *context) static void pt_read_raw(FILE *in, int compat, void *context, - void (*config_cb)(unsigned interval, unsigned rec_int, + void (*config_cb)(unsigned interval, double rec_int_secs, unsigned wheel_sz_mm, void *context), void (*time_cb)(struct tm *time, time_t since_epoch, void *context), void (*data_cb)(double secs, double nm, double mph, @@ -105,7 +106,7 @@ pt_read_raw(FILE *in, int compat, void *context, unsigned interval = 0; unsigned last_interval = 0; unsigned wheel_sz_mm = 0; - unsigned rec_int = 0; + double rec_int_secs = 0.0; int i, n, row = 0; unsigned char buf[6]; unsigned sbuf[6]; @@ -120,6 +121,7 @@ pt_read_raw(FILE *in, int compat, void *context, struct tm time; time_t since_epoch; char ebuf[256]; + bool bIsVer81 = false; while ((n = fscanf(in, "%x %x %x %x %x %x\n", sbuf, sbuf+1, sbuf+2, sbuf+3, sbuf+4, sbuf+5)) == 6) { @@ -128,20 +130,25 @@ pt_read_raw(FILE *in, int compat, void *context, if (sbuf[i] > 0xff) { n = 1; break; } buf[i] = sbuf[i]; } - if (row == 1) { + if (row == 1) + { /* Serial number? */ + bIsVer81 = PowerTap::is_Ver81(buf); } - else if (PowerTap::is_config(buf)) { + else if (PowerTap::is_ignore_record(buf, bIsVer81)) { + // do nothing + } + else if (PowerTap::is_config(buf, bIsVer81)) { if (PowerTap::unpack_config(buf, &interval, &last_interval, - &rec_int, &wheel_sz_mm) < 0) { + &rec_int_secs, &wheel_sz_mm, bIsVer81) < 0) { sprintf(ebuf, "Couldn't unpack config record."); if (error_cb) error_cb(ebuf, context); return; } - if (config_cb) config_cb(interval, rec_int, wheel_sz_mm, context); + if (config_cb) config_cb(interval, rec_int_secs, wheel_sz_mm, context); } - else if (PowerTap::is_time(buf)) { - since_epoch = PowerTap::unpack_time(buf, &time); + else if (PowerTap::is_time(buf, bIsVer81)) { + since_epoch = PowerTap::unpack_time(buf, &time, bIsVer81); bool ignore = false; if (start_secs == 0.0) start_secs = since_epoch; @@ -156,14 +163,14 @@ pt_read_raw(FILE *in, int compat, void *context, } if (time_cb && !ignore) time_cb(&time, since_epoch, context); } - else if (PowerTap::is_data(buf)) { + else if (PowerTap::is_data(buf, bIsVer81)) { if (wheel_sz_mm == 0) { sprintf(ebuf, "Read data row before wheel size set."); if (error_cb) error_cb(ebuf, context); return; } - PowerTap::unpack_data(buf, compat, rec_int, wheel_sz_mm, &secs, - &nm, &mph, &watts, &meters, &cad, &hr); + PowerTap::unpack_data(buf, compat, rec_int_secs, wheel_sz_mm, &secs, + &nm, &mph, &watts, &meters, &cad, &hr, bIsVer81); if (compat) miles = round(meters) / 1000.0 * BAD_KM_TO_MI; else