+++ /dev/null
-package net.sf.openrocket.file;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import net.sf.openrocket.aerodynamics.WarningSet;
-import net.sf.openrocket.file.simplesax.ElementHandler;
-import net.sf.openrocket.file.simplesax.NullElementHandler;
-import net.sf.openrocket.file.simplesax.PlainTextHandler;
-import net.sf.openrocket.file.simplesax.SimpleSAX;
-import net.sf.openrocket.motor.Manufacturer;
-import net.sf.openrocket.motor.Motor;
-import net.sf.openrocket.motor.MotorDigest;
-import net.sf.openrocket.motor.ThrustCurveMotor;
-import net.sf.openrocket.motor.MotorDigest.DataType;
-import net.sf.openrocket.util.Coordinate;
-
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-public class RockSimMotorLoader extends MotorLoader {
-
- public static final String CHARSET_NAME = "UTF-8";
-
- public static final Charset CHARSET = Charset.forName(CHARSET_NAME);
-
-
- /** Any delay longed than this will be interpreted as a plugged motor. */
- private static final int DELAY_LIMIT = 90;
-
-
-
- @Override
- protected Charset getDefaultCharset() {
- return CHARSET;
- }
-
-
-
- /**
- * Load a <code>Motor</code> from a RockSim motor definition file specified by the
- * <code>Reader</code>. The <code>Reader</code> is responsible for using the correct
- * charset.
- * <p>
- * If automatic CG/mass calculation is used, then the CG is assumed to be located at
- * the center of the motor casing and the mass is calculated from the thrust curve
- * by assuming a constant exhaust velocity.
- *
- * @param reader the source of the file.
- * @return a list of the {@link Motor} objects defined in the file.
- * @throws IOException if an I/O error occurs or if the file format is invalid.
- */
- @Override
- public List<Motor> load(Reader reader, String filename) throws IOException {
- InputSource source = new InputSource(reader);
- RSEHandler handler = new RSEHandler();
- WarningSet warnings = new WarningSet();
-
- try {
- SimpleSAX.readXML(source, handler, warnings);
- return handler.getMotors();
- } catch (SAXException e) {
- throw new IOException(e.getMessage(), e);
- }
- }
-
-
-
- /**
- * Initial handler for the RockSim engine files.
- */
- private static class RSEHandler extends ElementHandler {
- private final List<Motor> motors = new ArrayList<Motor>();
-
- private RSEMotorHandler motorHandler;
-
- public List<Motor> getMotors() {
- return motors;
- }
-
- @Override
- public ElementHandler openElement(String element,
- HashMap<String, String> attributes, WarningSet warnings) throws SAXException {
-
- if (element.equals("engine-database") ||
- element.equals("engine-list")) {
- // Ignore <engine-database> and <engine-list> elements
- return this;
- }
-
- if (element.equals("version")) {
- // Ignore <version> elements completely
- return null;
- }
-
- if (element.equals("engine")) {
- motorHandler = new RSEMotorHandler(attributes);
- return motorHandler;
- }
-
- return null;
- }
-
- @Override
- public void closeElement(String element, HashMap<String, String> attributes,
- String content, WarningSet warnings) throws SAXException {
-
- if (element.equals("engine")) {
- Motor motor = motorHandler.getMotor();
- motors.add(motor);
- }
- }
- }
-
-
- /**
- * Handler for a RockSim engine file <motor> element.
- */
- private static class RSEMotorHandler extends ElementHandler {
-
- private final String manufacturer;
- private final String designation;
- private final double[] delays;
- private final double diameter;
- private final double length;
- private final double initMass;
- private final double propMass;
- private final Motor.Type type;
- private boolean calculateMass;
- private boolean calculateCG;
-
- private String description = "";
-
- private List<Double> time;
- private List<Double> force;
- private List<Double> mass;
- private List<Double> cg;
-
- private RSEMotorDataHandler dataHandler = null;
-
-
- public RSEMotorHandler(HashMap<String, String> attributes) throws SAXException {
- String str;
-
- // Manufacturer
- str = attributes.get("mfg");
- if (str == null)
- throw new SAXException("Manufacturer missing");
- manufacturer = str;
-
- // Designation
- str = attributes.get("code");
- if (str == null)
- throw new SAXException("Designation missing");
- designation = removeDelay(str);
-
- // Delays
- ArrayList<Double> delayList = new ArrayList<Double>();
- str = attributes.get("delays");
- if (str != null) {
- String[] split = str.split(",");
- for (String delay: split) {
- try {
-
- double d = Double.parseDouble(delay);
- if (d >= DELAY_LIMIT)
- d = Motor.PLUGGED;
- delayList.add(d);
-
- } catch (NumberFormatException e) {
- if (str.equalsIgnoreCase("P") || str.equalsIgnoreCase("plugged")) {
- delayList.add(Motor.PLUGGED);
- }
- }
- }
- }
- delays = new double[delayList.size()];
- for (int i=0; i<delayList.size(); i++) {
- delays[i] = delayList.get(i);
- }
-
- // Diameter
- str = attributes.get("dia");
- if (str == null)
- throw new SAXException("Diameter missing");
- try {
- diameter = Double.parseDouble(str) / 1000.0;
- } catch (NumberFormatException e) {
- throw new SAXException("Invalid diameter " + str);
- }
-
- // Length
- str = attributes.get("len");
- if (str == null)
- throw new SAXException("Length missing");
- try {
- length = Double.parseDouble(str) / 1000.0;
- } catch (NumberFormatException e) {
- throw new SAXException("Invalid length " + str);
- }
-
- // Initial mass
- str = attributes.get("initWt");
- if (str == null)
- throw new SAXException("Initial mass missing");
- try {
- initMass = Double.parseDouble(str) / 1000.0;
- } catch (NumberFormatException e) {
- throw new SAXException("Invalid initial mass " + str);
- }
-
- // Propellant mass
- str = attributes.get("propWt");
- if (str == null)
- throw new SAXException("Propellant mass missing");
- try {
- propMass = Double.parseDouble(str) / 1000.0;
- } catch (NumberFormatException e) {
- throw new SAXException("Invalid propellant mass " + str);
- }
-
- if (propMass > initMass) {
- throw new SAXException("Propellant weight exceeds total weight in " +
- "RockSim engine format");
- }
-
- // Motor type
- str = attributes.get("Type");
- if (str != null && str.equalsIgnoreCase("single-use")) {
- type = Motor.Type.SINGLE;
- } else if (str != null && str.equalsIgnoreCase("hybrid")) {
- type = Motor.Type.HYBRID;
- } else if (str != null && str.equalsIgnoreCase("reloadable")) {
- type = Motor.Type.RELOAD;
- } else {
- type = Motor.Type.UNKNOWN;
- }
-
- // Calculate mass
- str = attributes.get("auto-calc-mass");
- if ("0".equals(str) || "false".equalsIgnoreCase(str)) {
- calculateMass = false;
- } else {
- calculateMass = true;
- }
-
- // Calculate CG
- str = attributes.get("auto-calc-cg");
- if ("0".equals(str) || "false".equalsIgnoreCase(str)) {
- calculateCG = false;
- } else {
- calculateCG = true;
- }
- }
-
- @Override
- public ElementHandler openElement(String element,
- HashMap<String, String> attributes, WarningSet warnings) throws SAXException {
-
- if (element.equals("comments")) {
- return PlainTextHandler.INSTANCE;
- }
-
- if (element.equals("data")) {
- if (dataHandler != null) {
- throw new SAXException("Multiple data elements encountered in motor " +
- "definition");
- }
- dataHandler = new RSEMotorDataHandler();
- return dataHandler;
- }
-
- warnings.add("Unknown element '" + element + "' encountered, ignoring.");
- return null;
- }
-
- @Override
- public void closeElement(String element, HashMap<String, String> attributes,
- String content, WarningSet warnings) {
-
- if (element.equals("comments")) {
- if (description.length() > 0) {
- description = description + "\n\n" + content.trim();
- } else {
- description = content.trim();
- }
- return;
- }
-
- if (element.equals("data")) {
- time = dataHandler.getTime();
- force = dataHandler.getForce();
- mass = dataHandler.getMass();
- cg = dataHandler.getCG();
-
- sortLists(time, force, mass, cg);
-
- for (double d: mass) {
- if (Double.isNaN(d)) {
- calculateMass = true;
- break;
- }
- }
- for (double d: cg) {
- if (Double.isNaN(d)) {
- calculateCG = true;
- break;
- }
- }
- return;
- }
- }
-
- public Motor getMotor() throws SAXException {
- if (time == null || time.size() == 0)
- throw new SAXException("Illegal motor data");
-
-
- finalizeThrustCurve(time, force, mass, cg);
- final int n = time.size();
-
- if (hasIllegalValue(mass))
- calculateMass = true;
- if (hasIllegalValue(cg))
- calculateCG = true;
-
- if (calculateMass) {
- mass = calculateMass(time, force, initMass, propMass);
- }
- if (calculateCG) {
- for (int i=0; i < n; i++) {
- cg.set(i, length/2);
- }
- }
-
- double[] timeArray = toArray(time);
- double[] thrustArray = toArray(force);
- Coordinate[] cgArray = new Coordinate[n];
- for (int i=0; i < n; i++) {
- cgArray[i] = new Coordinate(cg.get(i),0,0,mass.get(i));
- }
-
-
- // Create the motor digest from all data available in the file
- MotorDigest motorDigest = new MotorDigest();
- motorDigest.update(DataType.TIME_ARRAY, timeArray);
- if (!calculateMass) {
- motorDigest.update(DataType.MASS_PER_TIME, toArray(mass));
- } else {
- motorDigest.update(DataType.MASS_SPECIFIC, initMass, initMass-propMass);
- }
- if (!calculateCG) {
- motorDigest.update(DataType.CG_PER_TIME, toArray(cg));
- }
- motorDigest.update(DataType.FORCE_PER_TIME, thrustArray);
- final String digest = motorDigest.getDigest();
-
-
- try {
- return new ThrustCurveMotor(Manufacturer.getManufacturer(manufacturer),
- designation, description, type,
- delays, diameter, length, timeArray, thrustArray, cgArray, digest);
- } catch (IllegalArgumentException e) {
- throw new SAXException("Illegal motor data", e);
- }
- }
- }
-
-
- /**
- * Handler for the <data> element in a RockSim engine file motor definition.
- */
- private static class RSEMotorDataHandler extends ElementHandler {
-
- private final List<Double> time = new ArrayList<Double>();
- private final List<Double> force = new ArrayList<Double>();
- private final List<Double> mass = new ArrayList<Double>();
- private final List<Double> cg = new ArrayList<Double>();
-
-
- public List<Double> getTime() {
- return time;
- }
- public List<Double> getForce() {
- return force;
- }
- public List<Double> getMass() {
- return mass;
- }
- public List<Double> getCG() {
- return cg;
- }
-
-
- @Override
- public ElementHandler openElement(String element,
- HashMap<String, String> attributes, WarningSet warnings) {
-
- if (element.equals("eng-data")) {
- return NullElementHandler.INSTANCE;
- }
-
- warnings.add("Unknown element '" + element + "' encountered, ignoring.");
- return null;
- }
-
- @Override
- public void closeElement(String element, HashMap<String, String> attributes,
- String content, WarningSet warnings) throws SAXException {
-
- double t = parseDouble(attributes.get("t"));
- double f = parseDouble(attributes.get("f"));
- double m = parseDouble(attributes.get("m")) / 1000.0;
- double g = parseDouble(attributes.get("cg")) / 1000.0;
-
- if (Double.isNaN(t) || Double.isNaN(f)) {
- throw new SAXException("Illegal motor data point encountered");
- }
-
- time.add(t);
- force.add(f);
- mass.add(m);
- cg.add(g);
- }
-
-
- private double parseDouble(String str) {
- if (str == null)
- return Double.NaN;
- try {
- return Double.parseDouble(str);
- } catch (NumberFormatException e) {
- return Double.NaN;
- }
- }
- }
-
-
-
- private static boolean hasIllegalValue(List<Double> list) {
- for (Double d: list) {
- if (d == null || d.isNaN() || d.isInfinite()) {
- return true;
- }
- }
- return false;
- }
-
- private static double[] toArray(List<Double> list) {
- final int n = list.size();
- double[] array = new double[n];
- for (int i=0; i < n; i++) {
- array[i] = list.get(i);
- }
- return array;
- }
-}