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.HashMap;
12 import java.util.List;
15 import net.sf.openrocket.rocketcomponent.Motor;
16 import net.sf.openrocket.util.MathUtil;
19 public abstract class MotorLoader implements Loader<Motor> {
23 * Manufacturer codes to expand. These are general translations that are used
24 * both in RASP and RockSim engine files.
26 private static final Map<String,String> MANUFACTURER_CODES =
27 new HashMap<String,String>();
31 * TODO: CRITICAL: Should names have Inc. LLC. etc?
34 // AeroTech has many name combinations...
35 for (String s: new String[] { "A", "AT", "AERO", "AEROT", "AEROTECH" }) {
36 MANUFACTURER_CODES.put(s, "AeroTech");
37 MANUFACTURER_CODES.put(s+"-RMS", "AeroTech");
38 MANUFACTURER_CODES.put(s+"/RMS", "AeroTech");
39 MANUFACTURER_CODES.put(s+"-RCS", "AeroTech");
40 MANUFACTURER_CODES.put(s+"/RCS", "AeroTech");
41 MANUFACTURER_CODES.put("RCS-" + s, "AeroTech");
42 MANUFACTURER_CODES.put("RCS/" + s, "AeroTech");
43 MANUFACTURER_CODES.put(s+"-APOGEE", "AeroTech");
44 MANUFACTURER_CODES.put(s+"/APOGEE", "AeroTech");
46 MANUFACTURER_CODES.put("ISP", "AeroTech");
48 MANUFACTURER_CODES.put("AHR", "Alpha Hybrid Rocketry LLC");
49 MANUFACTURER_CODES.put("ALPHA", "Alpha Hybrid Rocketry LLC");
50 MANUFACTURER_CODES.put("ALPHA HYBRID", "Alpha Hybrid Rocketry LLC");
51 MANUFACTURER_CODES.put("ALPHA HYBRIDS", "Alpha Hybrid Rocketry LLC");
52 MANUFACTURER_CODES.put("ALPHA HYBRID ROCKETRY", "Alpha Hybrid Rocketry LLC");
53 MANUFACTURER_CODES.put("ALPHA HYBRIDS ROCKETRY", "Alpha Hybrid Rocketry LLC");
54 MANUFACTURER_CODES.put("ALPHA HYBRID ROCKETRY LLC", "Alpha Hybrid Rocketry LLC");
55 MANUFACTURER_CODES.put("ALPHA HYBRID ROCKETRY, LLC", "Alpha Hybrid Rocketry LLC");
57 MANUFACTURER_CODES.put("AMW", "Animal Motor Works");
58 MANUFACTURER_CODES.put("AW", "Animal Motor Works");
59 MANUFACTURER_CODES.put("ANIMAL", "Animal Motor Works");
60 MANUFACTURER_CODES.put("ANIMAL MOTOR WORKS", "Animal Motor Works");
62 MANUFACTURER_CODES.put("AP", "Apogee");
63 MANUFACTURER_CODES.put("APOG", "Apogee");
64 MANUFACTURER_CODES.put("APOGEE", "Apogee");
65 MANUFACTURER_CODES.put("P", "Apogee");
67 MANUFACTURER_CODES.put("CES", "Cesaroni Technology Inc.");
68 MANUFACTURER_CODES.put("CESARONI", "Cesaroni Technology Inc.");
69 MANUFACTURER_CODES.put("CESARONI TECHNOLOGY", "Cesaroni Technology Inc.");
70 MANUFACTURER_CODES.put("CESARONI TECHNOLOGY INC", "Cesaroni Technology Inc.");
71 MANUFACTURER_CODES.put("CESARONI TECHNOLOGY INC.", "Cesaroni Technology Inc.");
72 MANUFACTURER_CODES.put("CESARONI TECHNOLOGY INCORPORATED", "Cesaroni Technology Inc.");
73 MANUFACTURER_CODES.put("CTI", "Cesaroni Technology Inc.");
74 MANUFACTURER_CODES.put("CS", "Cesaroni Technology Inc.");
75 MANUFACTURER_CODES.put("CSR", "Cesaroni Technology Inc.");
76 MANUFACTURER_CODES.put("PRO38", "Cesaroni Technology Inc.");
78 MANUFACTURER_CODES.put("CR", "Contrail Rockets");
79 MANUFACTURER_CODES.put("CONTR", "Contrail Rockets");
80 MANUFACTURER_CODES.put("CONTRAIL", "Contrail Rockets");
81 MANUFACTURER_CODES.put("CONTRAIL ROCKET", "Contrail Rockets");
82 MANUFACTURER_CODES.put("CONTRAIL ROCKETS", "Contrail Rockets");
84 MANUFACTURER_CODES.put("E", "Estes");
85 MANUFACTURER_CODES.put("ES", "Estes");
86 MANUFACTURER_CODES.put("ESTES", "Estes");
88 MANUFACTURER_CODES.put("EM", "Ellis Mountain");
89 MANUFACTURER_CODES.put("ELLIS", "Ellis Mountain");
90 MANUFACTURER_CODES.put("ELLIS MOUNTAIN", "Ellis Mountain");
91 MANUFACTURER_CODES.put("ELLIS MOUNTAIN ROCKET", "Ellis Mountain");
92 MANUFACTURER_CODES.put("ELLIS MOUNTAIN ROCKETS", "Ellis Mountain");
94 MANUFACTURER_CODES.put("GR", "Gorilla Rocket Motors");
95 MANUFACTURER_CODES.put("GORILLA", "Gorilla Rocket Motors");
96 MANUFACTURER_CODES.put("GORILLA ROCKET", "Gorilla Rocket Motors");
97 MANUFACTURER_CODES.put("GORILLA ROCKETS", "Gorilla Rocket Motors");
98 MANUFACTURER_CODES.put("GORILLA MOTOR", "Gorilla Rocket Motors");
99 MANUFACTURER_CODES.put("GORILLA MOTORS", "Gorilla Rocket Motors");
100 MANUFACTURER_CODES.put("GORILLA ROCKET MOTOR", "Gorilla Rocket Motors");
101 MANUFACTURER_CODES.put("GORILLA ROCKET MOTORS", "Gorilla Rocket Motors");
103 MANUFACTURER_CODES.put("H", "HyperTEK");
104 MANUFACTURER_CODES.put("HT", "HyperTEK");
105 MANUFACTURER_CODES.put("HYPER", "HyperTEK");
106 MANUFACTURER_CODES.put("HYPERTEK", "HyperTEK");
108 MANUFACTURER_CODES.put("K", "Kosdon by AeroTech");
109 MANUFACTURER_CODES.put("KBA", "Kosdon by AeroTech");
110 MANUFACTURER_CODES.put("K/AT", "Kosdon by AeroTech");
111 MANUFACTURER_CODES.put("K-AT", "Kosdon by AeroTech");
112 MANUFACTURER_CODES.put("KOS", "Kosdon by AeroTech");
113 MANUFACTURER_CODES.put("KOSDON", "Kosdon by AeroTech");
114 MANUFACTURER_CODES.put("KOSDON/AT", "Kosdon by AeroTech");
115 MANUFACTURER_CODES.put("KOSDON-AT", "Kosdon by AeroTech");
116 MANUFACTURER_CODES.put("KOSDON/AEROTECH", "Kosdon by AeroTech");
117 MANUFACTURER_CODES.put("KOSDON-AEROTECH", "Kosdon by AeroTech");
118 MANUFACTURER_CODES.put("KOSDON-BY-AEROTECH", "Kosdon by AeroTech");
119 MANUFACTURER_CODES.put("KOSDON BY AEROTECH", "Kosdon by AeroTech");
121 MANUFACTURER_CODES.put("LOKI", "Loki Research");
122 MANUFACTURER_CODES.put("LOKI RESEARCH", "Loki Research");
123 MANUFACTURER_CODES.put("LR", "Loki Research");
125 MANUFACTURER_CODES.put("PM", "Public Missiles, Ltd.");
126 MANUFACTURER_CODES.put("PML", "Public Missiles, Ltd.");
127 MANUFACTURER_CODES.put("PUBLIC MISSILES", "Public Missiles, Ltd.");
128 MANUFACTURER_CODES.put("PUBLIC MISSILES LTD", "Public Missiles, Ltd.");
129 MANUFACTURER_CODES.put("PUBLIC MISSILES, LTD", "Public Missiles, Ltd.");
130 MANUFACTURER_CODES.put("PUBLIC MISSILES LTD.", "Public Missiles, Ltd.");
131 MANUFACTURER_CODES.put("PUBLIC MISSILES, LTD.", "Public Missiles, Ltd.");
132 MANUFACTURER_CODES.put("PUBLIC MISSILES LIMITED", "Public Missiles, Ltd.");
133 MANUFACTURER_CODES.put("PUBLIC MISSILES, LIMITED", "Public Missiles, Ltd.");
135 MANUFACTURER_CODES.put("PP", "Propulsion Polymers");
136 MANUFACTURER_CODES.put("PROP", "Propulsion Polymers");
137 MANUFACTURER_CODES.put("PROPULSION", "Propulsion Polymers");
138 MANUFACTURER_CODES.put("PROPULSION-POLYMERS", "Propulsion Polymers");
139 MANUFACTURER_CODES.put("PROPULSION POLYMERS", "Propulsion Polymers");
141 MANUFACTURER_CODES.put("Q", "Quest");
142 MANUFACTURER_CODES.put("QU", "Quest");
143 MANUFACTURER_CODES.put("QUEST", "Quest");
145 MANUFACTURER_CODES.put("RATT", "RATT Works");
146 MANUFACTURER_CODES.put("RATT WORKS", "RATT Works");
147 MANUFACTURER_CODES.put("RT", "RATT Works");
148 MANUFACTURER_CODES.put("RTW", "RATT Works");
150 MANUFACTURER_CODES.put("RR", "Roadrunner Rocketry");
151 MANUFACTURER_CODES.put("ROADRUNNER", "Roadrunner Rocketry");
152 MANUFACTURER_CODES.put("ROADRUNNER ROCKETRY", "Roadrunner Rocketry");
154 MANUFACTURER_CODES.put("RV", "Rocketvision");
155 MANUFACTURER_CODES.put("ROCKETVISION", "Rocketvision");
157 MANUFACTURER_CODES.put("SR", "Sky Ripper Systems");
158 MANUFACTURER_CODES.put("SRS", "Sky Ripper Systems");
159 MANUFACTURER_CODES.put("SKYR", "Sky Ripper Systems");
160 MANUFACTURER_CODES.put("SKYRIPPER", "Sky Ripper Systems");
161 MANUFACTURER_CODES.put("SKYRIPPER SYSTEMS", "Sky Ripper Systems");
162 MANUFACTURER_CODES.put("SKY RIPPER SYSTEMS", "Sky Ripper Systems");
164 MANUFACTURER_CODES.put("WCH", "West Coast Hybrids");
165 MANUFACTURER_CODES.put("WCR", "West Coast Hybrids");
166 MANUFACTURER_CODES.put("WEST COAST HYBRIDS", "West Coast Hybrids");
168 MANUFACTURER_CODES.put("SF", "WECO Feuerwerk"); // Previously Sachsen Feuerwerks
169 MANUFACTURER_CODES.put("SACHSEN FEUERWERK", "WECO Feuerwerk");
170 MANUFACTURER_CODES.put("SACHSEN FEUERWERKS", "WECO Feuerwerk");
171 MANUFACTURER_CODES.put("WECO", "WECO Feuerwerk");
172 MANUFACTURER_CODES.put("WECO FEUERWERK", "WECO Feuerwerk");
173 MANUFACTURER_CODES.put("WECO FEUERWERKS", "WECO Feuerwerk");
180 * Load motors from the specified <code>InputStream</code>. The file is read using
181 * the default charset returned by {@link #getDefaultCharset()}.
183 * @param stream the source of the motor definitions.
184 * @param filename the file name of the file, may be <code>null</code> if not
186 * @return a list of motors contained in the file.
187 * @throws IOException if an I/O exception occurs of the file format is invalid.
189 public List<Motor> load(InputStream stream, String filename) throws IOException {
190 return load(new InputStreamReader(stream, getDefaultCharset()), filename);
195 * Load motors from the specified <code>Reader</code>.
197 * @param reader the source of the motor definitions.
198 * @param filename the file name of the file, may be <code>null</code> if not
200 * @return a list of motors contained in the file.
201 * @throws IOException if an I/O exception occurs of the file format is invalid.
203 public abstract List<Motor> load(Reader reader, String filename) throws IOException;
208 * Return the default charset to use when loading rocket files of this type.
210 * If the method {@link #load(InputStream, String)} is overridden as well, this
211 * method may return <code>null</code>.
213 * @return the charset to use when loading the rocket file.
215 protected abstract Charset getDefaultCharset();
220 ////////// Helper methods //////////
224 * Calculate the mass of a motor at distinct points in time based on the
225 * initial total mass, propellant weight and thrust.
227 * This calculation assumes that the velocity of the exhaust remains constant
228 * during the burning. This derives from the mass-flow and thrust relation
229 * <pre>F = m' * v</pre>
231 * @param time list of time points
232 * @param thrust thrust at the discrete times
233 * @param total total weight of the motor
234 * @param prop propellant amount consumed during burning
235 * @return a list of the mass at the specified time points
237 protected static List<Double> calculateMass(List<Double> time, List<Double> thrust,
238 double total, double prop) {
239 List<Double> mass = new ArrayList<Double>();
240 List<Double> deltam = new ArrayList<Double>();
243 double totalMassChange = 0;
246 // First calculate mass change between points
249 for (int i=1; i < time.size(); i++) {
250 double t1 = time.get(i);
251 double f1 = thrust.get(i);
253 double dm = 0.5*(f0+f1)*(t1-t0);
255 totalMassChange += dm;
260 // Scale mass change and calculate mass
262 scale = prop / totalMassChange;
263 for (double dm: deltam) {
273 * Helper method to remove a delay (or plugged) from the end of a motor designation,
276 * @param designation the motor designation.
277 * @return the designation with a possible delay removed.
279 protected static String removeDelay(String designation) {
280 if (designation.matches(".*-([0-9]+|[pP])$")) {
281 designation = designation.substring(0, designation.lastIndexOf('-'));
289 * Helper method to tokenize a string using whitespace as the delimiter.
291 protected static String[] split(String str) {
292 return split(str,"\\s+");
297 * Helper method to tokenize a string using the given delimiter.
299 protected static String[] split(String str, String delim) {
300 String[] pieces = str.split(delim);
301 if (pieces.length==0 || !pieces[0].equals(""))
303 return Arrays.copyOfRange(pieces, 1, pieces.length);
308 * Sort the primary list and other lists in that order.
310 * @param primary the list to order.
311 * @param lists lists to order in the same permutation.
313 protected static void sortLists(List<Double> primary, List<?> ... lists) {
315 // TODO: LOW: Very idiotic sort algorithm, but should be fast enough
316 // since the time should be sorted already
321 for (index=0; index < primary.size()-1; index++) {
322 if (primary.get(index+1) < primary.get(index)) {
323 Collections.swap(primary, index, index+1);
324 for (List<?> l: lists) {
325 Collections.swap(l, index, index+1);
330 } while (index < primary.size()-1);
335 * Convert a manufacturer string. This should be done for all manufacturers
336 * for overall consistency. This includes both RASP name expansions and some
337 * general renaming. This should also be performed when loading designs in order
338 * to identify manufacturers correctly.
340 * @param mfg the original manufacturer / manufacturer code.
341 * @return the manufacturer name.
343 public static String convertManufacturer(String mfg) {
344 // Replace underscore and trim
345 mfg = mfg.replace('_', ' ').trim();
347 // Check for conversion
348 String conv = MANUFACTURER_CODES.get(mfg.toUpperCase());
356 @SuppressWarnings("unchecked")
357 protected static void finalizeThrustCurve(List<Double> time, List<Double> thrust,
360 if (time.size() == 0)
364 if (!MathUtil.equals(time.get(0), 0) || !MathUtil.equals(thrust.get(0), 0)) {
367 for (List l: lists) {
374 int n = time.size()-1;
375 if (!MathUtil.equals(thrust.get(n), 0)) {
376 time.add(time.get(n));
378 for (List l: lists) {