diff options
Diffstat (limited to 'libktorrent/torrent/timeestimator.cpp')
-rw-r--r-- | libktorrent/torrent/timeestimator.cpp | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/libktorrent/torrent/timeestimator.cpp b/libktorrent/torrent/timeestimator.cpp new file mode 100644 index 0000000..7d18300 --- /dev/null +++ b/libktorrent/torrent/timeestimator.cpp @@ -0,0 +1,278 @@ +/*************************************************************************** + * Copyright (C) 2006 by Ivan Vasić * + * ivasic@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ +#include <math.h> +#include "timeestimator.h" +#include "torrentcontrol.h" +#include "settings.h" + +#include <torrent/globals.h> +#include <util/log.h> +#include <util/constants.h> + +using namespace kt; + +namespace bt +{ + TimeEstimator::TimeEstimator(TorrentControl* tc) + : m_tc(tc) + { + m_samples = new SampleQueue(20); + m_lastAvg = 0; + m_perc = -1; + + //default is KT algorithm + m_algorithm = (ETAlgorithm) Settings::eta(); + } + + + TimeEstimator::~TimeEstimator() + { + delete m_samples; + } + + Uint32 TimeEstimator::estimate() + { + const TorrentStats& s = m_tc->getStats(); + + // only estimate when we are downloading or stalled + if (!(s.status == kt::DOWNLOADING || s.status == kt::STALLED)) + return (Uint32) - 1; + + //ones without pre-calculations + switch (m_algorithm) + { + + case ETA_CSA: + return estimateCSA(); + + case ETA_GASA: + return estimateGASA(); + + case ETA_KT: + return estimateKT(); + } + + //complicated ones :) + + Uint32 sample = (Uint32) s.download_rate; + //push new sample + m_samples->push(sample); + + + switch (m_algorithm) + { + case ETA_MAVG: + return estimateMAVG(); + + case ETA_WINX: + return estimateWINX(); + + default: + return -1; + } + } + + Uint32 TimeEstimator::estimateCSA() + { + const TorrentStats& s = m_tc->getStats(); + + if (s.download_rate == 0) + return (Uint32) - 1; + + return (int)floor((float)s.bytes_left_to_download / (float)s.download_rate); + } + + Uint32 TimeEstimator::estimateGASA() + { + const TorrentStats& s = m_tc->getStats(); + + if (m_tc->getRunningTimeDL() > 0 && s.bytes_downloaded > 0) + { + double avg_speed = (double) s.bytes_downloaded / (double) m_tc->getRunningTimeDL(); + return (Uint32) floor((double) s.bytes_left_to_download / avg_speed); + } + + return (Uint32) - 1; + } + + Uint32 TimeEstimator::estimateWINX() + { + const TorrentStats& s = m_tc->getStats(); + + if (m_samples->sum() > 0 && m_samples->count() > 0) + return (Uint32) floor((double) s.bytes_left_to_download / ((double) m_samples->sum() / (double) m_samples->count())); + + return (Uint32) - 1; + } + + Uint32 TimeEstimator::estimateMAVG() + { + const TorrentStats& s = m_tc->getStats(); + + if (m_samples->count() > 0) + { + double lavg; + + if (m_lastAvg == 0) + lavg = (Uint32) m_samples->sum() / m_samples->count(); + else + lavg = m_lastAvg - ((double) m_samples->first() / (double) m_samples->count()) + ((double) m_samples->last() / (double) m_samples->count()); + + m_lastAvg = (Uint32) floor(lavg); + + if (lavg > 0) + return (Uint32) floor((double) s.bytes_left_to_download / ((lavg + (m_samples->sum() / m_samples->count())) / 2)); + + return (Uint32) - 1; + } + + return (Uint32) - 1; + } + +} + +bt::SampleQueue::SampleQueue(int max) + : m_size(max), m_count(0) +{ + m_samples = new Uint32[max]; + + for (int i = 0; i < m_size; ++i) + m_samples[i] = 0; + + m_end = -1; + + m_start = 0; +} + +bt::SampleQueue::~ SampleQueue() +{ + delete [] m_samples; +} + +void bt::SampleQueue::push(Uint32 sample) +{ + if (m_count < m_size) + { + //it's not full yet + m_samples[(++m_end) % m_size ] = sample; + m_count++; + + return; + } + + //since it's full I'll just replace the oldest value with new one and update all variables. + m_end = (++m_end) % m_size; + + m_start = (++m_start) % m_size; + + m_samples[m_end] = sample; +} + +Uint32 bt::SampleQueue::first() +{ + return m_samples[m_start]; +} + +Uint32 bt::SampleQueue::last() +{ + return m_samples[m_end]; +} + +bool bt::SampleQueue::isFull() +{ + return m_count >= m_size; +} + +int bt::SampleQueue::count() +{ + return m_count; +} + +Uint32 bt::SampleQueue::sum() +{ + Uint32 s = 0; + + for (int i = 0; i < m_count; ++i) + s += m_samples[i]; + + return s; +} + +void bt::TimeEstimator::setAlgorithm(const ETAlgorithm& theValue) +{ + m_algorithm = theValue; +} + +Uint32 bt::TimeEstimator::estimateKT() +{ + const TorrentStats& s = m_tc->getStats(); + + Uint32 sample = (Uint32) s.download_rate; + + //push new sample + m_samples->push(sample); + + double perc = (double) s.bytes_downloaded / (double) s.total_bytes; + + int percentage = (int)(perc) * 100; + + //calculate percentage increasement + double delta = 1 - 1 / (perc / m_perc); + + //remember last percentage + m_perc = perc; + + + if (s.bytes_downloaded < 1024*1024*100 && sample > 0) // < 100KB + { + m_lastETA = estimateGASA(); + return m_lastETA; + } + + if (percentage >= 99 && sample > 0 && s.bytes_left_to_download <= 10*1024*1024*1024) //1% of a very large torrent could be hundreds of MB so limit it to 10MB + { + + if (!m_samples->isFull()) + { + m_lastETA = estimateWINX(); + + if (m_lastETA == (Uint32) - 1) + m_lastETA = estimateGASA(); + + return m_lastETA; + } + else + { + m_lastETA = (Uint32) - 1; + + if (delta > 0.0001) + m_lastETA = estimateMAVG(); + + if (m_lastETA == (Uint32) - 1) + m_lastETA = estimateGASA(); + } + + return m_lastETA; + } + + m_lastETA = estimateGASA(); + + return m_lastETA; +} |