/*
openaltimeter -- an open-source altimeter for RC aircraft
Copyright (C) 2010-2011 Jony Hudson, Jan Steidl, mycarda
http://openaltimeter.org
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 3 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, see <http://www.gnu.org/licenses/>.
*/
package org.openaltimeter.data;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
public class FlightLog {
public ArrayList<LogEntry> logData = new ArrayList<LogEntry>();
public double logInterval = 0.5;
// altitudes are initially calculated from the pressure data, but can be
// subsequently modified by analysis functions. Altitudes are stored in meters.
public double[] altitudes;
private static final int BASE_PRESSURE_SAMPLES = 20;
public static final long PRESSURE_EMPTY_DATA = -1;
public void add(LogEntry entry) {
logData.add(entry);
}
// method for calculating base pressure for data starting at startIndex
public double calculateBasePressure(int startIndex)
{
int count = 0;
double basePressure = 0;
for (int i = startIndex; (i < startIndex + BASE_PRESSURE_SAMPLES) && (i < logData.size()); i++)
{
LogEntry le = logData.get(i);
if (le.pressure != PRESSURE_EMPTY_DATA) {
count++;
basePressure += le.pressure;
}
else
break;
}
return basePressure / count;
}
// this method takes the raw log data and converts it into altitude data
// the curious-looking logic in the middle makes sure that the base pressure is
// reset whenever an EOF marker is encountered.
public void calculateAltitudes()
{
double basePressure = 0;
for (int i = 0; i < logData.size(); i++)
{
LogEntry le = logData.get(i);
if (le.pressure != PRESSURE_EMPTY_DATA) {
if (basePressure == 0)
basePressure = calculateBasePressure(i);
le.altitude = AltitudeConverter.altitudeMFromPressure(le.pressure, basePressure);
}
else
basePressure = 0;
}
int numPoints = logData.size();
altitudes = new double[numPoints];
for (int i = 0; i < numPoints; i++)
altitudes[i] = logData.get(i).pressure != PRESSURE_EMPTY_DATA ? logData.get(i).altitude : 0;
}
public double[] getAltitude() {
return altitudes;
}
public void setAltitude(double[] altData) {
altitudes = altData;
}
public double[] getBattery()
{
// when an end-of-file is encountered, return the last valid battery reading rather than zero
int numPoints = logData.size();
double[] data = new double[numPoints];
// just in case there is only one data point and that is PRESSURE_EMPTY_DATA (in which case 0.0 is a reasonable value)
double lastVoltage = 0.0;
for (int i = 0; i < numPoints; i++)
{
if (logData.get(i).pressure != PRESSURE_EMPTY_DATA)
{
data[i] = logData.get(i).battery;
lastVoltage = data[i];
}
else
{
// if there is no real data, use the last good value
data[i] = lastVoltage;
}
}
return data;
}
public double[] getTemperature() {
// when an end-of-file is encountered, return the last valid temperature reading rather than zero
int numPoints = logData.size();
double[] data = new double[numPoints];
// just in case there is only one data point and that is PRESSURE_EMPTY_DATA (in which case 0.0 is a reasonable value)
double lastTemperature = 0.0;
for (int i = 0; i < numPoints; i++)
{
if (logData.get(i).pressure != PRESSURE_EMPTY_DATA)
{
data[i] = logData.get(i).temperature;
lastTemperature = data[i];
}
else
{
// if there is no real data, use the last good value
data[i] = lastTemperature;
}
}
return data;
}
public double[] getServo()
{
int numPoints = logData.size();
double[] data = new double[numPoints];
for (int i = 0; i < numPoints; i++) data[i] = logData.get(i).pressure != PRESSURE_EMPTY_DATA ? logData.get(i).servo : 0;
return data;
}
// returns the _indices_ of the end of file markers
public List<Integer> getEOFIndices() {
int numPoints = logData.size();
List<Integer> data = new ArrayList<Integer>();
for (int i = 0; i < numPoints; i++) if (logData.get(i).pressure == PRESSURE_EMPTY_DATA) data.add(i);
return data;
}
public String rawDataToString(int lower, int upper) {
if (lower < 0) lower = 0;
if (upper > logData.size() - 1) upper = logData.size() - 1;
StringBuilder sb = new StringBuilder();
// a simple header has the logging interval in it
sb.append("#logInterval: " + logInterval + "\r\n");
for (int i = lower; i < upper; i++) sb.append(logData.get(i).rawDataToString() + "\r\n");
return sb.toString();
}
public String rawDataToString() {
return rawDataToString(0, logData.size());
}
// this mangles the data into the upload format - pretty cheezy hack
public String rawDataToUploadString(int lower, int upper) {
if (lower < 0) lower = 0;
if (upper > logData.size() - 1) upper = logData.size() - 1;
StringBuilder sb = new StringBuilder();
for (int i = lower; i < upper; i++) sb.append(logData.get(i).rawDataToUploadString() + "\r\n");
return sb.toString();
}
public void fromRawData(String rawData) throws IOException {
BufferedReader br = new BufferedReader(new StringReader(rawData));
Vector<String> headerLines = new Vector<String>();
String line;
while ((line = br.readLine()) != null) {
if (line.startsWith("#")) headerLines.add(line.substring(1));
else {
LogEntry le = new LogEntry();
le.fromRawData(line);
logData.add(le);
}
}
calculateAltitudes();
// find the logging interval, if present - otherwise default is used
for (String l : headerLines) {
if (l.startsWith("logInterval")) logInterval = Double.parseDouble(l.substring(14));
}
}
}