/*
* Copyright (C) 2013 - Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 or 3.0
* of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the applicable version of the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of both the GNU Lesser General Public
* License along with this program. If not, see
*
* Authored by: Daniel d'Andrada
*/
#include "AxisVelocityCalculator.h"
#include
using namespace UbuntuGestures;
AxisVelocityCalculator::AxisVelocityCalculator(QObject *parent)
: AxisVelocityCalculator(SharedTimeSource(new RealTimeSource), parent)
{
}
AxisVelocityCalculator::AxisVelocityCalculator(const SharedTimeSource &timeSource,
QObject *parent)
: QObject(parent)
, m_timeSource(timeSource)
, m_trackedPosition(0.0)
{
reset();
}
AxisVelocityCalculator::~AxisVelocityCalculator()
{
}
qreal AxisVelocityCalculator::trackedPosition() const
{
return m_trackedPosition;
}
void AxisVelocityCalculator::setTrackedPosition(qreal newPosition)
{
processMovement(newPosition - m_trackedPosition);
if (newPosition != m_trackedPosition) {
m_trackedPosition = newPosition;
Q_EMIT trackedPositionChanged(newPosition);
}
}
void AxisVelocityCalculator::updateIdleTime()
{
processMovement(0);
}
void AxisVelocityCalculator::processMovement(qreal movement)
{
if (m_samplesRead == -1) {
m_samplesRead = m_samplesWrite;
} else if (m_samplesRead == m_samplesWrite) {
/* the oldest value is going to be overwritten.
so now the oldest will be the next one. */
m_samplesRead = (m_samplesRead + 1) % MAX_SAMPLES;
}
m_samples[m_samplesWrite].mov = movement;
m_samples[m_samplesWrite].time = m_timeSource->msecsSinceReference();
m_samplesWrite = (m_samplesWrite + 1) % MAX_SAMPLES;
}
qreal AxisVelocityCalculator::calculate()
{
if (numSamples() < MIN_SAMPLES_NEEDED) {
return 0.0;
}
updateIdleTime(); // consider the time elapsed since the last update and now
int lastIndex;
if (m_samplesWrite == 0) {
lastIndex = MAX_SAMPLES - 1;
} else {
lastIndex = m_samplesWrite - 1;
}
qint64 currTime = m_samples[lastIndex].time;
qreal totalTime = 0;
qreal totalDistance = 0;
int sampleIndex = (m_samplesRead + 1) % MAX_SAMPLES;
qint64 previousTime = m_samples[m_samplesRead].time;
while (sampleIndex != m_samplesWrite) {
// Skip this sample if it's too old
if (currTime - m_samples[sampleIndex].time <= AGE_OLDEST_SAMPLE) {
int deltaTime = m_samples[sampleIndex].time - previousTime;
totalDistance += m_samples[sampleIndex].mov;
totalTime += deltaTime;
}
previousTime = m_samples[sampleIndex].time;
sampleIndex = (sampleIndex + 1) % MAX_SAMPLES;
}
return totalDistance / totalTime;
}
void AxisVelocityCalculator::reset()
{
m_samplesRead = -1;
m_samplesWrite = 0;
}
int AxisVelocityCalculator::numSamples() const
{
if (m_samplesRead == -1) {
return 0;
} else {
if (m_samplesWrite == 0) {
/* consider only what's to the right of m_samplesRead (including himself) */
return MAX_SAMPLES - m_samplesRead;
} else if (m_samplesWrite == m_samplesRead) {
return MAX_SAMPLES; /* buffer is full */
} else if (m_samplesWrite < m_samplesRead) {
return (MAX_SAMPLES - m_samplesRead) + m_samplesWrite;
} else {
return m_samplesWrite - m_samplesRead;
}
}
}
void AxisVelocityCalculator::setTimeSource(const SharedTimeSource &timeSource)
{
m_timeSource = timeSource;
if (numSamples() > 0) {
qWarning("AxisVelocityCalculator: changing time source while there are samples present.");
// Any existent samples are based on the old time source and are, therefore, incompatible
// with this new one.
reset();
}
}