~ubuntu-branches/ubuntu/saucy/goldencheetah/saucy

« back to all changes in this revision

Viewing changes to src/FitRideFile.cpp

  • Committer: Package Import Robot
  • Author(s): KURASHIKI Satoru
  • Date: 2013-08-18 07:02:45 UTC
  • mfrom: (4.1.8 sid)
  • Revision ID: package-import@ubuntu.com-20130818070245-zgdvb47e1k3mtgil
Tags: 3.0-3
debian/control: remove needless dependency. (Closes: #719571)

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
#include <QSet>
23
23
#include <QtEndian>
24
24
#include <QDebug>
 
25
#include <QTime>
 
26
#include <assert.h>
25
27
#include <stdio.h>
26
28
#include <stdint.h>
27
29
#include <limits>
48
50
 
49
51
/* FIT has uint32 as largest integer type. So qint64 is large enough to
50
52
 * store all integer types - no matter if they're signed or not */
51
 
// XXX this needs to get changed to support non-integer values
 
53
 
 
54
// this will need to change if float or other non-integer values are 
 
55
// introduced into the file format
52
56
typedef qint64 fit_value_t;
53
57
#define NA_VALUE std::numeric_limits<fit_value_t>::max()
54
58
 
78
82
    }
79
83
 
80
84
    struct TruncatedRead {};
81
 
    struct BadDelta {};
82
85
 
83
86
    void read_unknown( int size, int *count = NULL ){
84
87
        char c[size+1];
85
88
 
86
 
        // XXX: just seek instead of read?
87
89
        if (file.read(c, size ) != size)
88
90
            throw TruncatedRead();
89
91
        if (count)
226
228
                case 988: rideFile->setDeviceType("Garmin FR60"); break;
227
229
                case 1018: rideFile->setDeviceType("Garmin FR310XT"); break;
228
230
                case 1036: rideFile->setDeviceType("Garmin Edge 500"); break;
 
231
                case 1124: rideFile->setDeviceType("Garmin FR110"); break;
229
232
                case 1169: rideFile->setDeviceType("Garmin Edge 800"); break;
230
 
                default: rideFile->setDeviceType(QString("Unknown Garmin Device %1").arg(prod));
 
233
                case 1325: rideFile->setDeviceType("Garmin Edge 200"); break;
 
234
                case 1561: rideFile->setDeviceType("Garmin Edge 510"); break;
 
235
                case 1567: rideFile->setDeviceType("Garmin Edge 810"); break;
 
236
                case 20119: rideFile->setDeviceType("Garmin Training Center"); break;
 
237
                case 65534: rideFile->setDeviceType("Garmin Connect Website"); break;
 
238
                default: rideFile->setDeviceType(QString("Garmin %1").arg(prod));
 
239
            }
 
240
        }
 
241
        else  if (manu == 38) {
 
242
            switch (prod) {
 
243
                case 1: rideFile->setDeviceType("o_synce navi2coach"); break;
 
244
                default: rideFile->setDeviceType(QString("o_synce %1").arg(prod));
231
245
            }
232
246
        }
233
247
        else {
234
248
            rideFile->setDeviceType(QString("Unknown FIT Device %1:%2").arg(manu).arg(prod));
235
249
        }
 
250
        rideFile->setFileFormat("FIT (*.fit)");
236
251
    }
237
252
 
238
253
    void decodeEvent(const FitDefinition &def, int, const std::vector<fit_value_t> values) {
239
 
        time_t time = 0;
240
254
        int event = -1;
241
255
        int event_type = -1;
242
256
        int i = 0;
247
261
                continue;
248
262
 
249
263
            switch (field.num) {
250
 
                case 253: time = value + qbase_time.toTime_t(); break;
 
264
                case 253: //time = value + qbase_time.toTime_t();
 
265
                          break;
251
266
                case 0: event = value; break;
252
267
                case 1: event_type = value; break;
253
268
                default: ; // do nothing
306
321
                default: ; // ignore it
307
322
            }
308
323
        }
309
 
        if (this_start_time == 0 || this_start_time-start_time < 0)
310
 
            errors << QString("lap %1 has invalid start time").arg(interval);
311
 
        else {
312
 
            if (rideFile->dataPoints().count()) // no samples means no laps..
313
 
                rideFile->addInterval(this_start_time - start_time, time - start_time, QString("%1").arg(interval));
 
324
        if (this_start_time == 0 || this_start_time-start_time < 0) {
 
325
            //errors << QString("lap %1 has invalid start time").arg(interval);
 
326
            this_start_time = start_time; // time was corrected after lap start
 
327
 
 
328
            if (time == 0 || time-start_time < 0) {
 
329
                errors << QString("lap %1 is ignored (invalid end time)").arg(interval);
 
330
                return;
 
331
            }
314
332
        }
 
333
 
 
334
        if (rideFile->dataPoints().count()) // no samples means no laps..
 
335
            rideFile->addInterval(this_start_time - start_time, time - start_time, QString("%1").arg(interval));
315
336
    }
316
337
 
317
338
    void decodeRecord(const FitDefinition &def, int time_offset, const std::vector<fit_value_t> values) {
318
339
        time_t time = 0;
319
340
        if (time_offset > 0)
320
341
            time = last_time + time_offset;
321
 
        double alt = 0, cad = 0, km = 0, grade = 0, hr = 0, lat = 0, lng = 0, badgps = 0;
322
 
        double resistance = 0, kph = 0, temperature = 0, time_from_course = 0, watts = 0;
 
342
        double alt = 0, cad = 0, km = 0, hr = 0, lat = 0, lng = 0, badgps = 0, lrbalance = 0;
 
343
        double kph = 0, temperature = RideFile::noTemp, watts = 0, slope = 0;
323
344
        fit_value_t lati = NA_VALUE, lngi = NA_VALUE;
324
345
        int i = 0;
325
346
        foreach(const FitField &field, def.fields) {
332
353
                case 253: time = value + qbase_time.toTime_t();
333
354
                          // Time MUST NOT go backwards
334
355
                          // You canny break the laws of physics, Jim
335
 
                          if (time < last_time) time = last_time;
 
356
                          if (time < last_time)
 
357
                              time = last_time;
336
358
                          break;
337
359
                case 0: lati = value; break;
338
360
                case 1: lngi = value; break;
342
364
                case 5: km = value / 100000.0; break;
343
365
                case 6: kph = value * 3.6 / 1000.0; break;
344
366
                case 7: watts = value; break;
345
 
                case 8: break; // XXX packed speed/dist
346
 
                case 9: grade = value / 100.0; break;
347
 
                case 10: resistance = value; break;
348
 
                case 11: time_from_course = value / 1000.0; break;
349
 
                case 12: break; // XXX "cycle_length"
 
367
                case 8: break; // packed speed/dist
 
368
                case 9: slope = value / 100.0;
 
369
                        break;
 
370
                case 10: //resistance = value;
 
371
                        break;
 
372
                case 11: //time_from_course = value / 1000.0;
 
373
                         break;
 
374
                case 12: break; // "cycle_length"
350
375
                case 13: temperature = value; break;
 
376
                case 29: // ACCUMULATED_POWER
 
377
                         break;
 
378
                case 30: lrbalance = (value & 0x80 ? 100 - (value & 0x7F) : value & 0x7F);break;
 
379
 
351
380
                default: unknown_record_fields.insert(field.num);
352
381
            }
353
382
        }
374
403
            badgps = 1;
375
404
        }
376
405
        if (start_time == 0) {
377
 
            start_time = time - 1; // XXX: recording interval?
 
406
            start_time = time - 1; // recording interval?
378
407
            QDateTime t;
379
408
            t.setTime_t(start_time);
380
409
            rideFile->setStartTime(t);
395
424
            // Evil smart recording.  Linearly interpolate missing points.
396
425
            RideFilePoint *prevPoint = rideFile->dataPoints().back();
397
426
            int deltaSecs = (int) (secs - prevPoint->secs);
398
 
            if(deltaSecs != secs - prevPoint->secs)
399
 
                throw BadDelta(); // no fractional part
 
427
            assert(deltaSecs == secs - prevPoint->secs); // no fractional part
400
428
            // This is only true if the previous record was of type record:
401
 
            if(deltaSecs != time - last_time)
402
 
                throw BadDelta();
 
429
            assert(deltaSecs == time - last_time);
403
430
            // If the last lat/lng was missing (0/0) then all points up to lat/lng are marked as 0/0.
404
431
            if (prevPoint->lat == 0 && prevPoint->lon == 0 ) {
405
432
                badgps = 1;
415
442
            double deltaLon = lng - prevPoint->lon;
416
443
            double deltaLat = lat - prevPoint->lat;
417
444
            double deltaHeadwind = headwind - prevPoint->headwind;
418
 
            for (int i = 1; i < deltaSecs; i++) {
419
 
                double weight = 1.0 * i / deltaSecs;
420
 
                rideFile->appendPoint(
421
 
                    prevPoint->secs + (deltaSecs * weight),
422
 
                    prevPoint->cad + (deltaCad * weight),
423
 
                    prevPoint->hr + (deltaHr * weight),
424
 
                    prevPoint->km + (deltaDist * weight),
425
 
                    prevPoint->kph + (deltaSpeed * weight),
426
 
                    prevPoint->nm + (deltaTorque * weight),
427
 
                    prevPoint->watts + (deltaPower * weight),
428
 
                    prevPoint->alt + (deltaAlt * weight),
429
 
                    (badgps == 1) ? 0 : prevPoint->lon + (deltaLon * weight),
430
 
                    (badgps == 1) ? 0 : prevPoint->lat + (deltaLat * weight),
431
 
                    prevPoint->headwind + (deltaHeadwind * weight),
432
 
                    interval);
 
445
            double deltaSlope = headwind - prevPoint->slope;
 
446
            double deltaLeftRightBalance = lrbalance - prevPoint->lrbalance;
 
447
 
 
448
            // only smooth for less than 30 minutes
 
449
            // we don't want to crash / stall on bad
 
450
            // or corrupt files
 
451
            if (deltaSecs > 0 && deltaSecs < (60*30)) {
 
452
 
 
453
                for (int i = 1; i < deltaSecs; i++) {
 
454
                    double weight = 1.0 * i / deltaSecs;
 
455
                    rideFile->appendPoint(
 
456
                        prevPoint->secs + (deltaSecs * weight),
 
457
                        prevPoint->cad + (deltaCad * weight),
 
458
                        prevPoint->hr + (deltaHr * weight),
 
459
                        prevPoint->km + (deltaDist * weight),
 
460
                        prevPoint->kph + (deltaSpeed * weight),
 
461
                        prevPoint->nm + (deltaTorque * weight),
 
462
                        prevPoint->watts + (deltaPower * weight),
 
463
                        prevPoint->alt + (deltaAlt * weight),
 
464
                        (badgps == 1) ? 0 : prevPoint->lon + (deltaLon * weight),
 
465
                        (badgps == 1) ? 0 : prevPoint->lat + (deltaLat * weight),
 
466
                        prevPoint->headwind + (deltaHeadwind * weight),
 
467
                        prevPoint->slope + (deltaSlope * weight),
 
468
                        temperature,
 
469
                        prevPoint->lrbalance + (deltaLeftRightBalance * weight),
 
470
                        interval);
 
471
                }
 
472
                prevPoint = rideFile->dataPoints().back();
433
473
            }
434
 
            prevPoint = rideFile->dataPoints().back();
435
474
        }
436
475
 
437
476
        if (km < 0.00001f) km = last_distance;
438
 
        rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lng, lat, headwind, interval);
 
477
        rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lng, lat, headwind, slope, temperature, lrbalance, interval);
439
478
        last_time = time;
440
479
        last_distance = km;
441
480
    }
507
546
                    case 10: v = read_uint8z(&count); break;
508
547
                    case 11: v = read_uint16z(def.is_big_endian, &count); break;
509
548
                    case 12: v = read_uint32z(def.is_big_endian, &count); break;
510
 
                    //XXX: support float, string + byte base types
 
549
                    // we may need to add support for float, string + byte base types here
511
550
                    default:
512
551
                        read_unknown( field.size, &count );
513
552
                        v = NA_VALUE;
545
584
 
546
585
    RideFile * run() {
547
586
        rideFile = new RideFile;
548
 
        rideFile->setDeviceType("Garmin FIT"); // XXX: read from device msg?
549
 
        rideFile->setRecIntSecs(1.0); // XXX: always?
 
587
        rideFile->setDeviceType("Garmin FIT");
 
588
        rideFile->setRecIntSecs(1.0); // this is a terrible assumption!
550
589
        if (!file.open(QIODevice::ReadOnly)) {
551
590
            delete rideFile;
552
591
            return NULL;
554
593
        int header_size = read_uint8();
555
594
        if (header_size != 12 && header_size != 14) {
556
595
            errors << QString("bad header size: %1").arg(header_size);
 
596
            file.close();
557
597
            delete rideFile;
558
598
            return NULL;
559
599
        }
569
609
        char fit_str[5];
570
610
        if (file.read(fit_str, 4) != 4) {
571
611
            errors << "truncated header";
 
612
            file.close();
572
613
            delete rideFile;
573
614
            return NULL;
574
615
        }
575
616
        fit_str[4] = '\0';
576
617
        if (strcmp(fit_str, ".FIT") != 0) {
577
618
            errors << QString("bad header, expected \".FIT\" but got \"%1\"").arg(fit_str);
 
619
            file.close();
578
620
            delete rideFile;
579
621
            return NULL;
580
622
        }
590
632
        }
591
633
        catch (TruncatedRead &e) {
592
634
            errors << "truncated file body";
593
 
            delete rideFile;
594
 
            return NULL;
595
 
        }
596
 
        catch (BadDelta &e) {
597
 
            errors << "Unsupported smart recording interval found";
 
635
            file.close();
598
636
            delete rideFile;
599
637
            return NULL;
600
638
        }
601
639
        if (stop) {
 
640
            file.close();
602
641
            delete rideFile;
603
642
            return NULL;
604
643
        }
612
651
            foreach(int num, unknown_base_type)
613
652
                qDebug() << QString("FitRideFile: unknown base type %1; skipped").arg(num);
614
653
 
 
654
            file.close();
615
655
            return rideFile;
616
656
        }
617
657
    }
618
658
};
619
659
 
620
 
RideFile *FitFileReader::openRideFile(QFile &file, QStringList &errors) const
 
660
RideFile *FitFileReader::openRideFile(QFile &file, QStringList &errors, QList<RideFile*>*) const
621
661
{
622
662
    QSharedPointer<FitFileReaderState> state(new FitFileReaderState(file, errors));
623
663
    return state->run();