updates for 0.9.3
[debian/openrocket] / src / net / sf / openrocket / file / MotorLoader.java
1 package net.sf.openrocket.file;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.InputStreamReader;
6 import java.io.Reader;
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;
13 import java.util.Map;
14
15 import net.sf.openrocket.rocketcomponent.Motor;
16 import net.sf.openrocket.util.MathUtil;
17
18
19 public abstract class MotorLoader implements Loader<Motor> {
20         
21         
22         /** 
23          * Manufacturer codes to expand.  These are general translations that are used
24          * both in RASP and RockSim engine files.
25          */
26         private static final Map<String,String> MANUFACTURER_CODES =
27                 new HashMap<String,String>();
28         static {
29                 
30                 /*
31                  * TODO: CRITICAL: Should names have Inc. LLC. etc?
32                  */
33                 
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");
45                 }
46                 MANUFACTURER_CODES.put("ISP", "AeroTech");
47                 
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");
56
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");
61                 
62                 MANUFACTURER_CODES.put("AP", "Apogee");
63                 MANUFACTURER_CODES.put("APOG", "Apogee");
64                 MANUFACTURER_CODES.put("APOGEE", "Apogee");
65                 MANUFACTURER_CODES.put("P", "Apogee");
66                 
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.");
77                 
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");
83                 
84                 MANUFACTURER_CODES.put("E", "Estes");
85                 MANUFACTURER_CODES.put("ES", "Estes");
86                 MANUFACTURER_CODES.put("ESTES", "Estes");
87                 
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");
93                 
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");
102                 
103                 MANUFACTURER_CODES.put("H", "HyperTEK");
104                 MANUFACTURER_CODES.put("HT", "HyperTEK");
105                 MANUFACTURER_CODES.put("HYPER", "HyperTEK");
106                 MANUFACTURER_CODES.put("HYPERTEK", "HyperTEK");
107                 
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");
120                 
121                 MANUFACTURER_CODES.put("LOKI", "Loki Research");
122                 MANUFACTURER_CODES.put("LOKI RESEARCH", "Loki Research");
123                 MANUFACTURER_CODES.put("LR", "Loki Research");
124                 
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.");
134                 
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");
140                 
141                 MANUFACTURER_CODES.put("Q", "Quest");
142                 MANUFACTURER_CODES.put("QU", "Quest");
143                 MANUFACTURER_CODES.put("QUEST", "Quest");
144                 
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");
149                 
150                 MANUFACTURER_CODES.put("RR", "Roadrunner Rocketry");
151                 MANUFACTURER_CODES.put("ROADRUNNER", "Roadrunner Rocketry");
152                 MANUFACTURER_CODES.put("ROADRUNNER ROCKETRY", "Roadrunner Rocketry");
153                 
154                 MANUFACTURER_CODES.put("RV", "Rocketvision");
155                 MANUFACTURER_CODES.put("ROCKETVISION", "Rocketvision");
156
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");
163                 
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");
167                 
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");
174         }
175
176         
177         
178         
179         /**
180          * Load motors from the specified <code>InputStream</code>.  The file is read using
181          * the default charset returned by {@link #getDefaultCharset()}.
182          * 
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 
185          *                                              applicable.
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.
188          */
189         public List<Motor> load(InputStream stream, String filename) throws IOException {
190                 return load(new InputStreamReader(stream, getDefaultCharset()), filename);
191         }
192         
193         
194         /**
195          * Load motors from the specified <code>Reader</code>.
196          * 
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 
199          *                                              applicable.
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.
202          */
203         public abstract List<Motor> load(Reader reader, String filename) throws IOException;
204         
205
206         
207         /**
208          * Return the default charset to use when loading rocket files of this type.
209          * <p>
210          * If the method {@link #load(InputStream, String)} is overridden as well, this
211          * method may return <code>null</code>.
212          * 
213          * @return      the charset to use when loading the rocket file.
214          */
215         protected abstract Charset getDefaultCharset(); 
216         
217
218         
219         
220         //////////  Helper methods  //////////
221         
222         
223         /**
224          * Calculate the mass of a motor at distinct points in time based on the
225          * initial total mass, propellant weight and thrust.
226          * <p>
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>
230          *  
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
236          */
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>();
241
242                 double t0, f0;
243                 double totalMassChange = 0;
244                 double scale;
245                 
246                 // First calculate mass change between points
247                 t0 = time.get(0);
248                 f0 = thrust.get(0);
249                 for (int i=1; i < time.size(); i++) {
250                         double t1 = time.get(i);
251                         double f1 = thrust.get(i);
252                         
253                         double dm = 0.5*(f0+f1)*(t1-t0);
254                         deltam.add(dm);
255                         totalMassChange += dm;
256                         t0 = t1;
257                         f0 = f1;
258                 }
259                 
260                 // Scale mass change and calculate mass
261                 mass.add(total);
262                 scale = prop / totalMassChange;
263                 for (double dm: deltam) {
264                         total -= dm*scale;
265                         mass.add(total);
266                 }
267                 
268                 return mass;
269         }
270         
271         
272         /**
273          * Helper method to remove a delay (or plugged) from the end of a motor designation,
274          * if present.
275          * 
276          * @param designation   the motor designation.
277          * @return                              the designation with a possible delay removed.
278          */
279         protected static String removeDelay(String designation) {
280                 if (designation.matches(".*-([0-9]+|[pP])$")) {
281                         designation = designation.substring(0, designation.lastIndexOf('-'));
282                 }
283                 return designation;
284         }
285         
286         
287         
288         /**
289          * Helper method to tokenize a string using whitespace as the delimiter.
290          */
291         protected static String[] split(String str) {
292                 return split(str,"\\s+");
293         }
294         
295         
296         /**
297          * Helper method to tokenize a string using the given delimiter.
298          */
299         protected static String[] split(String str, String delim) {
300                 String[] pieces = str.split(delim);
301                 if (pieces.length==0 || !pieces[0].equals(""))
302                         return pieces;
303                 return Arrays.copyOfRange(pieces, 1, pieces.length);
304         }
305         
306         
307         /**
308          * Sort the primary list and other lists in that order.
309          * 
310          * @param primary       the list to order.
311          * @param lists         lists to order in the same permutation.
312          */
313         protected static void sortLists(List<Double> primary, List<?> ... lists) {
314                 
315                 // TODO: LOW: Very idiotic sort algorithm, but should be fast enough
316                 // since the time should be sorted already
317                 
318                 int index;
319                 
320                 do {
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);
326                                         }
327                                         break;
328                                 }
329                         }
330                 } while (index < primary.size()-1);
331         }
332         
333         
334         /**
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.
339          * 
340          * @param mfg   the original manufacturer / manufacturer code.
341          * @return              the manufacturer name.
342          */
343         public static String convertManufacturer(String mfg) {
344                 // Replace underscore and trim
345                 mfg = mfg.replace('_', ' ').trim();
346                 
347                 // Check for conversion
348                 String conv = MANUFACTURER_CODES.get(mfg.toUpperCase());
349                 if (conv != null)
350                         return conv;
351                 else
352                         return mfg;
353         }
354         
355         
356         @SuppressWarnings("unchecked")
357         protected static void finalizeThrustCurve(List<Double> time, List<Double> thrust,
358                         List ... lists) {
359                 
360                 if (time.size() == 0)
361                         return;
362                 
363                 // Start
364                 if (!MathUtil.equals(time.get(0), 0) || !MathUtil.equals(thrust.get(0), 0)) {
365                         time.add(0, 0.0);
366                         thrust.add(0, 0.0);
367                         for (List l: lists) {
368                                 Object o = l.get(0);
369                                 l.add(0, o);
370                         }
371                 }
372                 
373                 // End
374                 int n = time.size()-1;
375                 if (!MathUtil.equals(thrust.get(n), 0)) {
376                         time.add(time.get(n));
377                         thrust.add(0.0);
378                         for (List l: lists) {
379                                 Object o = l.get(n);
380                                 l.add(o);
381                         }
382                 }
383         }
384         
385 }