]> git.gag.com Git - debian/openrocket/blob - src/net/sf/openrocket/file/RockSimMotorLoader.java
updates for 0.9.3
[debian/openrocket] / src / net / sf / openrocket / file / RockSimMotorLoader.java
1 package net.sf.openrocket.file;
2
3 import java.io.IOException;
4 import java.io.Reader;
5 import java.nio.charset.Charset;
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 import java.util.List;
9
10 import net.sf.openrocket.aerodynamics.WarningSet;
11 import net.sf.openrocket.file.simplesax.ElementHandler;
12 import net.sf.openrocket.file.simplesax.NullElementHandler;
13 import net.sf.openrocket.file.simplesax.PlainTextHandler;
14 import net.sf.openrocket.file.simplesax.SimpleSAX;
15 import net.sf.openrocket.rocketcomponent.Motor;
16 import net.sf.openrocket.rocketcomponent.ThrustCurveMotor;
17 import net.sf.openrocket.util.Coordinate;
18
19 import org.xml.sax.InputSource;
20 import org.xml.sax.SAXException;
21
22 public class RockSimMotorLoader extends MotorLoader {
23         
24         public static final String CHARSET_NAME = "UTF-8";
25         
26         public static final Charset CHARSET = Charset.forName(CHARSET_NAME);
27
28         
29         /** Any delay longed than this will be interpreted as a plugged motor. */
30         private static final int DELAY_LIMIT = 90;
31         
32
33         
34         @Override
35         protected Charset getDefaultCharset() {
36                 return CHARSET;
37         }
38
39         
40
41         /**
42          * Load a <code>Motor</code> from a RockSim motor definition file specified by the 
43          * <code>Reader</code>. The <code>Reader</code> is responsible for using the correct 
44          * charset.
45          * <p>
46          * If automatic CG/mass calculation is used, then the CG is assumed to be located at 
47          * the center of the motor casing and the mass is calculated from the thrust curve 
48          * by assuming a constant exhaust velocity.
49          * 
50          * @param reader  the source of the file.
51          * @return                a list of the {@link Motor} objects defined in the file.
52          * @throws IOException  if an I/O error occurs or if the file format is invalid.
53          */
54         @Override
55         public List<Motor> load(Reader reader, String filename) throws IOException {
56                 InputSource source = new InputSource(reader);
57                 RSEHandler handler = new RSEHandler();
58                 WarningSet warnings = new WarningSet();
59                 
60                 try {
61                         SimpleSAX.readXML(source, handler, warnings);
62                         return handler.getMotors();
63                 } catch (SAXException e) {
64                         throw new IOException(e.getMessage(), e);
65                 }
66         }
67         
68         
69         
70         /**
71          * Initial handler for the RockSim engine files.
72          */
73         private static class RSEHandler extends ElementHandler {
74                 private final List<Motor> motors = new ArrayList<Motor>();
75                 
76                 private RSEMotorHandler motorHandler;
77                 
78                 public List<Motor> getMotors() {
79                         return motors;
80                 }
81                 
82                 @Override
83                 public ElementHandler openElement(String element,
84                                 HashMap<String, String> attributes, WarningSet warnings) throws SAXException {
85
86                         if (element.equals("engine-database") ||
87                                         element.equals("engine-list")) {
88                                 // Ignore <engine-database> and <engine-list> elements
89                                 return this;
90                         }
91                         
92                         if (element.equals("version")) {
93                                 // Ignore <version> elements completely
94                                 return null;
95                         }
96                         
97                         if (element.equals("engine")) {
98                                 motorHandler = new RSEMotorHandler(attributes);
99                                 return motorHandler;
100                         }
101                         
102                         return null;
103                 }
104
105                 @Override
106                 public void closeElement(String element, HashMap<String, String> attributes,
107                                 String content, WarningSet warnings) throws SAXException {
108
109                         if (element.equals("engine")) {
110                                 Motor motor = motorHandler.getMotor();
111                                 motors.add(motor);
112                         }
113                 }
114         }
115         
116         
117         /**
118          * Handler for a RockSim engine file <motor> element.
119          */
120         private static class RSEMotorHandler extends ElementHandler {
121
122                 private final String manufacturer;
123                 private final String designation;
124                 private final double[] delays;
125                 private final double diameter;
126                 private final double length;
127                 private final double initMass;
128                 private final double propMass;
129                 private final Motor.Type type;
130                 private boolean calculateMass;
131                 private boolean calculateCG;
132                 
133                 private String description = "";
134                 
135                 private List<Double> time;
136                 private List<Double> force;
137                 private List<Double> mass;
138                 private List<Double> cg;
139                 
140                 private RSEMotorDataHandler dataHandler = null;
141
142                 
143                 public RSEMotorHandler(HashMap<String, String> attributes) throws SAXException {
144                         String str;
145                         
146                         // Manufacturer
147                         str = attributes.get("mfg");
148                         if (str == null)
149                                 throw new SAXException("Manufacturer missing");
150                         manufacturer = convertManufacturer(str);
151                         
152                         // Designation
153                         str = attributes.get("code");
154                         if (str == null)
155                                 throw new SAXException("Designation missing");
156                         designation = removeDelay(str);
157                         
158                         // Delays
159                         ArrayList<Double> delayList = new ArrayList<Double>();
160                         str = attributes.get("delays");
161                         if (str != null) {
162                                 String[] split = str.split(",");
163                                 for (String delay: split) {
164                                         try {
165                                                 
166                                                 double d = Double.parseDouble(delay);
167                                                 if (d >= DELAY_LIMIT)
168                                                         d = Motor.PLUGGED;
169                                                 delayList.add(d);
170                                                 
171                                         } catch (NumberFormatException e) {
172                                                 if (str.equalsIgnoreCase("P") || str.equalsIgnoreCase("plugged")) {
173                                                         delayList.add(Motor.PLUGGED);
174                                                 }
175                                         }
176                                 }
177                         }
178                         delays = new double[delayList.size()];
179                         for (int i=0; i<delayList.size(); i++) {
180                                 delays[i] = delayList.get(i);
181                         }
182                         
183                         // Diameter
184                         str = attributes.get("dia");
185                         if (str == null)
186                                 throw new SAXException("Diameter missing");
187                         try {
188                                 diameter = Double.parseDouble(str) / 1000.0;
189                         } catch (NumberFormatException e) {
190                                 throw new SAXException("Invalid diameter " + str);
191                         }
192                         
193                         // Length
194                         str = attributes.get("len");
195                         if (str == null)
196                                 throw new SAXException("Length missing");
197                         try {
198                                 length = Double.parseDouble(str) / 1000.0;
199                         } catch (NumberFormatException e) {
200                                 throw new SAXException("Invalid length " + str);
201                         }
202                         
203                         // Initial mass
204                         str = attributes.get("initWt");
205                         if (str == null)
206                                 throw new SAXException("Initial mass missing");
207                         try {
208                                 initMass = Double.parseDouble(str) / 1000.0;
209                         } catch (NumberFormatException e) {
210                                 throw new SAXException("Invalid initial mass " + str);
211                         }
212                         
213                         // Propellant mass
214                         str = attributes.get("propWt");
215                         if (str == null)
216                                 throw new SAXException("Propellant mass missing");
217                         try {
218                                 propMass = Double.parseDouble(str) / 1000.0;
219                         } catch (NumberFormatException e) {
220                                 throw new SAXException("Invalid propellant mass " + str);
221                         }
222                         
223                         if (propMass > initMass) {
224                                 throw new SAXException("Propellant weight exceeds total weight in " +
225                                                 "RockSim engine format");
226                         }
227                         
228                         // Motor type
229                         str = attributes.get("Type");
230                         if (str != null && str.equalsIgnoreCase("single-use")) {
231                                 type = Motor.Type.SINGLE;
232                         } else if (str != null && str.equalsIgnoreCase("hybrid")) {
233                                 type = Motor.Type.HYBRID;
234                         } else if (str != null && str.equalsIgnoreCase("reloadable")) {
235                                 type = Motor.Type.RELOAD;
236                         } else {
237                                 type = Motor.Type.UNKNOWN;
238                         }
239                         
240                         // Calculate mass
241                         str = attributes.get("auto-calc-mass");
242                         if ("0".equals(str) || "false".equalsIgnoreCase(str)) {
243                                 calculateMass = false;
244                         } else {
245                                 calculateMass = true;
246                         }
247                         
248                         // Calculate CG
249                         str = attributes.get("auto-calc-cg");
250                         if ("0".equals(str) || "false".equalsIgnoreCase(str)) {
251                                 calculateCG = false;
252                         } else {
253                                 calculateCG = true;
254                         }
255                 }
256                 
257                 @Override
258                 public ElementHandler openElement(String element,
259                                 HashMap<String, String> attributes, WarningSet warnings) throws SAXException {
260
261                         if (element.equals("comments")) {
262                                 return PlainTextHandler.INSTANCE;
263                         }
264                         
265                         if (element.equals("data")) {
266                                 if (dataHandler != null) {
267                                         throw new SAXException("Multiple data elements encountered in motor " +
268                                                         "definition");
269                                 }
270                                 dataHandler = new RSEMotorDataHandler();
271                                 return dataHandler;
272                         }
273
274                         warnings.add("Unknown element '" + element + "' encountered, ignoring.");
275                         return null;
276                 }
277
278                 @Override
279                 public void closeElement(String element, HashMap<String, String> attributes,
280                                 String content, WarningSet warnings) {
281
282                         if (element.equals("comments")) {
283                                 if (description.length() > 0) {
284                                         description = description + "\n\n" + content.trim();
285                                 } else {
286                                         description = content.trim();
287                                 }
288                                 return;
289                         }
290                         
291                         if (element.equals("data")) {
292                                 time = dataHandler.getTime();
293                                 force = dataHandler.getForce();
294                                 mass = dataHandler.getMass();
295                                 cg = dataHandler.getCG();
296                                 
297                                 sortLists(time, force, mass, cg);
298                                 
299                                 for (double d: mass) {
300                                         if (Double.isNaN(d)) {
301                                                 calculateMass = true;
302                                                 break;
303                                         }
304                                 }
305                                 for (double d: cg) {
306                                         if (Double.isNaN(d)) {
307                                                 calculateCG = true;
308                                                 break;
309                                         }
310                                 }
311                                 return;
312                         }
313                 }
314                 
315                 public Motor getMotor() throws SAXException {
316                         if (time == null || time.size() == 0)
317                                 throw new SAXException("Illegal motor data");
318
319                         
320                         finalizeThrustCurve(time, force, mass, cg);
321                         final int n = time.size();
322                         
323                         if (calculateMass) {
324                                 mass = calculateMass(time, force, initMass, propMass);
325                         }
326                         if (calculateCG) {
327                                 for (int i=0; i < n; i++) {
328                                         cg.set(i, length/2);
329                                 }
330                         }
331                         
332                         double[] timeArray = new double[n];
333                         double[] thrustArray = new double[n];
334                         Coordinate[] cgArray = new Coordinate[n];
335                         for (int i=0; i < n; i++) {
336                                 timeArray[i] = time.get(i);
337                                 thrustArray[i] = force.get(i);
338                                 cgArray[i] = new Coordinate(cg.get(i),0,0,mass.get(i));
339                         }
340                         
341                         try {
342                                 return new ThrustCurveMotor(manufacturer, designation, description, type,
343                                                 delays, diameter, length, timeArray, thrustArray, cgArray);
344                         } catch (IllegalArgumentException e) {
345                                 throw new SAXException("Illegal motor data", e);
346                         }
347                 }
348         }
349         
350         
351         /**
352          * Handler for the <data> element in a RockSim engine file motor definition.
353          */
354         private static class RSEMotorDataHandler extends ElementHandler {
355                 
356                 private final List<Double> time = new ArrayList<Double>();
357                 private final List<Double> force = new ArrayList<Double>();
358                 private final List<Double> mass = new ArrayList<Double>();
359                 private final List<Double> cg = new ArrayList<Double>();
360                 
361                 
362                 public List<Double> getTime() {
363                         return time;
364                 }
365                 public List<Double> getForce() {
366                         return force;
367                 }
368                 public List<Double> getMass() {
369                         return mass;
370                 }
371                 public List<Double> getCG() {
372                         return cg;
373                 }
374                 
375                 
376                 @Override
377                 public ElementHandler openElement(String element,
378                                 HashMap<String, String> attributes, WarningSet warnings) {
379
380                         if (element.equals("eng-data")) {
381                                 return NullElementHandler.INSTANCE;
382                         }
383                         
384                         warnings.add("Unknown element '" + element + "' encountered, ignoring.");
385                         return null;
386                 }
387
388                 @Override
389                 public void closeElement(String element, HashMap<String, String> attributes,
390                                 String content, WarningSet warnings) throws SAXException {
391
392                         double t = parseDouble(attributes.get("t"));
393                         double f = parseDouble(attributes.get("f"));
394                         double m = parseDouble(attributes.get("m")) / 1000.0;
395                         double g = parseDouble(attributes.get("cg")) / 1000.0;
396                         
397                         if (Double.isNaN(t) || Double.isNaN(f)) {
398                                 throw new SAXException("Illegal motor data point encountered");
399                         }
400                         
401                         time.add(t);
402                         force.add(f);
403                         mass.add(m);
404                         cg.add(g);
405                 }
406                 
407                 
408                 private double parseDouble(String str) {
409                         if (str == null)
410                                 return Double.NaN;
411                         try {
412                                 return Double.parseDouble(str);
413                         } catch (NumberFormatException e) {
414                                 return Double.NaN;
415                         }
416                 }
417         }
418 }