package net.sf.openrocket.file.motor;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.util.ArrayUtils;
import net.sf.openrocket.util.MathUtil;
public abstract class AbstractMotorLoader implements MotorLoader {
/**
* {@inheritDoc}
* <p>
* This method delegates the reading to the loaded from the Reader using the charset
* returned by {@link #getDefaultCharset()}.
*/
@Override
public List<Motor> load(InputStream stream, String filename) throws IOException {
return load(new InputStreamReader(stream, getDefaultCharset()), filename);
}
/**
* Load motors from the specified <code>Reader</code>.
*
* @param reader the source of the motor definitions.
* @param filename the file name of the file, may be <code>null</code> if not
* applicable.
* @return a list of motors contained in the file.
* @throws IOException if an I/O exception occurs of the file format is invalid.
*/
protected abstract List<Motor> load(Reader reader, String filename) throws IOException;
/**
* Return the default charset to use when loading rocket files of this type.
* <p>
* If the method {@link #load(InputStream, String)} is overridden as well, this
* method may return <code>null</code>.
*
* @return the charset to use when loading the rocket file.
*/
protected abstract Charset getDefaultCharset();
////////// Helper methods //////////
/**
* Calculate the mass of a motor at distinct points in time based on the
* initial total mass, propellant weight and thrust.
* <p>
* This calculation assumes that the velocity of the exhaust remains constant
* during the burning. This derives from the mass-flow and thrust relation
* <pre>F = m' * v</pre>
*
* @param time list of time points
* @param thrust thrust at the discrete times
* @param total total weight of the motor
* @param prop propellant amount consumed during burning
* @return a list of the mass at the specified time points
*/
protected static List<Double> calculateMass(List<Double> time, List<Double> thrust,
double total, double prop) {
List<Double> mass = new ArrayList<Double>();
List<Double> deltam = new ArrayList<Double>();
double t0, f0;
double totalMassChange = 0;
double scale;
// First calculate mass change between points
t0 = time.get(0);
f0 = thrust.get(0);
for (int i = 1; i < time.size(); i++) {
double t1 = time.get(i);
double f1 = thrust.get(i);
double dm = 0.5 * (f0 + f1) * (t1 - t0);
deltam.add(dm);
totalMassChange += dm;
t0 = t1;
f0 = f1;
}
// Scale mass change and calculate mass
mass.add(total);
scale = prop / totalMassChange;
for (double dm : deltam) {
total -= dm * scale;
// to correct negative mass error condition: (caused by rounding errors in the above loop)
if (total < 0) {
total = 0;
}
mass.add(total);
}
return mass;
}
/**
* Helper method to remove a delay (or plugged) from the end of a motor designation,
* if present.
*
* @param designation the motor designation.
* @return the designation with a possible delay removed.
*/
protected static String removeDelay(String designation) {
if (designation.matches(".*-([0-9]+|[pP])$")) {
designation = designation.substring(0, designation.lastIndexOf('-'));
}
return designation;
}
/**
* Helper method to tokenize a string using whitespace as the delimiter.
*/
protected static String[] split(String str) {
return split(str, "\\s+");
}
/**
* Helper method to tokenize a string using the given delimiter.
*/
protected static String[] split(String str, String delim) {
String[] pieces = str.split(delim);
if (pieces.length == 0 || !pieces[0].equals(""))
return pieces;
return ArrayUtils.copyOfRange(pieces, 1, pieces.length);
}
/**
* Sort the primary list and other lists in that order.
*
* @param primary the list to order.
* @param lists lists to order in the same permutation.
*/
protected static void sortLists(List<Double> primary, List<?>... lists) {
// TODO: LOW: Very idiotic sort algorithm, but should be fast enough
// since the time should be sorted already
int index;
do {
for (index = 0; index < primary.size() - 1; index++) {
if (primary.get(index + 1) < primary.get(index)) {
Collections.swap(primary, index, index + 1);
for (List<?> l : lists) {
Collections.swap(l, index, index + 1);
}
break;
}
}
} while (index < primary.size() - 1);
}
@SuppressWarnings("unchecked")
protected static void finalizeThrustCurve(List<Double> time, List<Double> thrust,
List... lists) {
if (time.size() == 0)
return;
// Start
if (!MathUtil.equals(time.get(0), 0) || !MathUtil.equals(thrust.get(0), 0)) {
time.add(0, 0.0);
thrust.add(0, 0.0);
for (List l : lists) {
Object o = l.get(0);
l.add(0, o);
}
}
// End
int n = time.size() - 1;
if (!MathUtil.equals(thrust.get(n), 0)) {
time.add(time.get(n));
thrust.add(0.0);
for (List l : lists) {
Object o = l.get(n);
l.add(o);
}
}
}
}