Newer
Older
/*===================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief Implementation of class LogCompressor
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <QFile>
#include <QTextStream>
#include <QStringList>
/**
* It will only get active upon calling startCompression()
*/
LogCompressor::LogCompressor(QString logFileName, QString outFileName, int uasid) :
outFileName(outFileName),
running(true),
currentDataLine(0),
dataLines(1),
uasid(uasid)
{
}
void LogCompressor::run()
{
QString separator = "\t";
QString fileName = logFileName;
QFile file(fileName);
QFile outfile(outFileName);
QList<quint64> times;// = new QList<quint64>();
QList<quint64> finalTimes;
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
if (outFileName != "")
{
// Check if file is writeable
if (!QFileInfo(outfile).isWritable())
{
return;
}
}
// Search only a certain region, assuming that not more
// than N dimensions at H Hertz can be send
const unsigned int keySearchLimit = 15000;
// e.g. 500 Hz * 30 values or
// e.g. 100 Hz * 150 values
unsigned int keyCounter = 0;
while (!in.atEnd() && keyCounter < keySearchLimit) {
QString line = in.readLine();
// Accumulate map of keys
// Data field name is at position 2
QString key = line.split(separator).at(2);
if (!keys->contains(key)) keys->append(key);
QString header = "";
QString spacer = "";
for (int i = 0; i < keys->length(); i++)
{
header += keys->at(i) + separator;
spacer += " " + separator;
}
emit logProcessingStatusChanged(tr("Log compressor: Dataset contains dimension: ") + header);
//qDebug() << "NOW READING TIMES";
// Find all times
//in.reset();
file.reset();
in.reset();
in.resetStatus();
// Data field name is at position 2b
quint64 time = static_cast<QString>(line.split(separator).at(0)).toLongLong(&ok);
if (ok)
qSort(times);
qint64 lastTime = -1;
if (times.at(i) != lastTime)
{
outLines->append(QString("%1").arg(times.at(i)) + separator + spacer);
lastTime = static_cast<qint64>(times.at(i));
finalTimes.append(times.at(i));
//qDebug() << "ADDED:" << outLines->last();
}
dataLines = finalTimes.length();
emit logProcessingStatusChanged(tr("Log compressor: Now processing %1 log lines").arg(finalTimes.length()));
// Fill in the values for all keys
file.reset();
QTextStream data(&file);
quint64 lastTimeIndex = 0;
while (!data.atEnd())
{
linecounter++;
currentDataLine = linecounter;
QString line = data.readLine();
QStringList parts = line.split(separator);
// Get time
quint64 time = static_cast<QString>(parts.first()).toLongLong(&ok);
// Enforce NaN if no value is present
if (value.length() == 0 || value == "" || value == " " || value == "\t" || value == "\n")
{
value = "NaN";
}
// Constraining the search area might result in not finding a key,
// but it significantly reduces the time needed for the search
// setting a window of 1000 entries means that a 1 Hz data point
// can still be located
quint64 offset;
qint64 index = -1;
failed = false;
// Search the index until it is valid (!= -1)
// or the start of the list has been reached (failed)
while (index == -1 && !failed)
{
if (lastTimeIndex > offsetLimit)
{
offset = lastTimeIndex - offsetLimit;
}
else
{
offset = 0;
}
if (index == -1)
{
if (offset == 0)
{
emit logProcessingStatusChanged(tr("Log compressor: Timestamp %1 not found in dataset, ignoring log line %2").arg(time).arg(linecounter));
//continue;
failed = true;
}
else
{
emit logProcessingStatusChanged(tr("Log compressor: Timestamp %1 not found in dataset, restarting search.").arg(time));
offsetLimit*=2;
}
}
}
if (index % (dataLines/10) == 0) emit logProcessingStatusChanged(tr("Log compressor: Processed %1%% of %2 lines").arg(index/(float)dataLines).arg(dataLines));
// When the algorithm reaches here the correct index was found
lastTimeIndex = index;
QString outLine = outLines->at(index);
QStringList outParts = outLine.split(separator);
// Replace measurement placeholder with current value
outParts.replace(keys->indexOf(field)+1, value);
outLine = outParts.join(separator);
outLines->replace(index, outLine);
}
if (outFileName == "")
{
QFile::remove(file.fileName());
outfile.setFileName(file.fileName());
}
if (!outfile.open(QIODevice::WriteOnly | QIODevice::Text))
outfile.write(QString(QString("unix_timestamp") + separator + header.replace(" ", "_") + QString("\n")).toLatin1());
//QString fileHeader = QString("unix_timestamp") + header.replace(" ", "_") + QString("\n");
for (int i = 0; i < outLines->length(); i++)
{
//qDebug() << outLines->at(i);
outfile.write(QString(outLines->at(i) + "\n").toLatin1());
currentDataLine = 0;
dataLines = 1;
emit logProcessingStatusChanged(tr("Log compressor: Finished processing file: %1").arg(outfile.fileName()));
//qDebug() << "Done with logfile processing";
emit finishedFile(outfile.fileName());
running = false;
}
void LogCompressor::startCompression()
{
start();
}
bool LogCompressor::isFinished()
{
return !running;
}
int LogCompressor::getCurrentLine()
{
return currentDataLine;
}
int LogCompressor::getDataLines()
{
return dataLines;