import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
import java.util.zip.GZIPOutputStream;
import net.sf.openrocket.aerodynamics.Warning;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.MotorMount;
+import net.sf.openrocket.rocketcomponent.RecoveryDevice;
+import net.sf.openrocket.rocketcomponent.RecoveryDevice.DeployEvent;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.TubeCoupler;
+import net.sf.openrocket.simulation.CustomExpression;
import net.sf.openrocket.simulation.FlightData;
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightDataType;
public class OpenRocketSaver extends RocketSaver {
private static final LogHelper log = Application.getLogger();
-
+
/**
* Divisor used in converting an integer version to the point-represented version.
* The integer version divided by this value is the major version and the remainder is
*/
public static final int FILE_VERSION_DIVISOR = 100;
-
+
private static final String OPENROCKET_CHARSET = "UTF-8";
private static final String METHOD_PACKAGE = "net.sf.openrocket.file.openrocket.savers";
private static final String METHOD_SUFFIX = "Saver";
-
+
// Estimated storage used by different portions
// These have been hand-estimated from saved files
private static final int BYTES_PER_COMPONENT_UNCOMPRESSED = 590;
private static final int BYTES_PER_DATAPOINT_UNCOMPRESSED = 350;
private static final int BYTES_PER_DATAPOINT_COMPRESSED = 100;
-
+
private int indent;
private Writer dest;
(fileVersion / FILE_VERSION_DIVISOR) + "." + (fileVersion % FILE_VERSION_DIVISOR);
log.debug("Storing file version " + fileVersionString);
-
+
this.indent = 0;
-
+
writeln("<?xml version='1.0' encoding='utf-8'?>");
writeln("<openrocket version=\"" + fileVersionString + "\" creator=\"OpenRocket "
+ BuildProperties.getVersion() + "\">");
}
-
+
@Override
public long estimateFileSize(OpenRocketDocument doc, StorageOptions options) {
else
size += componentCount * BYTES_PER_COMPONENT_UNCOMPRESSED;
-
+
// Size per simulation
if (options.isCompressionEnabled())
size += doc.getSimulationCount() * BYTES_PER_SIMULATION_COMPRESSED;
else
size += doc.getSimulationCount() * BYTES_PER_SIMULATION_UNCOMPRESSED;
-
+
// Size per flight data point
int pointCount = 0;
double timeSkip = options.getSimulationTimeSkip();
*/
private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) {
/*
- * File version 1.2 is required for:
+ * File version 1.5 is requires for:
+ * - saving designs using ComponentPrests
+ * - recovery device deployment on lower stage separation
+ *
+ * File version 1.4 is required for:
+ * - saving simulation data
* - saving motor data
*
* File version 1.1 is required for:
*
* Otherwise use version 1.0.
*/
-
- // Check if design has simulations defined (version 1.3)
+
+ // Search the rocket for any ComponentPresets (version 1.5)
+ for (RocketComponent c : document.getRocket()) {
+ if (c.getPresetComponent() != null) {
+ return FILE_VERSION_DIVISOR + 5;
+ }
+ }
+
+ // Search for recovery device deployment type LOWER_STAGE_SEPARATION (version 1.5)
+ for (RocketComponent c : document.getRocket()) {
+ if (c instanceof RecoveryDevice) {
+ if (((RecoveryDevice) c).getDeployEvent() == DeployEvent.LOWER_STAGE_SEPARATION) {
+ return FILE_VERSION_DIVISOR + 5;
+ }
+ }
+ }
+
+ // Check if design has simulations defined (version 1.4)
if (document.getSimulationCount() > 0) {
- return FILE_VERSION_DIVISOR + 3;
+ return FILE_VERSION_DIVISOR + 4;
}
- // Check for motor definitions (version 1.2)
- Iterator<RocketComponent> iterator = document.getRocket().iterator();
- while (iterator.hasNext()) {
- RocketComponent c = iterator.next();
+ // Check for motor definitions (version 1.4)
+ for (RocketComponent c : document.getRocket()) {
if (!(c instanceof MotorMount))
continue;
MotorMount mount = (MotorMount) c;
for (String id : document.getRocket().getMotorConfigurationIDs()) {
if (mount.getMotor(id) != null) {
- return FILE_VERSION_DIVISOR + 2;
+ return FILE_VERSION_DIVISOR + 4;
}
}
}
// Check for fin tabs (version 1.1)
- iterator = document.getRocket().iterator();
- while (iterator.hasNext()) {
- RocketComponent c = iterator.next();
-
+ for (RocketComponent c : document.getRocket()) {
// Check for fin tabs
if (c instanceof FinSet) {
FinSet fin = (FinSet) c;
}
-
+
@SuppressWarnings("unchecked")
private void saveComponent(RocketComponent component) throws IOException {
writeln("<name>" + escapeXML(simulation.getName()) + "</name>");
// TODO: MEDIUM: Other simulators/calculators
+
writeln("<simulator>RK4Simulator</simulator>");
writeln("<calculator>BarrowmanCalculator</calculator>");
- writeln("<conditions>");
- indent++;
+
+ // Write out custom expressions
+ if (!simulation.getCustomExpressions().isEmpty()){
+ writeln("<customexpressions>"); indent++;
+ for (CustomExpression expression : simulation.getCustomExpressions()){
+ writeln("<expression>"); indent++;
+ writeElement("name", expression.getName());
+ writeElement("symbol", expression.getSymbol());
+ writeElement("unit", expression.getUnit());
+ writeElement("expressionstring", expression.getExpressionString());
+ indent--; writeln("</expression>");
+ }
+ indent--; writeln("</customexpressions>");
+ }
+
+ writeln("<conditions>"); indent++;
writeElement("configid", cond.getMotorConfigurationID());
writeElement("launchrodlength", cond.getLaunchRodLength());
writeElement("launchaltitude", cond.getLaunchAltitude());
writeElement("launchlatitude", cond.getLaunchLatitude());
writeElement("launchlongitude", cond.getLaunchLongitude());
- writeElement("geodeticmethod", cond.getGeodeticComputation().name().toLowerCase());
+ writeElement("geodeticmethod", cond.getGeodeticComputation().name().toLowerCase(Locale.ENGLISH));
if (cond.isISAAtmosphere()) {
writeln("<atmosphere model=\"isa\"/>");
indent--;
writeln("</conditions>");
-
+
for (String s : simulation.getSimulationListeners()) {
writeElement("listener", escapeXML(s));
}
-
// Write basic simulation data
FlightData data = simulation.getSimulatedData();
str += " flighttime=\"" + TextUtil.doubleToString(data.getFlightTime()) + "\"";
if (!Double.isNaN(data.getGroundHitVelocity()))
str += " groundhitvelocity=\"" + TextUtil.doubleToString(data.getGroundHitVelocity()) + "\"";
+ if (!Double.isNaN(data.getLaunchRodVelocity()))
+ str += " launchrodvelocity=\"" + TextUtil.doubleToString(data.getLaunchRodVelocity()) + "\"";
+ if (!Double.isNaN(data.getDeploymentVelocity()))
+ str += " deploymentvelocity=\"" + TextUtil.doubleToString(data.getDeploymentVelocity()) + "\"";
str += ">";
writeln(str);
indent++;
}
-
+
private void saveFlightDataBranch(FlightDataBranch branch, double timeSkip)
throws IOException {
double previousTime = -100000;
StringBuilder sb = new StringBuilder();
sb.append("<databranch name=\"");
sb.append(escapeXML(branch.getBranchName()));
+
+
+ sb.append("\" typekeys=\"");
+ for (int i = 0; i < types.length; i++) {
+ if (i > 0)
+ sb.append(",");
+ sb.append(types[i].getKey());
+ }
sb.append("\" types=\"");
for (int i = 0; i < types.length; i++) {
if (i > 0)
}
-
+
/* TODO: LOW: This is largely duplicated from above! */
private int countFlightDataBranchPoints(FlightDataBranch branch, double timeSkip) {
int count = 0;
}
-
+
private void writeDataPointString(List<List<Double>> data, int index, StringBuilder sb)
throws IOException {
sb.setLength(0);
}
-
+
private void writeElement(String element, Object content) throws IOException {
if (content == null)
content = "";
}
-
+
private void writeln(String str) throws IOException {
if (str.length() == 0) {
dest.write("\n");
}
-
-
+
+
/**
* Return the XML equivalent of an enum name.
*
* @return the corresponding XML name.
*/
public static String enumToXMLName(Enum<?> e) {
- return e.name().toLowerCase().replace("_", "");
+ return e.name().toLowerCase(Locale.ENGLISH).replace("_", "");
}
}