1 package net.sf.openrocket.file;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.InputStreamReader;
7 import java.nio.charset.Charset;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.Collections;
11 import java.util.List;
13 import net.sf.openrocket.motor.Motor;
14 import net.sf.openrocket.util.MathUtil;
17 public abstract class MotorLoader implements Loader<Motor> {
21 * Load motors from the specified <code>InputStream</code>. The file is read using
22 * the default charset returned by {@link #getDefaultCharset()}.
24 * @param stream the source of the motor definitions.
25 * @param filename the file name of the file, may be <code>null</code> if not
27 * @return a list of motors contained in the file.
28 * @throws IOException if an I/O exception occurs of the file format is invalid.
30 public List<Motor> load(InputStream stream, String filename) throws IOException {
31 return load(new InputStreamReader(stream, getDefaultCharset()), filename);
36 * Load motors from the specified <code>Reader</code>.
38 * @param reader the source of the motor definitions.
39 * @param filename the file name of the file, may be <code>null</code> if not
41 * @return a list of motors contained in the file.
42 * @throws IOException if an I/O exception occurs of the file format is invalid.
44 public abstract List<Motor> load(Reader reader, String filename) throws IOException;
49 * Return the default charset to use when loading rocket files of this type.
51 * If the method {@link #load(InputStream, String)} is overridden as well, this
52 * method may return <code>null</code>.
54 * @return the charset to use when loading the rocket file.
56 protected abstract Charset getDefaultCharset();
61 ////////// Helper methods //////////
65 * Calculate the mass of a motor at distinct points in time based on the
66 * initial total mass, propellant weight and thrust.
68 * This calculation assumes that the velocity of the exhaust remains constant
69 * during the burning. This derives from the mass-flow and thrust relation
70 * <pre>F = m' * v</pre>
72 * @param time list of time points
73 * @param thrust thrust at the discrete times
74 * @param total total weight of the motor
75 * @param prop propellant amount consumed during burning
76 * @return a list of the mass at the specified time points
78 protected static List<Double> calculateMass(List<Double> time, List<Double> thrust,
79 double total, double prop) {
80 List<Double> mass = new ArrayList<Double>();
81 List<Double> deltam = new ArrayList<Double>();
84 double totalMassChange = 0;
87 // First calculate mass change between points
90 for (int i=1; i < time.size(); i++) {
91 double t1 = time.get(i);
92 double f1 = thrust.get(i);
94 double dm = 0.5*(f0+f1)*(t1-t0);
96 totalMassChange += dm;
101 // Scale mass change and calculate mass
103 scale = prop / totalMassChange;
104 for (double dm: deltam) {
114 * Helper method to remove a delay (or plugged) from the end of a motor designation,
117 * @param designation the motor designation.
118 * @return the designation with a possible delay removed.
120 protected static String removeDelay(String designation) {
121 if (designation.matches(".*-([0-9]+|[pP])$")) {
122 designation = designation.substring(0, designation.lastIndexOf('-'));
130 * Helper method to tokenize a string using whitespace as the delimiter.
132 protected static String[] split(String str) {
133 return split(str,"\\s+");
138 * Helper method to tokenize a string using the given delimiter.
140 protected static String[] split(String str, String delim) {
141 String[] pieces = str.split(delim);
142 if (pieces.length==0 || !pieces[0].equals(""))
144 return Arrays.copyOfRange(pieces, 1, pieces.length);
149 * Sort the primary list and other lists in that order.
151 * @param primary the list to order.
152 * @param lists lists to order in the same permutation.
154 protected static void sortLists(List<Double> primary, List<?> ... lists) {
156 // TODO: LOW: Very idiotic sort algorithm, but should be fast enough
157 // since the time should be sorted already
162 for (index=0; index < primary.size()-1; index++) {
163 if (primary.get(index+1) < primary.get(index)) {
164 Collections.swap(primary, index, index+1);
165 for (List<?> l: lists) {
166 Collections.swap(l, index, index+1);
171 } while (index < primary.size()-1);
176 @SuppressWarnings("unchecked")
177 protected static void finalizeThrustCurve(List<Double> time, List<Double> thrust,
180 if (time.size() == 0)
184 if (!MathUtil.equals(time.get(0), 0) || !MathUtil.equals(thrust.get(0), 0)) {
187 for (List l: lists) {
194 int n = time.size()-1;
195 if (!MathUtil.equals(thrust.get(n), 0)) {
196 time.add(time.get(n));
198 for (List l: lists) {