示例#1
0
    void compute(const RideFile *ride, const Zones *, int,
                 const HrZones *, int,
                 const QHash<QString,RideMetric*> &,
                 const Context *) {

        // LNP only makes sense for running and it needs recIntSecs > 0
        if (!ride->isRun() || ride->recIntSecs() == 0) {
            setValue(0.0);
            setCount(0);
            return;
        }

        // unconst naughty boy, get athlete's data
        RideFile *uride = const_cast<RideFile*>(ride);
        double weight = uride->getWeight();
        double height = uride->getHeight();

        int rollingwindowsize120 = 120 / ride->recIntSecs();
        int rollingwindowsize30 = 30 / ride->recIntSecs();

        double total = 0.0;
        int count = 0;

        // no point doing a rolling average if the
        // sample rate is greater than the rolling average
        // window!!
        if (rollingwindowsize30 > 1) {

            QVector<double> rollingSpeed(rollingwindowsize120);
            QVector<double> rollingSlope(rollingwindowsize120);
            QVector<double> rollingPower(rollingwindowsize30);
            int index120 = 0;
            int index30 = 0;
            double sumSpeed = 0.0;
            double sumSlope = 0.0;
            double sumPower = 0.0;
            double initial_speed = 0.0;

            // loop over the data and convert to a rolling
            // average for the given windowsize
            for (int i=0; i<ride->dataPoints().size(); i++) {

                double speed = ride->dataPoints()[i]->kph/3.6;
                sumSpeed += speed;
                sumSpeed -= rollingSpeed[index120];
                rollingSpeed[index120] = speed;
                double speed120 = sumSpeed/std::min(count+1, rollingwindowsize120); // speed rolling average

                double slope = ride->dataPoints()[i]->slope/100.0;
                sumSlope += slope;
                sumSlope -= rollingSlope[index120];
                rollingSlope[index120] = slope;
                double slope120 = sumSlope/std::min(count+1, rollingwindowsize120); // slope rolling average

                // running power based on 120sec averages
                double watts = running_power(weight, height, speed120, slope120, speed120*ride->recIntSecs(), initial_speed);
                initial_speed = speed120;

                sumPower += watts;
                sumPower -= rollingPower[index30];
                rollingPower[index30] = watts;

                total += pow(sumPower/std::min(count+1, rollingwindowsize30), 4); // raise rolling average to 4th power
                count ++;

                // move index on/round
                index120 = (index120 >= rollingwindowsize120-1) ? 0 : index120+1;
                index30 = (index30 >= rollingwindowsize30-1) ? 0 : index30+1;
            }
        }
        if (count) {
            lnp = pow(total/count, 0.25);
            secs = count * ride->recIntSecs();
        } else {
            lnp = secs = 0;
        }

        setValue(lnp);
        setCount(secs);
    }
// Refresh not up to date metrics and metrics after date
void MetricAggregator::refreshMetrics(QDateTime forceAfterThisDate)
{
    // only if we have established a connection to the database
    if (dbaccess == NULL || main->isclean==true) return;

    // first check db structure is still up to date
    // this is because metadata.xml may add new fields
    dbaccess->checkDBVersion();

    // Get a list of the ride files
    QRegExp rx = RideFileFactory::instance().rideFileRegExp();
    QStringList filenames = RideFileFactory::instance().listRideFiles(home);
    QStringListIterator i(filenames);

    // get a Hash map of statistic records and timestamps
    QSqlQuery query(dbaccess->connection());
    QHash <QString, status> dbStatus;
    bool rc = query.exec("SELECT filename, timestamp, fingerprint FROM metrics ORDER BY ride_date;");
    while (rc && query.next()) {
        status add;
        QString filename = query.value(0).toString();
        add.timestamp = query.value(1).toInt();
        add.fingerprint = query.value(2).toInt();
        dbStatus.insert(filename, add);
    }

    // begin LUW -- byproduct of turning off sync (nosync)
    dbaccess->connection().transaction();

    // Delete statistics for non-existant ride files
    QHash<QString, status>::iterator d;
    for (d = dbStatus.begin(); d != dbStatus.end(); ++d) {
        if (QFile(home.absolutePath() + "/" + d.key()).exists() == false) {
            dbaccess->deleteRide(d.key());
#ifdef GC_HAVE_LUCENE
            main->lucene->deleteRide(d.key());
#endif
        }
    }

    unsigned long zoneFingerPrint = static_cast<unsigned long>(zones->getFingerprint())
                                  + static_cast<unsigned long>(hrzones->getFingerprint()); // checksum of *all* zone data (HR and Power)

    // update statistics for ride files which are out of date
    // showing a progress bar as we go
    QTime elapsed;
    elapsed.start();
    QString title = tr("Refreshing Ride Statistics...\nStarted");
    QProgressDialog bar(title, tr("Abort"), 0, filenames.count(), main);
    bar.setWindowModality(Qt::WindowModal);
    bar.setMinimumDuration(0);
    bar.show();

    int processed=0;
    QApplication::processEvents(); // get that dialog up!

    // log of progress
    QFile log(home.absolutePath() + "/" + "metric.log");
    log.open(QIODevice::WriteOnly);
    log.resize(0);
    QTextStream out(&log);
    out << "METRIC REFRESH STARTS: " << QDateTime::currentDateTime().toString() + "\r\n";

    while (i.hasNext()) {
        QString name = i.next();
        QFile file(home.absolutePath() + "/" + name);

        // if it s missing or out of date then update it!
        status current = dbStatus.value(name);
        unsigned long dbTimeStamp = current.timestamp;
        unsigned long fingerprint = current.fingerprint;

        RideFile *ride = NULL;

        // update progress bar
        long elapsedtime = elapsed.elapsed();
        QString elapsedString = QString("%1:%2:%3").arg(elapsedtime/3600000,2)
                                                .arg((elapsedtime%3600000)/60000,2,10,QLatin1Char('0'))
                                                .arg((elapsedtime%60000)/1000,2,10,QLatin1Char('0'));
        QString title = tr("Refreshing Ride Statistics...\nElapsed: %1\n%2").arg(elapsedString).arg(name);
        bar.setLabelText(title);
        bar.setValue(++processed);
        QApplication::processEvents();

        if (dbTimeStamp < QFileInfo(file).lastModified().toTime_t() ||
            zoneFingerPrint != fingerprint ||
            (!forceAfterThisDate.isNull() && name >= forceAfterThisDate.toString("yyyy_MM_dd_hh_mm_ss"))) {
            QStringList errors;

            // log
            out << "Opening ride: " << name << "\r\n";

            // read file and process it if we didn't already...
            if (ride == NULL) ride = RideFileFactory::instance().openRideFile(main, file, errors);

            out << "File open completed: " << name << "\r\n";

            if (ride != NULL) {

                out << "Getting weight: " << name << "\r\n";
                ride->getWeight();
                out << "Updating statistics: " << name << "\r\n";
                importRide(home, ride, name, zoneFingerPrint, (dbTimeStamp > 0));

            }
        }

        // update cache (will check timestamps itself)
        // if ride wasn't opened it will do it itself
        // we only want to check so passing check=true
        // because we don't actually want the results now
        RideFileCache updater(main, home.absolutePath() + "/" + name, ride, true);

        // free memory - if needed
        if (ride) delete ride;

        if (bar.wasCanceled()) {
            out << "METRIC REFRESH CANCELLED\r\n";
            break;
        }
    }

    // end LUW -- now syncs DB
    out << "COMMIT: " << QDateTime::currentDateTime().toString() + "\r\n";
    dbaccess->connection().commit();

#ifdef GC_HAVE_LUCENE
#ifndef WIN32 // windows crashes here....
    out << "OPTIMISE: " << QDateTime::currentDateTime().toString() + "\r\n";
    main->lucene->optimise();
#endif
#endif
    main->isclean = true;

    // stop logging
    out << "SIGNAL DATA CHANGED: " << QDateTime::currentDateTime().toString() + "\r\n";
    dataChanged(); // notify models/views

    out << "METRIC REFRESH ENDS: " << QDateTime::currentDateTime().toString() + "\r\n";
    log.close();
}
示例#3
0
void
PowerHist::setData(RideItem *_rideItem, bool force)
{
    // predefined deltas for each series
    static const double wattsDelta = 1.0;
    static const double wattsKgDelta = 0.01;
    static const double nmDelta    = 0.1;
    static const double hrDelta    = 1.0;
    static const double kphDelta   = 0.1;
    static const double cadDelta   = 1.0;

    source = Ride;

    // we set with this data already
    if (force == false && _rideItem == LASTrideItem && source == LASTsource) return;

    rideItem = _rideItem;
    if (!rideItem) return;

    RideFile *ride = rideItem->ride();

    bool hasData = ((series == RideFile::watts || series == RideFile::wattsKg) && ride->areDataPresent()->watts) ||
                   (series == RideFile::nm && ride->areDataPresent()->nm) ||
                   (series == RideFile::kph && ride->areDataPresent()->kph) ||
                   (series == RideFile::cad && ride->areDataPresent()->cad) ||
                   (series == RideFile::aPower && ride->areDataPresent()->apower) ||
                   (series == RideFile::hr && ride->areDataPresent()->hr);

    if (ride && hasData) {
        //setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));

        static const int maxSize = 4096;

        // recording interval in minutes
        dt = ride->recIntSecs() / 60.0;

        wattsArray.resize(0);
        wattsZoneArray.resize(0);
        wattsKgArray.resize(0);
        aPowerArray.resize(0);
        nmArray.resize(0);
        hrArray.resize(0);
        hrZoneArray.resize(0);
        kphArray.resize(0);
        cadArray.resize(0);

        wattsSelectedArray.resize(0);
        wattsZoneSelectedArray.resize(0);
        wattsKgSelectedArray.resize(0);
        aPowerSelectedArray.resize(0);
        nmSelectedArray.resize(0);
        hrSelectedArray.resize(0);
        hrZoneSelectedArray.resize(0);
        kphSelectedArray.resize(0);
        cadSelectedArray.resize(0);

        // unit conversion factor for imperial units for selected parameters
        double torque_factor = (context->athlete->useMetricUnits ? 1.0 : 0.73756215);
        double speed_factor  = (context->athlete->useMetricUnits ? 1.0 : 0.62137119);

        foreach(const RideFilePoint *p1, ride->dataPoints()) {
            bool selected = isSelected(p1, ride->recIntSecs());

            // watts array
            int wattsIndex = int(floor(p1->watts / wattsDelta));
            if (wattsIndex >= 0 && wattsIndex < maxSize) {
                if (wattsIndex >= wattsArray.size())
                    wattsArray.resize(wattsIndex + 1);
                wattsArray[wattsIndex]++;

                if (selected) {
                    if (wattsIndex >= wattsSelectedArray.size())
                        wattsSelectedArray.resize(wattsIndex + 1);
                    wattsSelectedArray[wattsIndex]++;
                }
            }

            // watts zoned array
            const Zones *zones = rideItem->zones;
            int zoneRange = zones ? zones->whichRange(ride->startTime().date()) : -1;

            // Only calculate zones if we have a valid range and check zeroes
            if (zoneRange > -1 && (withz || (!withz && p1->watts))) {
                wattsIndex = zones->whichZone(zoneRange, p1->watts);

                if (wattsIndex >= 0 && wattsIndex < maxSize) {
                    if (wattsIndex >= wattsZoneArray.size())
                        wattsZoneArray.resize(wattsIndex + 1);
                    wattsZoneArray[wattsIndex]++;

                    if (selected) {
                        if (wattsIndex >= wattsZoneSelectedArray.size())
                            wattsZoneSelectedArray.resize(wattsIndex + 1);
                        wattsZoneSelectedArray[wattsIndex]++;
                    }
                }
            }

            // aPower array
            int aPowerIndex = int(floor(p1->apower / wattsDelta));
            if (aPowerIndex >= 0 && aPowerIndex < maxSize) {
                if (aPowerIndex >= aPowerArray.size())
                    aPowerArray.resize(aPowerIndex + 1);
                aPowerArray[aPowerIndex]++;

                if (selected) {
                    if (aPowerIndex >= aPowerSelectedArray.size())
                        aPowerSelectedArray.resize(aPowerIndex + 1);
                    aPowerSelectedArray[aPowerIndex]++;
                }
            }

            // wattsKg array
            int wattsKgIndex = int(floor(p1->watts / ride->getWeight() / wattsKgDelta));
            if (wattsKgIndex >= 0 && wattsKgIndex < maxSize) {
                if (wattsKgIndex >= wattsKgArray.size())
                    wattsKgArray.resize(wattsKgIndex + 1);
                wattsKgArray[wattsKgIndex]++;

                if (selected) {
                    if (wattsKgIndex >= wattsKgSelectedArray.size())
                        wattsKgSelectedArray.resize(wattsKgIndex + 1);
                    wattsKgSelectedArray[wattsKgIndex]++;
                }
            }

            int nmIndex = int(floor(p1->nm * torque_factor / nmDelta));
            if (nmIndex >= 0 && nmIndex < maxSize) {
                if (nmIndex >= nmArray.size())
                    nmArray.resize(nmIndex + 1);
                nmArray[nmIndex]++;

                if (selected) {
                    if (nmIndex >= nmSelectedArray.size())
                        nmSelectedArray.resize(nmIndex + 1);
                    nmSelectedArray[nmIndex]++;
                }
            }

            int hrIndex = int(floor(p1->hr / hrDelta));
            if (hrIndex >= 0 && hrIndex < maxSize) {
                if (hrIndex >= hrArray.size())
                    hrArray.resize(hrIndex + 1);
                hrArray[hrIndex]++;

                if (selected) {
                    if (hrIndex >= hrSelectedArray.size())
                        hrSelectedArray.resize(hrIndex + 1);
                    hrSelectedArray[hrIndex]++;
                }
            }

            // hr zoned array
            int hrZoneRange = context->athlete->hrZones() ? context->athlete->hrZones()->whichRange(ride->startTime().date()) : -1;

            // Only calculate zones if we have a valid range
            if (hrZoneRange > -1 && (withz || (!withz && p1->hr))) {
                hrIndex = context->athlete->hrZones()->whichZone(hrZoneRange, p1->hr);

                if (hrIndex >= 0 && hrIndex < maxSize) {
                    if (hrIndex >= hrZoneArray.size())
                        hrZoneArray.resize(hrIndex + 1);
                    hrZoneArray[hrIndex]++;

                    if (selected) {
                        if (hrIndex >= hrZoneSelectedArray.size())
                            hrZoneSelectedArray.resize(hrIndex + 1);
                        hrZoneSelectedArray[hrIndex]++;
                    }
                }
            }

            int kphIndex = int(floor(p1->kph * speed_factor / kphDelta));
            if (kphIndex >= 0 && kphIndex < maxSize) {
                if (kphIndex >= kphArray.size())
                    kphArray.resize(kphIndex + 1);
                kphArray[kphIndex]++;

                if (selected) {
                    if (kphIndex >= kphSelectedArray.size())
                        kphSelectedArray.resize(kphIndex + 1);
                    kphSelectedArray[kphIndex]++;
                }
            }

            int cadIndex = int(floor(p1->cad / cadDelta));
            if (cadIndex >= 0 && cadIndex < maxSize) {
                if (cadIndex >= cadArray.size())
                    cadArray.resize(cadIndex + 1);
                cadArray[cadIndex]++;

                if (selected) {
                    if (cadIndex >= cadSelectedArray.size())
                        cadSelectedArray.resize(cadIndex + 1);
                    cadSelectedArray[cadIndex]++;
                }
            }
        }

    } else {
示例#4
0
// Refresh not up to date metrics and metrics after date
void MetricAggregator::refreshMetrics(QDateTime forceAfterThisDate)
{
    // only if we have established a connection to the database
    if (dbaccess == NULL || context->athlete->isclean==true) return;

    // first check db structure is still up to date
    // this is because metadata.xml may add new fields
    dbaccess->checkDBVersion();

    // Get a list of the ride files
    QRegExp rx = RideFileFactory::instance().rideFileRegExp();
    QStringList filenames = RideFileFactory::instance().listRideFiles(context->athlete->home);
    QStringListIterator i(filenames);

    // get a Hash map of statistic records and timestamps
    QSqlQuery query(dbaccess->connection());
    QHash <QString, status> dbStatus;
    bool rc = query.exec("SELECT filename, crc, timestamp, fingerprint FROM metrics ORDER BY ride_date;");
    while (rc && query.next()) {
        status add;
        QString filename = query.value(0).toString();
        add.crc = query.value(1).toInt();
        add.timestamp = query.value(2).toInt();
        add.fingerprint = query.value(3).toInt();
        dbStatus.insert(filename, add);
    }

    // begin LUW -- byproduct of turning off sync (nosync)
    dbaccess->connection().transaction();

    // Delete statistics for non-existant ride files
    QHash<QString, status>::iterator d;
    for (d = dbStatus.begin(); d != dbStatus.end(); ++d) {
        if (QFile(context->athlete->home.absolutePath() + "/" + d.key()).exists() == false) {
            dbaccess->deleteRide(d.key());
#ifdef GC_HAVE_LUCENE
            context->athlete->lucene->deleteRide(d.key());
#endif
        }
    }

    unsigned long zoneFingerPrint = static_cast<unsigned long>(context->athlete->zones()->getFingerprint(context))
                                  + static_cast<unsigned long>(context->athlete->hrZones()->getFingerprint()); // checksum of *all* zone data (HR and Power)

    // update statistics for ride files which are out of date
    // showing a progress bar as we go
    QTime elapsed;
    elapsed.start();
    QString title = context->athlete->cyclist;
    GProgressDialog *bar = NULL;

    int processed=0;
    int updates=0;
    QApplication::processEvents(); // get that dialog up!

    // log of progress
    QFile log(context->athlete->home.absolutePath() + "/" + "metric.log");
    log.open(QIODevice::WriteOnly);
    log.resize(0);
    QTextStream out(&log);
    out << "METRIC REFRESH STARTS: " << QDateTime::currentDateTime().toString() + "\r\n";

    while (i.hasNext()) {
        QString name = i.next();
        QFile file(context->athlete->home.absolutePath() + "/" + name);

        // if it s missing or out of date then update it!
        status current = dbStatus.value(name);
        unsigned long dbTimeStamp = current.timestamp;
        unsigned long crc = current.crc;
        unsigned long fingerprint = current.fingerprint;

        RideFile *ride = NULL;

        processed++;

        // create the dialog if we need to show progress for long running uodate
        long elapsedtime = elapsed.elapsed();
        if ((!forceAfterThisDate.isNull() || first || elapsedtime > 6000) && bar == NULL) {
            bar = new GProgressDialog(title, 0, filenames.count(), context->mainWindow->init, context->mainWindow);
            bar->show(); // lets hide until elapsed time is > 6 seconds

            // lets make sure it goes to the center!
            QApplication::processEvents();
        }

        // update the dialog always after 6 seconds
        if (!forceAfterThisDate.isNull() || first || elapsedtime > 6000) {

            // update progress bar
            QString elapsedString = QString("%1:%2:%3").arg(elapsedtime/3600000,2)
                                                .arg((elapsedtime%3600000)/60000,2,10,QLatin1Char('0'))
                                                .arg((elapsedtime%60000)/1000,2,10,QLatin1Char('0'));
            QString title = tr("Update Statistics\nElapsed: %1\n\n%2").arg(elapsedString).arg(name);
            bar->setLabelText(title);
            bar->setValue(processed);
        }
        QApplication::processEvents();

        if (dbTimeStamp < QFileInfo(file).lastModified().toTime_t() ||
            zoneFingerPrint != fingerprint ||
            (!forceAfterThisDate.isNull() && name >= forceAfterThisDate.toString("yyyy_MM_dd_hh_mm_ss"))) {
            QStringList errors;

            // ooh we have one to update -- lets check the CRC in case
            // its actually unchanged since last time and the timestamps
            // have been mucked up by dropbox / file copying / backups etc

            // but still update if we're doing this because settings changed not the ride!
            QString fullPath =  QString(context->athlete->home.absolutePath()) + "/" + name;
            if ((crc == 0 || crc != DBAccess::computeFileCRC(fullPath)) ||
                zoneFingerPrint != fingerprint ||
                (!forceAfterThisDate.isNull() && name >= forceAfterThisDate.toString("yyyy_MM_dd_hh_mm_ss"))) {

                // log
                out << "Opening ride: " << name << "\r\n";

                // read file and process it if we didn't already...
                if (ride == NULL) ride = RideFileFactory::instance().openRideFile(context, file, errors);

                out << "File open completed: " << name << "\r\n";

                if (ride != NULL) {

                    out << "Getting weight: " << name << "\r\n";
                    ride->getWeight();
                    out << "Updating statistics: " << name << "\r\n";
                    importRide(context->athlete->home, ride, name, zoneFingerPrint, (dbTimeStamp > 0));

                }
                updates++;
            }
        }

        // update cache (will check timestamps itself)
        // if ride wasn't opened it will do it itself
        // we only want to check so passing check=true
        // because we don't actually want the results now
        // it will also check the file CRC as well as timestamps
        RideFileCache updater(context, context->athlete->home.absolutePath() + "/" + name, ride, true);

        // free memory - if needed
        if (ride) delete ride;

        // for model run...
        // RideFileCache::meanMaxPowerFor(context, context->athlete->home.absolutePath() + "/" + name);

        if (bar && bar->wasCanceled()) {
            out << "METRIC REFRESH CANCELLED\r\n";
            break;
        }
    }


    // end LUW -- now syncs DB
    out << "COMMIT: " << QDateTime::currentDateTime().toString() + "\r\n";
    dbaccess->connection().commit();

#ifdef GC_HAVE_LUCENE
#ifndef WIN32 // windows crashes here....
    out << "OPTIMISE: " << QDateTime::currentDateTime().toString() + "\r\n";
    context->athlete->lucene->optimise();
#endif
#endif
    context->athlete->isclean = true;

    // clear out the estimates if something changed!
    if (updates) context->athlete->PDEstimates.clear();

    // now zap the progress bar
    if (bar) delete bar;

    // stop logging
    out << "SIGNAL DATA CHANGED: " << QDateTime::currentDateTime().toString() + "\r\n";
    dataChanged(); // notify models/views

    out << "METRIC REFRESH ENDS: " << QDateTime::currentDateTime().toString() + "\r\n";
    log.close();

    first = false;
}