motor selection enhancements

This commit is contained in:
Sampo Niskanen 2010-07-30 21:06:51 +00:00
parent 7a01b87cf6
commit 76dcbef5e5
14 changed files with 1350 additions and 59 deletions

View File

@ -1,3 +1,12 @@
2010-07-30 Sampo Niskanen
* [BUG] Fixed motor statistic computation
* Finalized enhanced motor selection dialog
2010-07-22 Doug Pedrick
* [BUG] Fixed RockSim fin loading
2010-07-21 Sampo Niskanen 2010-07-21 Sampo Niskanen
* Implemented enhanced motor selection dialog * Implemented enhanced motor selection dialog

View File

@ -37,6 +37,9 @@ openrocket.debug.bugurl
openrocket.debug.updateurl openrocket.debug.updateurl
URL used for retrieving update notifications. URL used for retrieving update notifications.
openrocket.debug.motordigest
If defined the motor digest will be displayed in the selection dialog.
openrocket.debug.coordinatecount openrocket.debug.coordinatecount
If defined, the number of instantiations of the Coordinate class are counted and reported If defined, the number of instantiations of the Coordinate class are counted and reported
every 1M instantiations, or as often as defined by this parameter. every 1M instantiations, or as often as defined by this parameter.

View File

@ -21,8 +21,10 @@ import net.sf.openrocket.file.RocketLoader;
import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.ElementHandler;
import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler;
import net.sf.openrocket.file.simplesax.SimpleSAX; import net.sf.openrocket.file.simplesax.SimpleSAX;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorDigest;
import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.rocketcomponent.BodyComponent; import net.sf.openrocket.rocketcomponent.BodyComponent;
import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.BodyTube;
@ -89,12 +91,15 @@ import org.xml.sax.SAXException;
* *
* @author Sampo Niskanen <sampo.niskanen@iki.fi> * @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/ */
public class OpenRocketLoader extends RocketLoader { public class OpenRocketLoader extends RocketLoader {
private static final LogHelper log = Application.getLogger();
@Override @Override
public OpenRocketDocument loadFromStream(InputStream source) throws RocketLoadException, public OpenRocketDocument loadFromStream(InputStream source) throws RocketLoadException,
IOException { IOException {
log.info("Loading .ork file");
InputSource xmlSource = new InputSource(source); InputSource xmlSource = new InputSource(source);
OpenRocketHandler handler = new OpenRocketHandler(); OpenRocketHandler handler = new OpenRocketHandler();
@ -102,6 +107,7 @@ public class OpenRocketLoader extends RocketLoader {
try { try {
SimpleSAX.readXML(xmlSource, handler, warnings); SimpleSAX.readXML(xmlSource, handler, warnings);
} catch (SAXException e) { } catch (SAXException e) {
log.warn("Malformed XML in input");
throw new RocketLoadException("Malformed XML in input.", e); throw new RocketLoadException("Malformed XML in input.", e);
} }
@ -141,6 +147,7 @@ public class OpenRocketLoader extends RocketLoader {
doc.getDefaultStorageOptions().setExplicitlySet(false); doc.getDefaultStorageOptions().setExplicitlySet(false);
doc.clearUndo(); doc.clearUndo();
log.info("Loading done");
return doc; return doc;
} }
@ -151,7 +158,7 @@ public class OpenRocketLoader extends RocketLoader {
class DocumentConfig { class DocumentConfig {
/* Remember to update OpenRocketSaver as well! */ /* Remember to update OpenRocketSaver as well! */
public static final String[] SUPPORTED_VERSIONS = { "0.9", "1.0", "1.1" }; public static final String[] SUPPORTED_VERSIONS = { "0.9", "1.0", "1.1", "1.2" };
//////// Component constructors //////// Component constructors
@ -971,6 +978,7 @@ class MotorHandler extends ElementHandler {
private Motor.Type type = null; private Motor.Type type = null;
private String manufacturer = null; private String manufacturer = null;
private String designation = null; private String designation = null;
private String digest = null;
private double diameter = Double.NaN; private double diameter = Double.NaN;
private double length = Double.NaN; private double length = Double.NaN;
private double delay = Double.NaN; private double delay = Double.NaN;
@ -990,25 +998,68 @@ class MotorHandler extends ElementHandler {
warnings.add(Warning.fromString("No motor specified, ignoring.")); warnings.add(Warning.fromString("No motor specified, ignoring."));
return null; return null;
} }
List<ThrustCurveMotor> motors = Application.getMotorSetDatabase().findMotors(type, manufacturer, List<ThrustCurveMotor> motors = Application.getMotorSetDatabase().findMotors(type, manufacturer,
designation, diameter, length); designation, diameter, length);
// No motors
if (motors.size() == 0) { if (motors.size() == 0) {
String str = "No motor with designation '" + designation + "'"; String str = "No motor with designation '" + designation + "'";
if (manufacturer != null) if (manufacturer != null)
str += " for manufacturer '" + manufacturer + "'"; str += " for manufacturer '" + manufacturer + "'";
warnings.add(Warning.fromString(str + " found.")); str += " found.";
warnings.add(str);
return null; return null;
} }
if (motors.size() > 1) {
// One motor
if (motors.size() == 1) {
ThrustCurveMotor m = motors.get(0);
if (digest != null && !MotorDigest.digestMotor(m).equals(digest)) {
String str = "Motor with designation '" + designation + "'";
if (manufacturer != null)
str += " for manufacturer '" + manufacturer + "'";
str += " has differing thrust curve than the original.";
warnings.add(str);
}
return m;
}
// Multiple motors, check digest for which one to use
if (digest != null) {
// Check for motor with correct digest
for (ThrustCurveMotor m : motors) {
if (MotorDigest.digestMotor(m).equals(digest)) {
return m;
}
}
String str = "Motor with designation '" + designation + "'";
if (manufacturer != null)
str += " for manufacturer '" + manufacturer + "'";
str += " has differing thrust curve than the original.";
warnings.add(str);
} else {
// No digest, check for preferred digest (OpenRocket <= 1.1.0)
// TODO: MEDIUM: This should only be done for document versions 1.1 and below
for (ThrustCurveMotor m : motors) {
if (PreferredMotorDigests.DIGESTS.contains(MotorDigest.digestMotor(m))) {
return m;
}
}
String str = "Multiple motors with designation '" + designation + "'"; String str = "Multiple motors with designation '" + designation + "'";
if (manufacturer != null) if (manufacturer != null)
str += " for manufacturer '" + manufacturer + "'"; str += " for manufacturer '" + manufacturer + "'";
warnings.add(Warning.fromString(str + " found, one chosen arbitrarily.")); str += " found, one chosen arbitrarily.";
warnings.add(str);
} }
return motors.get(0); return motors.get(0);
} }
/** /**
* Return the delay to use for the motor. * Return the delay to use for the motor.
*/ */
@ -1032,7 +1083,7 @@ class MotorHandler extends ElementHandler {
// Motor type // Motor type
type = null; type = null;
for (Motor.Type t : Motor.Type.values()) { for (Motor.Type t : Motor.Type.values()) {
if (t.name().toLowerCase().equals(content)) { if (t.name().toLowerCase().equals(content.trim())) {
type = t; type = t;
break; break;
} }
@ -1044,19 +1095,24 @@ class MotorHandler extends ElementHandler {
} else if (element.equals("manufacturer")) { } else if (element.equals("manufacturer")) {
// Manufacturer // Manufacturer
manufacturer = content; manufacturer = content.trim();
} else if (element.equals("designation")) { } else if (element.equals("designation")) {
// Designation // Designation
designation = content; designation = content.trim();
} else if (element.equals("digest")) {
// Digest
digest = content.trim();
} else if (element.equals("diameter")) { } else if (element.equals("diameter")) {
// Diameter // Diameter
diameter = Double.NaN; diameter = Double.NaN;
try { try {
diameter = Double.parseDouble(content); diameter = Double.parseDouble(content.trim());
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// Ignore // Ignore
} }
@ -1069,7 +1125,7 @@ class MotorHandler extends ElementHandler {
// Length // Length
length = Double.NaN; length = Double.NaN;
try { try {
length = Double.parseDouble(content); length = Double.parseDouble(content.trim());
} catch (NumberFormatException ignore) { } catch (NumberFormatException ignore) {
} }
@ -1085,7 +1141,7 @@ class MotorHandler extends ElementHandler {
delay = Motor.PLUGGED; delay = Motor.PLUGGED;
} else { } else {
try { try {
delay = Double.parseDouble(content); delay = Double.parseDouble(content.trim());
} catch (NumberFormatException ignore) { } catch (NumberFormatException ignore) {
} }

View File

@ -15,7 +15,9 @@ import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.Simulation;
import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.document.StorageOptions;
import net.sf.openrocket.file.RocketSaver; import net.sf.openrocket.file.RocketSaver;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.TubeCoupler; import net.sf.openrocket.rocketcomponent.TubeCoupler;
@ -24,6 +26,7 @@ import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightDataType; import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.simulation.GUISimulationConditions; import net.sf.openrocket.simulation.GUISimulationConditions;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Prefs; import net.sf.openrocket.util.Prefs;
@ -31,7 +34,9 @@ import net.sf.openrocket.util.Reflection;
import net.sf.openrocket.util.TextUtil; import net.sf.openrocket.util.TextUtil;
public class OpenRocketSaver extends RocketSaver { public class OpenRocketSaver extends RocketSaver {
private static final LogHelper log = Application.getLogger();
/** /**
* Divisor used in converting an integer version to the point-represented version. * 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 * The integer version divided by this value is the major version and the remainder is
@ -63,21 +68,25 @@ public class OpenRocketSaver extends RocketSaver {
public void save(OutputStream output, OpenRocketDocument document, StorageOptions options) public void save(OutputStream output, OpenRocketDocument document, StorageOptions options)
throws IOException { throws IOException {
log.info("Saving .ork file");
if (options.isCompressionEnabled()) { if (options.isCompressionEnabled()) {
log.debug("Enabling compression");
output = new GZIPOutputStream(output); output = new GZIPOutputStream(output);
} }
dest = new BufferedWriter(new OutputStreamWriter(output, OPENROCKET_CHARSET)); dest = new BufferedWriter(new OutputStreamWriter(output, OPENROCKET_CHARSET));
// Select file version number
final int fileVersion = calculateNecessaryFileVersion(document, options); final int fileVersion = calculateNecessaryFileVersion(document, options);
final String fileVersionString = final String fileVersionString =
(fileVersion / FILE_VERSION_DIVISOR) + "." + (fileVersion % FILE_VERSION_DIVISOR); (fileVersion / FILE_VERSION_DIVISOR) + "." + (fileVersion % FILE_VERSION_DIVISOR);
log.debug("Storing file version " + fileVersionString);
this.indent = 0; this.indent = 0;
System.out.println("Writing...");
writeln("<?xml version='1.0' encoding='utf-8'?>"); writeln("<?xml version='1.0' encoding='utf-8'?>");
writeln("<openrocket version=\"" + fileVersionString + "\" creator=\"OpenRocket " writeln("<openrocket version=\"" + fileVersionString + "\" creator=\"OpenRocket "
+ Prefs.getVersion() + "\">"); + Prefs.getVersion() + "\">");
@ -104,6 +113,7 @@ public class OpenRocketSaver extends RocketSaver {
indent--; indent--;
writeln("</openrocket>"); writeln("</openrocket>");
log.debug("Writing complete, flushing buffers");
dest.flush(); dest.flush();
if (options.isCompressionEnabled()) { if (options.isCompressionEnabled()) {
((GZIPOutputStream) output).finish(); ((GZIPOutputStream) output).finish();
@ -173,6 +183,9 @@ public class OpenRocketSaver extends RocketSaver {
*/ */
private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) { private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) {
/* /*
* File version 1.2 is required for:
* - saving motor data
*
* File version 1.1 is required for: * File version 1.1 is required for:
* - fin tabs * - fin tabs
* - components attached to tube coupler * - components attached to tube coupler
@ -180,8 +193,23 @@ public class OpenRocketSaver extends RocketSaver {
* Otherwise use version 1.0. * Otherwise use version 1.0.
*/ */
// Check for fin tabs (version 1.1) // Check for motor definitions (version 1.2)
Iterator<RocketComponent> iterator = document.getRocket().deepIterator(); Iterator<RocketComponent> iterator = document.getRocket().deepIterator();
while (iterator.hasNext()) {
RocketComponent c = iterator.next();
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;
}
}
}
// Check for fin tabs (version 1.1)
iterator = document.getRocket().deepIterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
RocketComponent c = iterator.next(); RocketComponent c = iterator.next();
@ -211,6 +239,8 @@ public class OpenRocketSaver extends RocketSaver {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void saveComponent(RocketComponent component) throws IOException { private void saveComponent(RocketComponent component) throws IOException {
log.debug("Saving component " + component.getComponentName());
Reflection.Method m = Reflection.findMethod(METHOD_PACKAGE, component, METHOD_SUFFIX, Reflection.Method m = Reflection.findMethod(METHOD_PACKAGE, component, METHOD_SUFFIX,
"getElements", RocketComponent.class); "getElements", RocketComponent.class);
if (m == null) { if (m == null) {
@ -502,22 +532,8 @@ public class OpenRocketSaver extends RocketSaver {
} }
public static void main(String[] arg) {
double d = -0.000000123456789123;
for (int i = 0; i < 20; i++) {
String str = TextUtil.doubleToString(d);
System.out.println(str + " -> " + Double.parseDouble(str));
d *= 10;
}
System.out.println("Value: " + Double.parseDouble("1.2345e9"));
}
/** /**
* Return the XML equivalent of an enum name. * Return the XML equivalent of an enum name.
* *

View File

@ -0,0 +1,885 @@
package net.sf.openrocket.file.openrocket;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* This class contains the motor digests of motors included in OpenRocket versions prior to 1.1.1.
* Before this the motor digest was not included in the design files, and therefore if the motor
* digest is missing when loading a file, the loader should prefer the motors with a digest defined
* in this class.
* <p>
* This is not a requirement for supporting the OpenRocket format, but allows opening older OpenRocket
* design files accurately without warnings of multiple motors.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
final class PreferredMotorDigests {
/**
* A set containing the preferred motor digests.
*/
public static final Set<String> DIGESTS;
static {
/*
* The list contains 845 digests, set initial parameters suitably to
* prevent any rehashing operations and to minimize size (power of two).
* 845/1024 = 0.825
*/
Set<String> set = new HashSet<String>(1024, 0.85f);
set.add("000ffb4c8e49ae47b2ab9a659da9e59b");
set.add("0039ed088e61360d934d9bd8503fad92");
set.add("003eeba358de7ebf9293b0e4c4ca9e66");
set.add("00e1a0576a93101d458c1c3d68d3eee0");
set.add("0111b89926277a6ea3f6075052343105");
set.add("0142c270a670ffff41c43268b0f129b9");
set.add("01be1f9100e05fb15df4c13395f7181c");
set.add("026f5924c48693077f2b11cdcdeb7452");
set.add("029082f7acda395568ca7f7df40764e1");
set.add("02dd1b3e2df7daf48b763f5ace35345e");
set.add("036e124dce42859ff08efa79e1f202e8");
set.add("03b88e64af521b03803247922801c996");
set.add("0468d7dc3dca25ac073dac1bd674e271");
set.add("048cfb7c2477c6e957d501c5ed3bc252");
set.add("049dda2ad1a709321734f393dc8a115b");
set.add("056d61b6a268283411e9dc9731dbb5e6");
set.add("05b85612f288726b02cdc47af7026aac");
set.add("05e205dc5dbd95db25305aa5c77b1192");
set.add("0601c6944d02e8736c09c2a8bb7cba49");
set.add("0622884d0a0954b1df6694ead24868bf");
set.add("063e7748d9a96508a70b1a2a1887aa3d");
set.add("06634321a8c5d533eb5efcbb40143257");
set.add("069a54372ed2776286160384ca0cac4f");
set.add("075539867b13c2afcc5198e00d7f4b5c");
set.add("076d9374af5fb0f2469083f9b57b7b96");
set.add("07c44b615a67060bca83c6faed56c0c6");
set.add("0825628215a980eed5fb4bed4eaec3b8");
set.add("082bad018f6d1e5622c371c1fe3148d6");
set.add("0837c3014078c8c8e79961b939be83cb");
set.add("08abceec22c5f6be5e9864be38df8ad5");
set.add("08c3b40a4bcf7a33256e5543e484f995");
set.add("08ca5be1a598772a8683016db619de36");
set.add("0a80cecafb53ae0ac73e6aec1a4567dd");
set.add("0add7ca688bcd32df8c3367a094e7847");
set.add("0b175b4beb0057db1b169d61061208a7");
set.add("0b955870dc2007c2b5f07eea57609420");
set.add("0c60a51d36ee5118fe29173aff2f6e49");
set.add("0c96cd95432d8e2ce6a6463ebf50beb1");
set.add("0d06d195c29a7f6fde6d002171922f2e");
set.add("0d642d7cb1544d19ec471124db97b92e");
set.add("0dd49968e2b1c4b1077e3c7ade056a79");
set.add("0e0d93ee28216440a5fa9452c9082351");
set.add("0e6774068b61579e20b89771b8a8f273");
set.add("0eac15679d3ae2fbd41083492b356b03");
set.add("0eca4c015dd1de561c2bbc47eaa4daf6");
set.add("0f0e1d09c7dec3a05b870b399ddbf6ee");
set.add("0f3c31b26b5768b3202f02f9d6bcc71c");
set.add("0f47293601d59fbad2076012090665dc");
set.add("0f5a1b31c333b722e4a72acbeba3a189");
set.add("0f6a55aca8a317f4d3d3236e4944343d");
set.add("0ffaa291ee52495d7dfec03b3a845636");
set.add("1092f5c5b48c2dcd4ac0bb16fa5383a8");
set.add("10a1689703dc533d435bef7265dd9ac0");
set.add("11bcc433b82e714f59809d76eed0ba05");
set.add("11ce2ec12b216f3e8d71fd9c53782b23");
set.add("11d11cdff93f18d10a1286cc3485c8c7");
set.add("11eac86852e12b9c3a2d0a4b183c3b79");
set.add("120eab6dd03b9bee7f5eb717e4e9d491");
set.add("1272d1a6979ea20a2efee3eb04657915");
set.add("12f6c5360c83656356c902ece3c0ff1b");
set.add("138a603a483dcf4127f1dcf208843e67");
set.add("140276d009fde1357ecdcb5d9ddc8a80");
set.add("1491fae1c7ce940915dd8296c74320f3");
set.add("14955ccec83043f3b1ef92f8524b3e67");
set.add("150b1279bc8b7f509a030274ee8e3f35");
set.add("153374d45687af1e96d5b8b1b03a2515");
set.add("1536a1389a9cd4ecf5bfaac9f4333852");
set.add("1539231d9952fdbe0533df405c46356c");
set.add("15d6a88bb656579740291df01297aa5c");
set.add("15fbf68a7c02161beb6cad00325752c3");
set.add("161cd37f60e13b9850e881bac61c840f");
set.add("161ed36663b694184f7f4131d1d5f3df");
set.add("167df7bf13809a19da8ff90a27f4b522");
set.add("170e81af0371550ea20c827669fbf0fd");
set.add("177c0df08cb85a4e13bf7412dacf2699");
set.add("179b9694bca64255ce9c0b06a08f46e6");
set.add("17d55e2cd3df50ef07aff9be6b160915");
set.add("1835337dfceafa20029fe6e472e7c7f0");
set.add("185820cacfb62e34c1f6c2e1feb42d27");
set.add("18981fde366efeca850bdf490253f0ec");
set.add("18b7f1dce04bc7838f3d2b234923de27");
set.add("18c2d213b8de15fc11ef66f7a7ad04a4");
set.add("1914ab609416b8559eeefda814867b9b");
set.add("19ae231357c49b6bf9427fa178dc58ab");
set.add("19b0b447800ba36f2d4ce76264009e2d");
set.add("19c9120e2fb7bbf6d21d71659f77439c");
set.add("19c9753bd99d7f0328792a434625f8a5");
set.add("1a508ce5b461be1998750bdad26764a3");
set.add("1a77681a4646cd21461df84c49074fe3");
set.add("1aa169a73004fc66a932576ac2732b15");
set.add("1aa1f3cc21a0f6a6eadb6166d468284c");
set.add("1ac8dac1b547a064a306bf42e568b5bc");
set.add("1af11d2e99f06b69ab5103731592ae8e");
set.add("1af30f73640ac1f9f3c8ef32fd04bfb8");
set.add("1b337a115a491abfc3abcd62399704d2");
set.add("1bb9c002f22ccd24bfcec36957ac0367");
set.add("1cbb12c9b58adc33642e1165b77c2e58");
set.add("1d30457aa2af0f212a26b9d2c203a216");
set.add("1d390d2ede88fb2f77ad7e7432155466");
set.add("1d920d4ee2bef0c7ffb28a91b9e325f6");
set.add("1e09cd01462e6d4728efafc4a550a5a6");
set.add("1e26c7969adb5bfc507da22802f17053");
set.add("1e5378337317821ffa4f53e9ecf47fbd");
set.add("1e68b1ce7eb224dc65b39546d0892299");
set.add("1e757827e2e03a781905e9f983c89933");
set.add("1f2564b3f0d78751b4e1d5836d54f7b1");
set.add("210bd4536d1c1872d213995420cf9513");
set.add("21bdc48d9411ffc8e811e32c45640f58");
set.add("21d4e53c3308cf3a1e916ef6cafff873");
set.add("21db7fea27e33cbab6fa2984017c241c");
set.add("221ab691a72a6f8b65792233b7bdf884");
set.add("222b7613b7a9a85d45051fb263b511bf");
set.add("224c062a8213f22058c0479c91ce470a");
set.add("22777fde378d9610258e4223fb5563f5");
set.add("22929b4849129644531a722397786513");
set.add("22c31705c3948c39721ced4ca04b2e65");
set.add("22e355a9e573b7f6f86c7e0791647ba7");
set.add("2320f4b15fb78448ce16a5a625f6f8f2");
set.add("234467bcf00a15e7377ceca46b7302f8");
set.add("23e140b2126af53487781f63082615e5");
set.add("245d147c568faf00dfb47d9c9080871c");
set.add("24a5102957c91107a092704f4f166e77");
set.add("24b7b0f55cea9329f981f00d922cfe61");
set.add("24d9308fa5d88f89365760a6e54f557f");
set.add("24fe3f1922a6d16b21f57b9925558296");
set.add("2571d40a353d275cdd8a4ba6e80a32fd");
set.add("259a0325a52acf54184fd439d1b2521d");
set.add("259d90773b3387c58aecb648c2c3812e");
set.add("25fd0f44fbbadfb70cee0467f9b53d3e");
set.add("26331fa38f2af84b18df5dd1db0244f0");
set.add("26a5e7834018943090396d419ca64662");
set.add("271f29d0b199d0d3f036e8f99ce76975");
set.add("2762f40ffacbc78b4c949cd38101a02a");
set.add("2769033a0acfff04e1f427027643c03a");
set.add("27b1601bb3a33c7cd2745caa651f0705");
set.add("27e522bd25f54343584ae89e90e64ee3");
set.add("2815e68ed1683663820682c8e00fd795");
set.add("285e598a676de414661a022f72967d29");
set.add("2886ee93f5dd4f02b331089928520e4f");
set.add("28f53f74ab45da2ab83072143f0d01d0");
set.add("2967cd7a160b396ef96f09695429d8e9");
set.add("29e99fbfab8c9771f4b5a86195db0c46");
set.add("2a1f5f5a829badfd64e2c20cd17bd38b");
set.add("2a941643d418880e0e7337aaaa00c555");
set.add("2a9d2a64b4601046774c9d27202de593");
set.add("2ad8de03de84415f1397cb2d4c77fb84");
set.add("2af7bcae566ada617d8888f34a5f70a3");
set.add("2bb2cea5465ab43f9b7e83cb44851223");
set.add("2bc22736450a8d0efb8d898bdcf52d43");
set.add("2c19c0cd4c005877798821dd65a2ff2e");
set.add("2c39985a5a49fa07759dc880e3722203");
set.add("2c58d5382b8d5fdbe1800e013f200f38");
set.add("2c8f6954ba9842ad9fc9bb367d14cf72");
set.add("2d13c151bbf6e1d7d7378c86d191d2d8");
set.add("2df4ee3f8a2c3611b267936e47bd3d3f");
set.add("2e6c8ecf50ee9ea82f407a8b0acd4f85");
set.add("2e97a2f015b1247b01b5e022bf6109cc");
set.add("2eae476e3eb97e2a1ad54c5b8fa48208");
set.add("2f44b9347e986c91ab886dc2e508885f");
set.add("2f478d2efa82571d5c3e49fde16c936e");
set.add("2f7460de6e7581a6775f739f894d86c6");
set.add("2fa429a16950f6c3f19a051b3417aac7");
set.add("2fa4545430dae966dce186984c98d0b7");
set.add("3027d63763f7aca58b41d52689f38dbd");
set.add("302b34ea5ec261fd74a4901d041d3f82");
set.add("30b5952157345beb00d753425a728757");
set.add("3136fef31b6d0e1d9a0dbbbdac05b0a3");
set.add("321377ccf346be4efa1fb8658032298a");
set.add("325e3898dc252f6c936301412be06505");
set.add("32fe6eecb5e97a6ff9c4f1c005857435");
set.add("33197b8e7194af401da6150c68004d7b");
set.add("3393a92e46a045c4eaf6b9e18f7044e3");
set.add("33a89133876e91fccc4058627b34d617");
set.add("3466c5940034ddb1371c4f86dabce964");
set.add("348abf304c63a702e4a229db28feee16");
set.add("349260e7bc0291ba2e4c26d4db00bee9");
set.add("3507c7d2b11a693620235ea3872dce66");
set.add("353236b8cb07eef73d80f25e240acddb");
set.add("35aeed248d254fbc3542b5cd3aa9842d");
set.add("36218bbb51e99aed53ea822ebaa2c873");
set.add("3666b06f839465adc5d36a6e75066a47");
set.add("36fb9fb79c253ee61e498e459f0cf395");
set.add("3703dd15904a118a05d771e7ee6e3f11");
set.add("370b98cc77577db1a07021e46c21cd3b");
set.add("3719475cc57cf3b5312f21b1efd228ef");
set.add("3738564e4327367bc2f359cdbb442304");
set.add("37bf1e76b05f333eefc0495e4f725838");
set.add("38715f11bf91b5ce06494e1ddd94c444");
set.add("387eea945f83c9567fa42c6e150b7ba9");
set.add("389687548b9f05e6c99d93a2ecf76307");
set.add("38b1e93cc1910ecc5301502fbb9bd8a3");
set.add("3a0b2ffd2d4593581c52bdc1094d92d8");
set.add("3a99a5318497e7108995a08675fa70d5");
set.add("3b4573f1f11db1ffedd14e10d539aad3");
set.add("3bc526028cf0be42fcbb75936810d41c");
set.add("3bc5834ec0726b10465b67f17b77044e");
set.add("3bf858e6c91e0292259a886b8bf793c3");
set.add("3c4eea57e65806dc59dd4c206bef79e1");
set.add("3c7b9e1836fe07f7a4ffaea90e7f33fc");
set.add("3c8aee818229c48b9a882caa6de58c18");
set.add("3cf831629486be08e747671a14d113f5");
set.add("3d6b990aaee7dff0be939343711dfa74");
set.add("3e2d355d7fd39e54ceead835d14df7e9");
set.add("3e8697fe46496d41528387e2d37d734a");
set.add("3ea538f54677ecaffbed1ae5f2e12d28");
set.add("3f654d824783b4392396b34ad2b44974");
set.add("3fc4889ea40aea23fedc994704ba4708");
set.add("41145e8204991f0b644b831cd859c4e2");
set.add("415fecbed32f8de16ffbab8e97edb4cb");
set.add("41633604778611785d7453c23823b0b3");
set.add("41d37971a99bb08d0f5f4fdcfcd87e8d");
set.add("428c0aeb520fe9e77d3b464543620716");
set.add("42cc2865a6fc399e689d2d569c58de2a");
set.add("43a6db581840e3645459ce51953ca9a5");
set.add("43a72eab1f3f874da7d68092e83357ec");
set.add("44255564acd68eca32ffab8e6130c5cc");
set.add("4448ff245bfd8d2606b418f33797571f");
set.add("44a4e02e520657221706cd6d69bcfb13");
set.add("44b12361fee8a2385a9b90e44fd079f3");
set.add("44b7c1c17e8e4728fadeecb6ba797af0");
set.add("44d734a18b45937c3035a047f9063dfd");
set.add("44edf41dd7624a6e2259d8e451622527");
set.add("4528bda7194c6dfafada95d68c2faa3a");
set.add("45a8a995a3614f823c04f3c157effe97");
set.add("45d2f014e70681483d6bc5864cf94b20");
set.add("46232174087bfb178ad7cc35bfb387a8");
set.add("46401106d96b729d330375a63e655374");
set.add("46ac2356b12ed7519ae2dd5f199b7c10");
set.add("4790684e33d48e3dfe99b6ff7712be8a");
set.add("479a2848353fef692063ec37e7d556dc");
set.add("47a649cae70a90e7d1ae2b2ab10465f0");
set.add("47bc150e2585e61cf9380ed540de4465");
set.add("4863872b48ecad3005e7b60d114c0fde");
set.add("487c3163ebf25cd3b4479e13e30cba5b");
set.add("48c5d84e56a982689f4268ed9b50cded");
set.add("493a84bde424f5946290238998d64873");
set.add("499e8c7c38dd4d8068eefc4eb58d4cf5");
set.add("4a03d963b887b4ade3d574e87d111e9d");
set.add("4a5509929d6991507c6e136178942a2d");
set.add("4a933f8824eba082555515e69d3bfe43");
set.add("4abc93cb926e33fbb97aa0d2ffe7885a");
set.add("4ad536d6aee9fffe1e84c9e75698f5cf");
set.add("4af14f94870a2e3d47dbd78cc05e58a8");
set.add("4b0a7961ee650f518533f03c38ca8320");
set.add("4b166cec69dc9ace3a9f598674c35b3c");
set.add("4b5a632e55f4dbea5435e151145337a7");
set.add("4b797a7d23faae4daf8b2946d6cb24dd");
set.add("4b9e8ea91d6bd67b22be67dd40b871a7");
set.add("4bd7e46dd429e45ddee5736f86d494cc");
set.add("4beec7064114b6e49cc76cc2f71594ec");
set.add("4c3f47c263ea5b933ac5184466573f6d");
set.add("4c9b11094fa43b8a9aaf1a1568bf60c2");
set.add("4ca44906c21909f396774827017e007e");
set.add("4ca7dd633f56f76f794faf958416f4c1");
set.add("4d6956c8d3db98528dfbdafa4a3316b6");
set.add("4d84b18416836b7297b990a408a6eda3");
set.add("4e13b8d5d4a77393b2fbfbaebe9ea1ca");
set.add("4e3b029d124b898f1d11a8d15d2a6688");
set.add("4e9723863a06235d9339cd00912871ed");
set.add("4efdf67cd98424e7cb008dd09b169942");
set.add("4f25dd1fcb4aedb512f24758961d87f9");
set.add("4f86907e557c00d13b42a2393b834d8d");
set.add("4fdb3ba6ebc9b3e9ab133c15312f995a");
set.add("504bbb5991ad94728e6b73c6ddc6c476");
set.add("515f449c1e9fd279dbdadf3cc38fd008");
set.add("51d9a0c78486de462af6a490acea1fcb");
set.add("52032bb8c1acb8bf7320aa73babd2e50");
set.add("5203feb9b0d422a38052d9df4103e3ab");
set.add("5222c37a7e8618d4cb43ce8c4a188129");
set.add("52731882ea73ad5b1d39c25e2969d9aa");
set.add("536af35745c20e4ee25486a31c2fb57c");
set.add("5379086fb93464cbdad4459101ed4d07");
set.add("542f3505366c2d6575e73064aacf536a");
set.add("54350b63fafc31af32bdf49cf0bbfda2");
set.add("5498ead583ab6cd6900a533b1cb69df8");
set.add("553eb9e396b2f304f963b717bb3a9059");
set.add("55c5181d0e1b170cfd05c3a9271b3bc6");
set.add("566ff170b185c5cfd893030c97080451");
set.add("568c906117202c4d4451dfb3be697921");
set.add("56a9926b91222c8943640da0b642d617");
set.add("56fcddb2fc61ab068e1ce98a226fd34d");
set.add("573f9b1aa16e771da95df44fe3a62167");
set.add("5805ae3e1c5fa9f7a604152c40c9d06d");
set.add("5844ffd995e179e21476fe41a72c7e85");
set.add("5866a0ca3348c1b406e4a4a869b183ae");
set.add("5922a04c19e52d4a3844b61b559a89d4");
set.add("5957b399b3380e097b70cfc09bae1bd3");
set.add("59785d3feccf91b7a9fcd96fe5e686de");
set.add("59cc15fde8f2bab7beac6a9542662df3");
set.add("59ef8fd572ad56b7c00092f185512c0a");
set.add("5a26e5d6effb9634941bbdaecf1cc4ce");
set.add("5a94fedb054c29331d22c4442ad619a6");
set.add("5b1a41ab325cdfb925f500868f77e058");
set.add("5b20fd5088ed40d65a52c38fbe314373");
set.add("5b3510c0aa53405e1fbd7a67b3af34fd");
set.add("5b96ce711afb37fb511e70ac12cb717f");
set.add("5bb8c694f0d7e39aceaa7fe7a885a6e1");
set.add("5bc7dae98ed248bc85f4782783c7a383");
set.add("5c1e091a898470db28aaddc968071a00");
set.add("5c603c37c8ae3b7441a49bfdd93a2426");
set.add("5ca4eac1f0b43f03288c19c42e1ccb2b");
set.add("5ced682df2330890f2239e8da8d33061");
set.add("5d437ac21a6da33b77c980abef5af0ac");
set.add("5d4f136bcd4f5f71e0402819f2f666ea");
set.add("5d9d43530d78a07475063de4f6b82578");
set.add("5e8973f53dfe0e36537e7d88ac84cfaa");
set.add("5e8b973df438112549cbd73b38240094");
set.add("5ec17176ac8ca3ffe5c7707d4b33aba0");
set.add("5ecdf016b2374b2029c58dce498548cf");
set.add("5f4d8576e9299aecd4ece33f8b4ffb3d");
set.add("5f5bc13ecb72cde7c4c6e89279e836f0");
set.add("5fc7b23ca79086fde585ac92b8ddfa61");
set.add("5fc8dfad0c6503b16fcbdaf2140f5bd6");
set.add("610b21fa92e08d26b0ebbd27ac406558");
set.add("618c82b1f690b74209a68062f0b7f50e");
set.add("6214548d7b510154960ca3d23da4f38d");
set.add("6244a9533207c9f3d89bd48d2c946d72");
set.add("628475d3f98ce21920983b2335d29771");
set.add("62b5f08d8f9087672b45485f5177b688");
set.add("62c0ca2c1be43447418c673a27f69a53");
set.add("62dd2d23b56d1991111028754f7d5718");
set.add("62fe634a6ec154d4b675a8944ab98a7b");
set.add("638e84fef470490263300ed27293aca9");
set.add("643181e6ca3418a86b5dac6858805839");
set.add("6431d2ee351952b0ca1c8e24aee89d9a");
set.add("64cf9d529b625818f06f910fd5a51ebc");
set.add("64f5901476b159bd9c4f5ed9aa2b4cc7");
set.add("651c5d94aa8b742ea6bf89eb4760d88b");
set.add("6535664e59493ee6581d3ec49d666a05");
set.add("659bd0331a1348d14e9cd72006008d5b");
set.add("659d8f3f58c60862ec21306475d5b86c");
set.add("65ed980ed9e129e301e3c219be11999c");
set.add("661c50f934f8b021101df91c063c2662");
set.add("66289df1c8d41414638696d9847188a7");
set.add("667f3707995e365e210a1bb9e1926455");
set.add("66ff6174d6a5b1c8a40637c8a3a8a7b9");
set.add("673d52c40a3153d07e7a81ad3bf2027c");
set.add("67c803799e8e1d877eb3f713dc775444");
set.add("680708ce5f383f0c7511eb3d7b7209d9");
set.add("68ee06fe147e143b6e1486d292fbc9b4");
set.add("690da28e475ebd2dec079e7de4c60719");
set.add("693db94b6ffb0c1afa7b82499d17b25f");
set.add("6961f9a08f066d0318669a9c9c94017d");
set.add("69a38fb26f994ccc04e84e66441e0287");
set.add("69f5b82d6acf05cee8615ff5d05f7921");
set.add("6a1e040ce59176bcbe4c47654dcf83a7");
set.add("6a26a773a6223c79697e12838582f122");
set.add("6a6e0e4350ef8d4a3489aa4398bd674b");
set.add("6a8abe4a6fe236bf93d9b85681db2c0e");
set.add("6aaddb50ae45f1006852479932dfbd54");
set.add("6adb0778b8930a8e6a2f1e99de2ef074");
set.add("6b54ec7203070bb29e514eb7d684e380");
set.add("6b598530a066271059bc94c1fa0cd7a1");
set.add("6be4f1c5af0ff30131481d009e87133b");
set.add("6be8cb8a938f1ecef2b36c823e8e6ade");
set.add("6bfe9b78813cfa4014e630454f7a06a5");
set.add("6cb4d52135f005a2c7ba8ccc3b8781e3");
set.add("6cd5f8dd36648fcafcfecc8d5b990e9b");
set.add("6cfdb07efc0d3c1f36b2d992263253f9");
set.add("6d95c9c12fe5498af055b013bf7ceb7d");
set.add("6e8f160f1b2b54c3c89b81c4f9a98283");
set.add("6eadec5ff4cb05c8ef1a64d2c94d627b");
set.add("6eceba3c0a19666f5a9adbc13ceb1ae7");
set.add("6f47bff8d62f5bd51cee156f78e8cfcb");
set.add("6f484725ba3abcadfe8fbfb4e6e83db6");
set.add("7031b89c62590b6a41e7ad524bb0a308");
set.add("7058fc792efe7aaddf8a0323bf998f72");
set.add("706e502b5a6db25407c2565457944633");
set.add("70cfd491765d3e4e6a4e4b1ccaf9c343");
set.add("711e7a11c4be36563cae1b71234dc414");
set.add("71794c9ad0e60b6d0dcd44b51c3704f0");
set.add("7193a4c6f286f7b86b18cc7908498d06");
set.add("71b17eeb05fd473e42aa5c4e51e47a15");
set.add("71e0014aeaebda1113a12cecb51fd20c");
set.add("71e2d06eaa0ab3ae59d0f7b7ef20fc31");
set.add("71fe7c7f2a54587c2278b3e630faee56");
set.add("729ba9dde60740e6d5e8140fae54f4c6");
set.add("72b5be92417a4a6a09f5e01c106cf96a");
set.add("72c0c6f5a653bb57a1aba44e7edb202b");
set.add("72f6580c0aa3b101acffce59adf7809b");
set.add("730ac94082f25931179a313af186b335");
set.add("73243d82b8157406558562b5eb70818b");
set.add("73371ae751751a5998c3bc8de577b83e");
set.add("733c6d4df3b6781473ba0a3a169ca74a");
set.add("7376d2568492e6f6c0fadab86e22416b");
set.add("737b791d27930ccba061fa36c4046208");
set.add("73a47e531e9c0ddf5a334c40508f6361");
set.add("73b2859aedfe1bf317841bbc150e0491");
set.add("7413c1de6d5f286055e8244101da306c");
set.add("741bfaabd99301c5503fd36d17912627");
set.add("7423f1b74c8367863a1071bcd0864d42");
set.add("747ddec4bc30cbde2ebefac7b8df466c");
set.add("7494df6c5102bbfb6e48c9b30281325b");
set.add("74c5cb4f540e2c9b27ae60dcc8247eae");
set.add("74f6a218f8877fb76f74eacc9df32fc6");
set.add("751ff3c7f3e77c8d3ba24c568fd11845");
set.add("757c05f3194d6d4b848b83c0e3e5f1a3");
set.add("75f724e20c3f2e26d2de13927fbf78f1");
set.add("761c6d190a411231fccfeef67f08eacf");
set.add("763eddbcb1f6787b3350f65c64b44ba4");
set.add("765a1a5d8f09ddffec749d3a6920c4a7");
set.add("76acd0d7e4d38152771480bedacba209");
set.add("76dc5d4b4f65aacb1dfc1a5a8f61b023");
set.add("771cc4503662f2fc2c5f15f46e7e58b6");
set.add("772a3193a3cf3e18fd2058c3f45c35f8");
set.add("7823311e8d60374d4b37c50d927507c8");
set.add("78282abebd03a40f2dd21f1505a5f789");
set.add("782e12a60bbef943f13f2fa1af1e39f1");
set.add("782e6df5b10a60b26117e0648e76c6c4");
set.add("7875a865fbaf33ba617cdb7c7f0f7140");
set.add("78ee37b2f7acb3d892e54f0e9d2e0c14");
set.add("791f7d21ea119ccd49df31b2f614a0d6");
set.add("795036eafd006f62ee5a68ba1c309865");
set.add("795fcaf2d6d9e1c37a4c283876f26cec");
set.add("79688fa15c424b73454d9cd0a070472f");
set.add("796c759040e72b8efd4630754bd3f30b");
set.add("798ad9ae186a9d89e6f69e065bc22a86");
set.add("7a2a604d923e5bd87846902e47acc438");
set.add("7a39ea82b6b2bb75f9f6a7b817dab9cb");
set.add("7a62872422cf100af636b434f4a0b307");
set.add("7acda66f5d077fa708de7d153882b97c");
set.add("7b1aca3caab3a61147d4ebf5f7971d42");
set.add("7b5bc0bfd0de5126b4d278aa5775abd7");
set.add("7bd0735d3b5d579f0c97e11309a12451");
set.add("7be2fb055d29d5c8e42c559295ee8113");
set.add("7c14e11e0126b310217db2488f898127");
set.add("7c4ab23d9b1db15ea1f49fe017edf346");
set.add("7c6080928783078289d9a473efecc134");
set.add("7ccde35451846731eff4ae16e40f661f");
set.add("7cce66eec1313c11f5b9005db8f2823d");
set.add("7d40723bc0200f13675626309559ce6d");
set.add("7da7fa494528cd0836f9988f3e7ada96");
set.add("7e3bc2bc33f34ad791573e94021381d5");
set.add("7fb1e485fa41841779a0a1f95a2b7cd8");
set.add("809b63d7a153ee60272ffc224766fd72");
set.add("80fc9ff72737286ad64fe7de1524c799");
set.add("82b602bacfe681cee58d5530ac9e8b99");
set.add("82f69b66499f2bc375467ee933fe4577");
set.add("83243e87941f4ec7f8841571dd90b3b2");
set.add("836481fe9bfd7612506e0545bdcf279d");
set.add("83a498353a59dea68538962eb2642ba8");
set.add("83eafb190276935630f43cddf3d78143");
set.add("845c54809778f5b838e32443a7d44072");
set.add("849b5885cbf965447675610ee1d0dca2");
set.add("84a895acdcd487244b6803082036fad7");
set.add("84bdf63a67691194e37082e3f7f6d712");
set.add("84c99be383e4ada00f4e6bd335774655");
set.add("84ed2fb163b5b2525a9a731056ffd144");
set.add("8517e14d6f657f1244c0344d4f1a828b");
set.add("8541aca6dd06f2dc6984d5e1b664900c");
set.add("85cc38b178bd721bf7225144dd301b0f");
set.add("85d00ae1ce88ace2bc6918750a97502f");
set.add("868af0eab556081846fdbff18df40b28");
set.add("871f7fe309f61ec7e45e4b29833349d9");
set.add("878e7848ab58bf9271fc04766e969c8f");
set.add("87b872efe9433147c61d5d2c3dcca14f");
set.add("87cd3518578a2ef76341b33f9c95198f");
set.add("87cd3a0a86f398ba1026cdb969e55090");
set.add("87cdeb3fcaa050209091a1600ce1df11");
set.add("88008ed2e9b600fa2e453390688aaa7e");
set.add("8833c25743e0f9725ca22dbc6e54d1bf");
set.add("88693556ff80aacd354c1948675c0674");
set.add("888664c26a3296f9571d561723b44255");
set.add("88ed07b96422ec99767fb35bf6a51966");
set.add("88ed43ef6f483b9a7e34c34b46335dea");
set.add("8a2e4445364c3c9151dcf4622c6add51");
set.add("8a73ce2e18dacf250a961dac49338407");
set.add("8ba75b207cc0bee8ec624e9f33019161");
set.add("8bc592cc7aaa336637b9c82c43cbb081");
set.add("8c1bdef25d6a6df69c303be204748da9");
set.add("8c8b182ec0845de4a5fed3245e5601ea");
set.add("8c8d724fba729940b1234e58e64125b8");
set.add("8ce47ac01efd8c0ab75ae5371ff0f7ba");
set.add("8e1600a04363c86199d660ccb7085735");
set.add("8eb548ee8bf98a6426f0b5a11e32c88a");
set.add("8ec54a8bd1ab30f324eb0f03ef79d097");
set.add("8ede1653debc5617deae6a7632c18502");
set.add("903594c774fd5be01132f559c00778b4");
set.add("9079d8f7488bca4504d58be0bc76deea");
set.add("909a1f7458c8f1f1138dff9ce019fb6c");
set.add("90b8dd2817509c2374b20a1975ca1a54");
set.add("90d0f3d40769a6219608964628d40e55");
set.add("9104737f888d85480d0cc9aef8587e77");
set.add("9118a19b2abc5d1d624b10f2bceb18bb");
set.add("912e499f9a4a55f11133e01b33542ad1");
set.add("915fcc373ba5d8a13814f236c1a9e4e5");
set.add("918ca652867678984ae1149a3b5467bd");
set.add("91fbebd600bbd88370994b739ae8e1f8");
set.add("92fc949a982c897ca4a17750e2ee4afd");
set.add("93c0446ee508efe75a176035986627cc");
set.add("93d4329e22ed50d3502b2d0bc117baa6");
set.add("93f33bcfa6201057376a3fe53eb29959");
set.add("944b74b5ff9c617312ca2b0868e8cbc2");
set.add("94bacf4caccc653a74a68698be0da4bc");
set.add("9572f2ed73f01766b0ede9ec3d52715a");
set.add("965e3d6087eec8e991175aada15a550a");
set.add("967119411833b80c9ea55f0e64dacad6");
set.add("968c5025a59e782d96682b09c4e94746");
set.add("97824aa7746b63a32ea6d0dedb3e3f84");
set.add("97aa914f28281f67ae3ac5222566c2a0");
set.add("97f5a198489144a2f306456c1a346f9b");
set.add("98a7e979d454d7f46ceb4a4283794d3c");
set.add("98ff8ee9107e864d7c52d631070fff3b");
set.add("993739fad4a47f34eb65e3ee48d15c09");
set.add("99bb411f89eb34ebfa59900de36581fc");
set.add("9a13940746bcf4cbe210150833b2a58b");
set.add("9a3d7af6ccb7d277e3ed582d8932b5db");
set.add("9a76e86b4275099983c5ede78626e0dd");
set.add("9a9caad4a9c674daf41b5cb616092501");
set.add("9ae6b0ad5010301ea610f49e008adf8c");
set.add("9b6033bd4470408ecf2949d88531d6a1");
set.add("9bfc7853ff00c7ea0e2f8972dc2595d4");
set.add("9c8bdd485912f9d9eaaba3d5872be726");
set.add("9cba07b76b4e79c0265deda5df2e2381");
set.add("9e082b9bb6c1361965c0f0e06f051acb");
set.add("9e24dbadcadc67447af65d571ffaee55");
set.add("9e6a5f03a8b524ffa3264a3f32818e1c");
set.add("9ead837b9e4f8c922f74ddbff0d2b88a");
set.add("9fb7aa659c0475d5dc72bb35567247c9");
set.add("a0006978c9a542518b425c0caa67042b");
set.add("a01bdd6575c3cad9f9a4cb8aac9c716a");
set.add("a02500e28eeb7e56e343607a822e2a7e");
set.add("a05c1799e061712459e6c46f533263a6");
set.add("a0799831bfb3f9b77b63c03fad39cce0");
set.add("a0d4911294ccb20c0920a3cc6705f326");
set.add("a11dfa1b02b1671d42b1abc858de2f2e");
set.add("a11e237bd6d3c4a4ee8a7ee791444ad3");
set.add("a148d83d50cf0852f6c08ceacbea0296");
set.add("a1d8b81c03860585fb40613e828c1b2e");
set.add("a20c867fdbb93bbe1d1231d9a8ea76c5");
set.add("a21e0795fe0977d50a4496ba60e685e1");
set.add("a260bb11468a2252a8dedff81f5672fd");
set.add("a2b01bf43bc1d403e6ab3a9b71f32484");
set.add("a2c15ded3e16d9aa12b9c07f3383c098");
set.add("a360659a62e2e34f4edc89ce5e8b8a0c");
set.add("a3a985e0ae5a9c96c74b8ee647101439");
set.add("a3bf05e31288667a43b4c39cc0604c97");
set.add("a427397e35b28706c5b8aa50e8f92a1c");
set.add("a432e1b27b7d9814368d8f4071cf2dd0");
set.add("a4b4800082feb6fcaf1cd56dda3a28c6");
set.add("a4b83742cb45f1dd9130250cd72c460e");
set.add("a5a8b20a222bd51b707e50303fdae33a");
set.add("a5cf16d12d611ddc5ae1b010083690ad");
set.add("a67b1720a7f123bb943c3f1ee74b8f00");
set.add("a6b31c2e971254f85e231653abdc3a06");
set.add("a6f9fe8c867cbef07730db4d1d461960");
set.add("a706de20cf1a31e379d98807d1cb2060");
set.add("a7b5467023706646a6b8ce2602bba226");
set.add("a7bb7f7f68b538fb778856d4fbda50b7");
set.add("a7fee39f2151056370c57d383e486348");
set.add("a84a5f90f1083075081f135c74314bff");
set.add("a8a6b73342c6a92638e41b86e6838958");
set.add("a8f1c8b28c017186778e3074637e52ef");
set.add("a90e513d9b2d0f55b875c3229e2d9816");
set.add("a9e697026e08d1a8765497a9569b04e6");
set.add("aa3218984177ce38bfdf06e38fbaa64b");
set.add("aaa0291aa11c304b3a2300d4025db74d");
set.add("aad63a3685d9f906a7c6c8716d626c0b");
set.add("aafee591c7a3ae5f3c4f44f2d0f8a70f");
set.add("ab85503c9acb730fcb9ed2a4dd74e2d7");
set.add("ab8ad454409604808d1b444b068e602d");
set.add("ac4c8af4d29676c8c79ac9ef180fc5df");
set.add("ac4cd34387b04786cc5315b464006ec8");
set.add("ac9c443698ac77bcb3a73a959f6ca0f0");
set.add("acde934989eba2c7fef7cce095ce85c7");
set.add("ad053830e5d0bb7e736ab98a8f3f1612");
set.add("ad08d0d2d84298deb08b4c4a1cf62f39");
set.add("ad0a1f2424a1b831f9777e638e8f829a");
set.add("add039636134cb123908df5053304f3e");
set.add("adf89cbcb01a2ec6d4afb24914790a67");
set.add("ae1ae7c31f46325ce6a28104fa7070e6");
set.add("af8c83664fd6eec8089ef1992aec463f");
set.add("afcd59e32572ecb7ebe2d9c993d5fa9d");
set.add("b012115b4276791c5049dace174933f7");
set.add("b218489d2d4d7ddbfee2f073e238ff30");
set.add("b251290e1d8690953d9cc0c1ea3bac6f");
set.add("b2843a551894de318b80f790565dcfe3");
set.add("b2a414aeb8800edfa8945863ffa5fbc9");
set.add("b2d68ad2619bbb15a827b7baca6677b0");
set.add("b2fe203ee319ae28b9ccdad26a8f21de");
set.add("b33afd95fbd9aae903bbe7cb853cbbf3");
set.add("b385f0f86168fea4f5f456b7700a8ffe");
set.add("b3bd462a51847d58ed348f17c8718dca");
set.add("b3d1befe2272f354b85f0ca5a3162dc8");
set.add("b3f50d0da11772487101b44ae8aeb4ac");
set.add("b42625f51295803ae1f99daf241c0bd0");
set.add("b49cdae29a3394a25058e94b4eb5573c");
set.add("b4ce8f683ec172aecf22cf8e516cce05");
set.add("b4ffd04e41c1b8757149777a894f33f2");
set.add("b5a1510fcf6dd596e87f497bfd5317bb");
set.add("b5a75d8c18db0a96a3423e06554122c8");
set.add("b5d312d32267bd15ee43f36077feefe9");
set.add("b6645bb07f58754b8838d54e24562c06");
set.add("b69831350ae6a3dfc18c0c05db0c25a8");
set.add("b6b70e569be8de2fdecf285730319735");
set.add("b6ee0ea7d82d3d7e0ab8bc62057c0385");
set.add("b707a076a44ca9b3e6b8dc1dcde7d877");
set.add("b77df6081bbeb4da02c075befb4beb9b");
set.add("b7bdcedd416cccc742643e8e244f6282");
set.add("b7ea4565381c6dc534cf0af8305f27ac");
set.add("b7f3fb01d8c41525b103fc5faba23362");
set.add("b80bf674f28284a3614596800ec02b3a");
set.add("b81ab08e53854aba9462ebbaee1ff248");
set.add("b87e12381d354360f7945299ad92a4d2");
set.add("b8bd5737f81fddbaf120ebe43a0010e4");
set.add("b92f1e45fdb62c1fd6d7f3e774b4b610");
set.add("b9769bfc0d93a570d94fa130de750b1f");
set.add("b980c7a501ce63ebb6e20b04348298b7");
set.add("b9e4b006db3e1597b21fb7aba13f79c2");
set.add("ba031cf2febc03ddbff278200dca45a0");
set.add("bb0f54037f5ab70e96c7d8ba7f57ca4b");
set.add("bb2eb6b3f7383d7ef521409fa7710385");
set.add("bb3eb6a5dbe0582b31f35e5dc2b457a7");
set.add("bbc22cc7f6851e06fadfac40a6225419");
set.add("bc4d886813fe2eba9ccd7bef0b4d05ca");
set.add("bc8162e261658ece13f8bc61aa43ab74");
set.add("bc89ec14f4629c3afe1e286b07e588f6");
set.add("bccdb576cb50b44ae34806c2a2781c52");
set.add("bd10772f1554ccd6e245d6869d84efe8");
set.add("bd969e90ff4b1362e2f24553124a66cc");
set.add("bde145f138ed13399b8a747da20c1826");
set.add("be11a726b56813c4b1aea0574c8302b2");
set.add("be1cfa6a82eb4fbf7186efd6ddbb0161");
set.add("be5f3dcf0badef84475741cc47e2ddc0");
set.add("bf316e6ad7e72f7dc4033207dd033c76");
set.add("bfadbf9c4cde6071e54e43a5da64aca9");
set.add("c029503ea85e7a7742d826bc184d65ce");
set.add("c049499ca03fd69115352e5d4be72de7");
set.add("c0524ddd036991b4718f6ab0ab4e4f42");
set.add("c056cf25df6751f7bb8a94bc4f64750f");
set.add("c0a9c2fd4f0ed2b8f2bdc7f2c0a7a7ce");
set.add("c165e480073bcdccb3fad1c5e507159f");
set.add("c24ac1ab205eb3fbd1c64841f0f288d6");
set.add("c26987c1c7e95810bbb6f2e284861494");
set.add("c295b3b2493aff880daac1532a063b72");
set.add("c2b18390691697037d5698b683eee361");
set.add("c2cd680e3032ce3a70d3bffdb7d0582f");
set.add("c2defcfb93d217c4be28aa27ec62978b");
set.add("c332adf9d689dcbbb38fead7098781b3");
set.add("c4d8f1baafe99501b0d80e8a9c8c3086");
set.add("c4e5ca3e96b219539e3e61c3c4fbe5a9");
set.add("c5e1448d1fb24ebcef161ee65f21a725");
set.add("c60db0ccfc2a22a152f7470505eef8d3");
set.add("c65e2561352e75a66b5738268b1d126a");
set.add("c69c01ed9b781941561c3a9dcfacf7ca");
set.add("c76bb0011d2519fc9e3af85de593e8a9");
set.add("c7a946bb164a3f642e4c5f1b7af337f1");
set.add("c833820441cbbf28a25d1ea7934ad6f8");
set.add("c8762972b9325b7ec040c782aa9414d0");
set.add("c8b1563c45f4fd4dc8ba5fafd5c566d2");
set.add("c8f2a5a0533de5eae8d1d01da8fcfc1c");
set.add("c94045226f625ab9a507552f64892fbe");
set.add("ca365baface31f6167328e65a0aec03b");
set.add("ca3dc74a6eb57042ea911afa05b1021b");
set.add("ca5b57fca35c5bfa4281802b13381d0c");
set.add("cab05efb1584bddbc5e4f064c1628a13");
set.add("cad2db4a8a73a867a6cdacceec4044ac");
set.add("cb6b65a06bbb9ba5536936188a08d836");
set.add("cba49b7c8d1982635866a32956861df3");
set.add("cbd94e882bdb84ec79ea2bebc1eb4aed");
set.add("cc49ecf163d383488b44dbb17dd5b4d9");
set.add("cc4bcc37de4d7bf87acea95ac914997e");
set.add("cc9300ecd7f2799c750ca3efcde7ce20");
set.add("ccccf43f691ed8bb94ac27d3eab98659");
set.add("cd0d8952d3e5742f4bf62195e4b385ec");
set.add("cd57964f0a86f3c9afbcca44733081d2");
set.add("cd80d4f5366cd31ae31e377c636a773a");
set.add("cd987a30667f7ff04a5852fd3f15fe3b");
set.add("ce16d46a26c771b1adbff14cc7272bf2");
set.add("ce43a9cf2d81a1e09237ed2279ca04be");
set.add("ce7a500ffd8f5925bea7a48401fae95e");
set.add("cf3894401e317e2545d0ae284802291f");
set.add("cf779cafecefb6bae5137bb77582e7e2");
set.add("cfdbc0be3829a6d55a5403ad92c77bcf");
set.add("cff24b5ef480cd58324e927e4ba0ed37");
set.add("d05a83622e817871306a3878a9d867e9");
set.add("d060e6662cda17c2c523c74963d2d556");
set.add("d0b058971d8a20e840de043c68c140b1");
set.add("d0c122a8a62cb5728f1423816b26c49f");
set.add("d141ed3ad5b33d6f92c976ad8d518b3b");
set.add("d15c7bdb5fc7b9316d1cc60a85acdc64");
set.add("d1c8ba5392f01f13dfef28e4ecd11cc2");
set.add("d1e661c0bfe1c23ca4e91cfa0a40a9d3");
set.add("d25a8aef0d42d7a31783e3da52dd4ee8");
set.add("d26ef38d4ea39ba85171f444a8947443");
set.add("d30b7d8663c7852f2be21d886b79a6eb");
set.add("d315c862f290b99466e3d406d3104413");
set.add("d4007ee1fd6ede4a148bdca7ab778dd3");
set.add("d4805374b5f1ece75c0dd51d6283a0f6");
set.add("d55b77d76f6ece8bbd33cb2afdbd040f");
set.add("d570cb3cbfe88dfdf086bb1ab9ef76f8");
set.add("d587ebc9902ba2620d52a610441470cc");
set.add("d6196bfd55d758dd5a4899ce84cea85b");
set.add("d6815ec0b3e12fea0f470c01c0da3888");
set.add("d68971ffd271de2ddde6ac10c2817451");
set.add("d68ff175d25d083eee4f31bf0701a0d8");
set.add("d7fca16ff4e4ed99b72046f99533eef3");
set.add("d815d7a8dd0446ba21ddbc37d3733f36");
set.add("d8d40312d0751951b4c45f2100379950");
set.add("d97e0d0fbea2a015d5e7ea229b99a6c3");
set.add("d98c6a23fafafb0f9981f2123b1a1938");
set.add("d99d6bfaede491ceae109a644cf54098");
set.add("da0f523e815ce990a3a5f5b5574cec4a");
set.add("da686b7f2a26a36051b273bbabdd7ecc");
set.add("dadb54d4a8ba762a8d2a2fad5fcd7582");
set.add("db03b5399e1a324660934ad81036d987");
set.add("db29f48ac45b41ad389b1b4e8e30f553");
set.add("db98a87e4be1e5b59f589335e1509996");
set.add("db9be69a1dd929be69406f9e75533fd3");
set.add("dba45dfda4d06aebf3adc194ca4ec35d");
set.add("dc339714def04ec3e5e280faec8445e5");
set.add("dc3fb757715c96f7c629b7712d87ca61");
set.add("dc5dabf20b3fbee0d3a0b1d800c74d4f");
set.add("dcc7b2c56f358641ea9a73c6a81479f5");
set.add("dd922d98ecf5096482946d8671b647e3");
set.add("dda8214d7b53392f8ed9fbe4178e90b9");
set.add("ddd37af5af0a69bed06bc50cc0a6f4c2");
set.add("de8d217fad9883d9bfdee5af01472971");
set.add("def34fc0fd41527b300d3181a70cdecf");
set.add("df00d9361332c48cba23bfcd41e799d4");
set.add("df35772f10769bc28701c488d33e89b2");
set.add("df769f9dc2477135b0c4320e7e7b4c2f");
set.add("df95377a3f69b8fbe5dcdfa041491717");
set.add("df98c3766238aa84f9a9dd92cd73fe72");
set.add("dfd019d69302047a67434458d0fa8952");
set.add("e037a9e26a8b319437ab7c962714dc56");
set.add("e0d2f02d29a965fafd85a4ae6ad37246");
set.add("e10e651cd85e41be3402f51885bbf107");
set.add("e162d7b2e436ae6d369f4fbaf53af4b4");
set.add("e176177a2b64669a6bcd1cf8beb35df2");
set.add("e194252a63a3433b5a5278f68678b7dc");
set.add("e19d16546c555d073454ea56ece1cbd6");
set.add("e1cb375938189d4090b000ab41c77a06");
set.add("e1ce2428389f0c2356881e626f161ccc");
set.add("e27c22419443eb612af1620f4c8be007");
set.add("e2a5adcdd7b01611736b6b74f8c506ee");
set.add("e3084721ba7ae53996337e82c5f469ab");
set.add("e35f4cccfe57bdd7338dadeba55609f1");
set.add("e39a2ef2eaaaf7ba74623f14c917ee1d");
set.add("e3e11cf57dc3f1c6ca59acb06370698f");
set.add("e4557f7733332200116b753408cdb966");
set.add("e48c96d6025b38addad2278f24c963ef");
set.add("e48db7db130af48cccb2d830d3cbaa14");
set.add("e4d169990e34bfeab0c6a780d6a49d58");
set.add("e4ea1f6b01c9cdcf9e550792ed336384");
set.add("e4ee8ada1fbbe886fb25a7f484609690");
set.add("e54846d325334547923d8b64da70f529");
set.add("e56ccf6eca77d62dde88c895abfc1c1a");
set.add("e591790779db1c866b179d6f85b09dda");
set.add("e5bec4799ceef43816054f92de9652b5");
set.add("e5db5832d59e14d6999144fa8cd10e3f");
set.add("e611fb9e857f9bee391056e1f971a0aa");
set.add("e6321fdd099d70352883b45f6c2a20a9");
set.add("e682fc42ee7ecdbf595116293cbe8a6b");
set.add("e6ae48418d10883fe9657075b476274d");
set.add("e6fa2a139e5c56f8f483aaeeee0b7fbb");
set.add("e77d3c78240ec60f7f4dd67a2e71085a");
set.add("e78ad15fb1fd450f9221147e458b1abd");
set.add("e7a1dc89a6cab821776ea61fe6ba10f4");
set.add("e7df074666f6caa44b798342bdab6230");
set.add("e7f25163d78c2c658300cd0f9a8a3b04");
set.add("e80f22347419025053de7da1f07912ec");
set.add("e828123fa3cdf86dc0fe1b5c86d7c87d");
set.add("e8764c00097a0a1254f43a16c98a1d7f");
set.add("e89df60deddf270cbc2232bbe26420d1");
set.add("e8d289f3c1aa961cf4ac8d164e286dde");
set.add("e9051eac7829dc1a95987230fb21d2d9");
set.add("e90846d2c3e16de5ed5dff4c21356edd");
set.add("e954907cdfba1cf07f19f64af5cf45b1");
set.add("e96e004e988b8e36b2ab9ed1b0f65649");
set.add("e984d3924451d3498a3efccd845a77fe");
set.add("e98b4097ddb057755e561c33a0f3428d");
set.add("e9a2ba17cc4b93063d61813359fd6798");
set.add("ea90b42f6ada6e0ac5d179af4129022d");
set.add("eab4231af5b3ffab13f9a04b8aef0fad");
set.add("eac17a7d30d24e60e6b2ce10059b37a0");
set.add("eaf3af4d0b61df60ee8fe3a808de6ffd");
set.add("eb3178d36a578fd8b430ea82429ee145");
set.add("eb4fbf9835a3584e6015260f0dff2174");
set.add("ec3a9437b1481db4d8cbc3b4fc0888a1");
set.add("ec47d133c23dba3d015ae730a1b9659f");
set.add("ec6d321581a133fee9766eedff4db9d6");
set.add("eca16f6d986bd893af3c4a97b99df623");
set.add("ecf09182c51e5110d636828364ba3ee6");
set.add("ecfbf6f7017f0981ba6d30331c9dc870");
set.add("ed1eaef061f0d30503a64f27d8ea2825");
set.add("ed2e4441ad7dcbe0a5d3e62bc31aa9bc");
set.add("ed6304572c0f1283fd06f9c09ef32b61");
set.add("ed7d650fc0f5de8c9da107e53025f220");
set.add("ef405c5b0de638c01cf57c379aaff45b");
set.add("ef5ec03860cd32e910a3ddb2d8740300");
set.add("efdc8c21ee843f98a0dc08ef694b6db7");
set.add("f0111af67e822944e1dc1ab24e5b8458");
set.add("f0e8026289bc1f9109b25f4599556aaf");
set.add("f0ff5a7aa6f4e8785fa406636618c01d");
set.add("f19a809facb46a7a094c17039126eb3e");
set.add("f1c7524d454c25cdd837662a80709030");
set.add("f202d26f911074235ac8e4a5c1ed4dad");
set.add("f2250dd8736aa007a7e2530dca1c6081");
set.add("f2e9d36561ed03eb23d38005919625d2");
set.add("f303e8a2a96635d2f44c414229c349bb");
set.add("f35b8eac398bae58ba622ef643f64aa2");
set.add("f3a37dbd51e59af2801272fffe457d64");
set.add("f3c4afc965427977685da607f8a6edca");
set.add("f468c204661ab47379613a1d03f56571");
set.add("f4ff8d1667c95377ac96e870170bfe64");
set.add("f585dccae7fae67affbf500ecf9a3839");
set.add("f59a4193ec432bd26f780a6b74b8be38");
set.add("f5d44e9d1523c3e6834108d9c76b2da9");
set.add("f69f58acf2b80f5dc29682c64de8da7f");
set.add("f6adafaf98b92438e1ad916e51a65366");
set.add("f6f175a7910c5039d0fa51393c920df8");
set.add("f71a00225b1abf1dddfcace79d0345a2");
set.add("f7446eb342242f011c118bb3439335a0");
set.add("f76e6e86c9b0d84638c1c26c55b16cc4");
set.add("f775463704e3d13817abd9674d62f468");
set.add("f80df9b85c1219fd2030ada584fbfc35");
set.add("f843fa1d0cd00122fcbcfd7caf1cb8ca");
set.add("f88e05d8303a6e5dfbd60ceed3988d78");
set.add("f92dbcd91aac65e0770f5fe947fc5a80");
set.add("f9512c5cc198adeff74fed3d4b0f4509");
set.add("f9e1ffe33f3676d0e39bc24e63cf3a99");
set.add("fa492225fbf03ad58ee88b6d84914583");
set.add("fa6279cc58de3fe6c617559684afec4f");
set.add("fb2a4db1a1a68dae79653dd2e32ade50");
set.add("fb2bc93f011d62ac03aed40d92b26ba2");
set.add("fb9b6d2d2d5e3c219e0163092181e014");
set.add("fbdc7fc274e5c71226372606beedb706");
set.add("fbe25dc54e2761c2c5e9f1f3a98d7f0f");
set.add("fbec5f910872b333be655c5143b1cb37");
set.add("fc372707722b210b257ef9e2f731edc3");
set.add("fcedc7e1d4fc17c7c4f2c6f6c7a820e0");
set.add("fcff88f351f2535dcbab726dec9060ee");
set.add("fd15d45e5f876ac3ff10cef478077e8b");
set.add("fd21ff84af0fe74de102f1985d068dee");
set.add("fd30f89057cd8ad151d612def38afb41");
set.add("fdab6eed0ecadf924ae128f25e8b1a10");
set.add("fdced723077daed39d0e4da044f75d64");
set.add("fddbc361461ae318e147e420a805a428");
set.add("fdee78ddeb6f567a636b9850f942256f");
set.add("fe858217631f3aaf02d8aaf849c7b2c9");
set.add("fec4bbfe3563397790d48ce6f5b48260");
set.add("ff73d7804897f4e1624a3b5571e48fbb");
set.add("ff78c3b27889d5365a03b3a3fd3a4c1e");
set.add("ffac65c383eb053e54b267fe4dfd2141");
DIGESTS = Collections.unmodifiableSet(set);
}
private PreferredMotorDigests() {
// Prevent instantiation
}
}

View File

@ -8,6 +8,7 @@ import java.util.List;
import net.sf.openrocket.file.RocketSaver; import net.sf.openrocket.file.RocketSaver;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorDigest;
import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.MotorMount;
@ -124,7 +125,10 @@ public class RocketComponentSaver {
elements.add(" <type>" + motor.getMotorType().name().toLowerCase() + "</type>"); elements.add(" <type>" + motor.getMotorType().name().toLowerCase() + "</type>");
} }
if (motor instanceof ThrustCurveMotor) { if (motor instanceof ThrustCurveMotor) {
elements.add(" <manufacturer>" + RocketSaver.escapeXML(((ThrustCurveMotor) motor).getManufacturer().getSimpleName()) + "</manufacturer>"); ThrustCurveMotor m = (ThrustCurveMotor) motor;
elements.add(" <manufacturer>" + RocketSaver.escapeXML(m.getManufacturer().getSimpleName()) +
"</manufacturer>");
elements.add(" <digest>" + MotorDigest.digestMotor(m) + "</digest>");
} }
elements.add(" <designation>" + RocketSaver.escapeXML(motor.getDesignation()) + "</designation>"); elements.add(" <designation>" + RocketSaver.escapeXML(motor.getDesignation()) + "</designation>");
elements.add(" <diameter>" + motor.getDiameter() + "</diameter>"); elements.add(" <diameter>" + motor.getDiameter() + "</diameter>");

View File

@ -0,0 +1,30 @@
package net.sf.openrocket.gui.dialogs.motor.thrustcurve;
import java.util.Comparator;
import net.sf.openrocket.motor.ThrustCurveMotor;
/**
* Compares two ThrustCurveMotor objects for quality.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class ThrustCurveMotorComparator implements Comparator<ThrustCurveMotor> {
@Override
public int compare(ThrustCurveMotor o1, ThrustCurveMotor o2) {
return calculateGoodness(o2) - calculateGoodness(o1);
}
private int calculateGoodness(ThrustCurveMotor motor) {
/*
* 10 chars of comments correspond to one thrust point, max ten points.
*/
int commentLength = Math.min(motor.getDescription().length(), 100);
return motor.getTimePoints().length * 10 + commentLength;
}
}

View File

@ -13,7 +13,6 @@ import javax.swing.JDialog;
import javax.swing.JPanel; import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.database.ThrustCurveMotorSet;
import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.GUIUtil;
@ -29,7 +28,7 @@ import org.jfree.data.xy.XYSeriesCollection;
public class ThrustCurveMotorPlotDialog extends JDialog { public class ThrustCurveMotorPlotDialog extends JDialog {
public ThrustCurveMotorPlotDialog(ThrustCurveMotorSet motorSet, ThrustCurveMotor selectedMotor, Window parent) { public ThrustCurveMotorPlotDialog(List<ThrustCurveMotor> motors, int selected, Window parent) {
super(parent, "Motor thrust curves", ModalityType.APPLICATION_MODAL); super(parent, "Motor thrust curves", ModalityType.APPLICATION_MODAL);
JPanel panel = new JPanel(new MigLayout("fill")); JPanel panel = new JPanel(new MigLayout("fill"));
@ -73,21 +72,19 @@ public class ThrustCurveMotorPlotDialog extends JDialog {
// Create the plot data set // Create the plot data set
XYSeriesCollection dataset = new XYSeriesCollection(); XYSeriesCollection dataset = new XYSeriesCollection();
List<ThrustCurveMotor> motors = motorSet.getMotors();
// Selected thrust curve // Selected thrust curve
int index = motors.indexOf(selectedMotor);
int n = 0; int n = 0;
dataset.addSeries(generateSeries(selectedMotor)); if (selected >= 0) {
renderer.setSeriesStroke(n, new BasicStroke(1.5f)); dataset.addSeries(generateSeries(motors.get(selected)));
if (index >= 0) { renderer.setSeriesStroke(n, new BasicStroke(1.5f));
renderer.setSeriesPaint(n, ThrustCurveMotorSelectionPanel.getColor(index)); renderer.setSeriesPaint(n, ThrustCurveMotorSelectionPanel.getColor(selected));
} }
n++; n++;
// Other thrust curves // Other thrust curves
for (int i = 0; i < motors.size(); i++) { for (int i = 0; i < motors.size(); i++) {
if (i == index) if (i == selected)
continue; continue;
ThrustCurveMotor m = motors.get(i); ThrustCurveMotor m = motors.get(i);

View File

@ -18,6 +18,7 @@ import java.util.prefs.Preferences;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -56,6 +57,7 @@ import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.GUIUtil;
import net.sf.openrocket.util.Icons; import net.sf.openrocket.util.Icons;
import net.sf.openrocket.util.Prefs; import net.sf.openrocket.util.Prefs;
import net.sf.openrocket.utils.MotorCorrelation;
import org.jfree.chart.ChartColor; import org.jfree.chart.ChartColor;
import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartFactory;
@ -71,6 +73,8 @@ import org.jfree.data.xy.XYSeriesCollection;
public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelector { public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelector {
private static final LogHelper log = Application.getLogger(); private static final LogHelper log = Application.getLogger();
private static final double MOTOR_SIMILARITY_THRESHOLD = 0.95;
private static final int SHOW_ALL = 0; private static final int SHOW_ALL = 0;
private static final int SHOW_SMALLER = 1; private static final int SHOW_SMALLER = 1;
private static final int SHOW_EXACT = 2; private static final int SHOW_EXACT = 2;
@ -89,6 +93,10 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
private static final Color NO_COMMENT_COLOR = Color.GRAY; private static final Color NO_COMMENT_COLOR = Color.GRAY;
private static final Color WITH_COMMENT_COLOR = Color.BLACK; private static final Color WITH_COMMENT_COLOR = Color.BLACK;
private static final ThrustCurveMotorComparator MOTOR_COMPARATOR = new ThrustCurveMotorComparator();
private final List<ThrustCurveMotorSet> database; private final List<ThrustCurveMotorSet> database;
private final double diameter; private final double diameter;
@ -99,6 +107,8 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
private final JTable table; private final JTable table;
private final TableRowSorter<TableModel> sorter; private final TableRowSorter<TableModel> sorter;
private final JCheckBox hideSimilarBox;
private final JTextField searchField; private final JTextField searchField;
private String[] searchTerms = new String[0]; private String[] searchTerms = new String[0];
@ -114,6 +124,7 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
private final JLabel launchMassLabel; private final JLabel launchMassLabel;
private final JLabel emptyMassLabel; private final JLabel emptyMassLabel;
private final JLabel dataPointsLabel; private final JLabel dataPointsLabel;
private final JLabel digestLabel;
private final JTextArea comment; private final JTextArea comment;
private final Font noCommentFont; private final Font noCommentFont;
@ -212,9 +223,21 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
scrollSelectionVisible(); scrollSelectionVisible();
} }
}); });
panel.add(filterComboBox, "spanx, growx, wrap para"); panel.add(filterComboBox, "spanx, growx, wrap rel");
hideSimilarBox = new JCheckBox("Hide very similar thrust curves");
GUIUtil.changeFontSize(hideSimilarBox, -1);
hideSimilarBox.setSelected(Prefs.getBoolean(Prefs.MOTOR_HIDE_SIMILAR, true));
hideSimilarBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Prefs.putBoolean(Prefs.MOTOR_HIDE_SIMILAR, hideSimilarBox.isSelected());
updateData();
}
});
panel.add(hideSimilarBox, "gapleft para, spanx, growx, wrap para");
// Motor selection table // Motor selection table
model = new ThrustCurveMotorDatabaseModel(database); model = new ThrustCurveMotorDatabaseModel(database);
@ -402,6 +425,14 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
dataPointsLabel = new JLabel(); dataPointsLabel = new JLabel();
panel.add(dataPointsLabel, "wrap para"); panel.add(dataPointsLabel, "wrap para");
if (System.getProperty("openrocket.debug.motordigest") != null) {
panel.add(new JLabel("Digest:"));
digestLabel = new JLabel();
panel.add(digestLabel, "w :300:, wrap para");
} else {
digestLabel = null;
}
comment = new JTextArea(5, 5); comment = new JTextArea(5, 5);
GUIUtil.changeFontSize(comment, -2); GUIUtil.changeFontSize(comment, -2);
@ -461,7 +492,9 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
return; return;
if (e.getButton() == MouseEvent.BUTTON1) { if (e.getButton() == MouseEvent.BUTTON1) {
// Open plot dialog // Open plot dialog
ThrustCurveMotorPlotDialog plotDialog = new ThrustCurveMotorPlotDialog(selectedMotorSet, selectedMotor, List<ThrustCurveMotor> motors = getFilteredCurves();
ThrustCurveMotorPlotDialog plotDialog = new ThrustCurveMotorPlotDialog(motors,
motors.indexOf(selectedMotor),
SwingUtilities.getWindowAncestor(ThrustCurveMotorSelectionPanel.this)); SwingUtilities.getWindowAncestor(ThrustCurveMotorSelectionPanel.this));
plotDialog.setVisible(true); plotDialog.setVisible(true);
} }
@ -488,7 +521,7 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
// Sets the filter: // Sets the filter:
int showMode = Prefs.getChoise("MotorDiameterMatch", SHOW_MAX, SHOW_EXACT); int showMode = Prefs.getChoise(Prefs.MOTOR_DIAMETER_FILTER, SHOW_MAX, SHOW_EXACT);
filterComboBox.setSelectedIndex(showMode); filterComboBox.setSelectedIndex(showMode);
@ -500,7 +533,6 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
} }
@Override @Override
public Motor getSelectedMotor() { public Motor getSelectedMotor() {
return selectedMotor; return selectedMotor;
@ -590,15 +622,21 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
launchMassLabel.setText(""); launchMassLabel.setText("");
emptyMassLabel.setText(""); emptyMassLabel.setText("");
dataPointsLabel.setText(""); dataPointsLabel.setText("");
if (digestLabel != null) {
digestLabel.setText("");
}
setComment(""); setComment("");
chart.getXYPlot().setDataset(new XYSeriesCollection()); chart.getXYPlot().setDataset(new XYSeriesCollection());
return; return;
} }
List<ThrustCurveMotor> motors = selectedMotorSet.getMotors(); // Check which thrust curves to display
List<ThrustCurveMotor> motors = getFilteredCurves();
final int index = motors.indexOf(selectedMotor); final int index = motors.indexOf(selectedMotor);
// Update the thrust curve selection box
curveSelectionModel.removeAllElements(); curveSelectionModel.removeAllElements();
for (int i = 0; i < motors.size(); i++) { for (int i = 0; i < motors.size(); i++) {
curveSelectionModel.addElement(new MotorHolder(motors.get(i), i)); curveSelectionModel.addElement(new MotorHolder(motors.get(i), i));
@ -613,6 +651,8 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
curveSelectionLabel.setEnabled(false); curveSelectionLabel.setEnabled(false);
} }
// Update thrust curve data
totalImpulseLabel.setText(UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit( totalImpulseLabel.setText(UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(
selectedMotor.getTotalImpulseEstimate())); selectedMotor.getTotalImpulseEstimate()));
avgThrustLabel.setText(UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit( avgThrustLabel.setText(UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit(
@ -626,6 +666,9 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
emptyMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit( emptyMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
selectedMotor.getEmptyCG().weight)); selectedMotor.getEmptyCG().weight));
dataPointsLabel.setText("" + (selectedMotor.getTimePoints().length - 1)); dataPointsLabel.setText("" + (selectedMotor.getTimePoints().length - 1));
if (digestLabel != null) {
digestLabel.setText(MotorDigest.digestMotor(selectedMotor));
}
setComment(selectedMotor.getDescription()); setComment(selectedMotor.getDescription());
@ -656,6 +699,32 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
} }
private List<ThrustCurveMotor> getFilteredCurves() {
List<ThrustCurveMotor> motors = selectedMotorSet.getMotors();
if (hideSimilarBox.isSelected()) {
List<ThrustCurveMotor> filtered = new ArrayList<ThrustCurveMotor>(motors.size());
for (int i = 0; i < motors.size(); i++) {
ThrustCurveMotor m = motors.get(i);
if (m.equals(selectedMotor)) {
filtered.add(m);
continue;
}
double similarity = MotorCorrelation.similarity(selectedMotor, m);
log.debug("Motor similarity: " + similarity);
if (similarity < MOTOR_SIMILARITY_THRESHOLD) {
filtered.add(m);
}
}
motors = filtered;
}
Collections.sort(motors, MOTOR_COMPARATOR);
return motors;
}
private void setComment(String s) { private void setComment(String s) {
s = s.trim(); s = s.trim();
if (s.length() == 0) { if (s.length() == 0) {
@ -721,18 +790,20 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec
return set.getMotors().get(0); return set.getMotors().get(0);
} }
// Find which motor has been used the most recently // Find which motor has been used the most recently
List<ThrustCurveMotor> list = set.getMotors();
Preferences prefs = Prefs.getNode(Prefs.PREFERRED_THRUST_CURVE_MOTOR_NODE); Preferences prefs = Prefs.getNode(Prefs.PREFERRED_THRUST_CURVE_MOTOR_NODE);
for (ThrustCurveMotor m : set.getMotors()) { for (ThrustCurveMotor m : list) {
String digest = MotorDigest.digestMotor(m); String digest = MotorDigest.digestMotor(m);
if (prefs.getBoolean(digest, false)) { if (prefs.getBoolean(digest, false)) {
return m; return m;
} }
} }
// No motor has been used, use heuristics to select motor // No motor has been used
// TODO: CRITICAL: Heuristics Collections.sort(list, MOTOR_COMPARATOR);
return set.getMotors().get(0); return list.get(0);
} }

View File

@ -280,12 +280,11 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor> {
throw new BugException("Could not compute burn start time, maxThrust=" + maxThrust + throw new BugException("Could not compute burn start time, maxThrust=" + maxThrust +
" limit=" + thrustLimit + " thrust=" + Arrays.toString(thrust)); " limit=" + thrustLimit + " thrust=" + Arrays.toString(thrust));
} }
if (MathUtil.equals(thrust[pos], thrust[pos + 1])) { if (MathUtil.equals(thrust[pos - 1], thrust[pos])) {
// For safety // For safety
burnStart = (time[pos] + time[pos + 1]) / 2; burnStart = (time[pos - 1] + time[pos]) / 2;
} else { } else {
burnStart = MathUtil.map(thrustLimit, thrust[pos], thrust[pos + 1], burnStart = MathUtil.map(thrustLimit, thrust[pos - 1], thrust[pos], time[pos - 1], time[pos]);
time[pos], time[pos + 1]);
} }
@ -319,9 +318,9 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor> {
double t0 = time[pos]; double t0 = time[pos];
double t1 = time[pos + 1]; double t1 = time[pos + 1];
double f0 = thrust[pos]; double f0 = thrust[pos];
double f1 = thrust[pos]; double f1 = thrust[pos + 1];
totalImpulse += (f0 + f1) / 2 * (t1 - t0); totalImpulse += (t1 - t0) * (f0 + f1) / 2;
if (t0 < burnStart && t1 > burnStart) { if (t0 < burnStart && t1 > burnStart) {
double fStart = MathUtil.map(burnStart, t0, t1, f0, f1); double fStart = MathUtil.map(burnStart, t0, t1, f0, f1);

View File

@ -18,12 +18,10 @@ public final class Application {
private static ThrustCurveMotorSetDatabase motorSetDatabase; private static ThrustCurveMotorSetDatabase motorSetDatabase;
// Initialize the logger to something sane for testing without executing Startup // Initialize the logger to something sane for testing without executing Startup
static { static {
logger = new PrintStreamLogger(); setLogOutputLevel(LogLevel.DEBUG);
for (LogLevel l : LogLevel.values()) {
((PrintStreamLogger) logger).setOutput(l, System.out);
}
} }
@ -64,7 +62,22 @@ public final class Application {
} }
/**
* Set the logging to output the specified log level and upwards to standard output.
*
* @param level the minimum logging level to output.
*/
public static void setLogOutputLevel(LogLevel level) {
logger = new PrintStreamLogger();
for (LogLevel l : LogLevel.values()) {
if (l.atLeast(level)) {
((PrintStreamLogger) logger).setOutput(l, System.out);
}
}
}
/** /**
* Return the database of all thrust curves loaded into the system. * Return the database of all thrust curves loaded into the system.
*/ */

View File

@ -128,6 +128,9 @@ public class Prefs {
private static final String CHECK_UPDATES = "CheckUpdates"; private static final String CHECK_UPDATES = "CheckUpdates";
public static final String LAST_UPDATE = "LastUpdateVersion"; public static final String LAST_UPDATE = "LastUpdateVersion";
public static final String MOTOR_DIAMETER_FILTER = "MotorDiameterMatch";
public static final String MOTOR_HIDE_SIMILAR = "MotorHideSimilar";
// Node names // Node names
public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors"; public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";

View File

@ -0,0 +1,145 @@
package net.sf.openrocket.utils;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.sf.openrocket.file.GeneralMotorLoader;
import net.sf.openrocket.file.MotorLoader;
import net.sf.openrocket.logging.LogLevel;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorDigest;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.MathUtil;
public class MotorCorrelation {
/**
* Return a measure of motor similarity. The measure is a value between 0.0 and 1.0.
* The larger the value, the more similar the motor thrust curves are, for value 1.0 they
* are identical.
* <p>
* This method takes into account the thrust curve shape, average thrust, burn time and
* total impulse of the motor. The similarity is the minimum of all of these.
*
* @param motor1 the first motor
* @param motor2 the second motor
* @return the similarity of the two motors
*/
public static double similarity(Motor motor1, Motor motor2) {
double d;
d = crossCorrelation(motor1, motor2);
d = Math.min(d, diff(motor1.getAverageThrustEstimate(), motor2.getAverageThrustEstimate()));
d = Math.min(d, 2 * diff(motor1.getBurnTimeEstimate(), motor2.getBurnTimeEstimate()));
d = Math.min(d, diff(motor1.getTotalImpulseEstimate(), motor2.getTotalImpulseEstimate()));
return d;
}
private static double diff(double a, double b) {
double min = Math.min(a, b);
double max = Math.max(a, b);
if (MathUtil.equals(max, 0))
return 1.0;
return min / max;
}
/**
* Compute the cross-correlation of the thrust curves of the two motors. The result is
* a double between 0 and 1 (inclusive). The closer the return value is to one the more
* similar the thrust curves are.
*
* @param motor1 the first motor.
* @param motor2 the second motor.
* @return the scaled cross-correlation of the two thrust curves.
*/
public static double crossCorrelation(Motor motor1, Motor motor2) {
MotorInstance m1 = motor1.getInstance();
MotorInstance m2 = motor2.getInstance();
AtmosphericConditions cond = new AtmosphericConditions();
double t;
double auto1 = 0;
double auto2 = 0;
double cross = 0;
for (t = 0; t < 1000; t += 0.01) {
m1.step(t, 0, cond);
m2.step(t, 0, cond);
double t1 = m1.getThrust();
double t2 = m2.getThrust();
if (t1 < 0 || t2 < 0) {
throw new BugException("Negative thrust, t1=" + t1 + " t2=" + t2);
}
auto1 += t1 * t1;
auto2 += t2 * t2;
cross += t1 * t2;
}
double auto = Math.max(auto1, auto2);
if (MathUtil.equals(auto, 0)) {
return 1.0;
}
return cross / auto;
}
public static void main(String[] args) {
Application.setLogOutputLevel(LogLevel.WARN);
MotorLoader loader = new GeneralMotorLoader();
List<Motor> motors = new ArrayList<Motor>();
List<String> files = new ArrayList<String>();
// Load files
for (String file : args) {
List<Motor> m = null;
try {
InputStream stream = new FileInputStream(file);
m = loader.load(stream, file);
stream.close();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
if (m != null) {
motors.addAll(m);
for (int i = 0; i < m.size(); i++)
files.add(file);
}
}
// Output motor digests
final int count = motors.size();
for (int i = 0; i < count; i++) {
System.out.println(files.get(i) + ": " + MotorDigest.digestMotor((ThrustCurveMotor) motors.get(i)));
}
// Cross-correlate every pair
for (int i = 0; i < count; i++) {
for (int j = i + 1; j < count; j++) {
System.out.println(files.get(i) + " " + files.get(j) + " : " +
crossCorrelation(motors.get(i), motors.get(j)));
}
}
}
}

View File

@ -0,0 +1,60 @@
package net.sf.openrocket.utils;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import net.sf.openrocket.file.GeneralMotorLoader;
import net.sf.openrocket.file.MotorLoader;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorDigest;
import net.sf.openrocket.motor.ThrustCurveMotor;
public class MotorDigester {
public static void main(String[] args) {
final MotorLoader loader = new GeneralMotorLoader();
final boolean printFileNames;
if (args.length == 0) {
System.err.println("Usage: MotorDigester <files>");
printFileNames = false;
System.exit(1);
} else if (args.length == 1) {
printFileNames = false;
} else {
printFileNames = true;
}
for (String file : args) {
List<Motor> motors = null;
try {
InputStream stream = new FileInputStream(file);
motors = loader.load(stream, file);
stream.close();
} catch (IOException e) {
System.err.println("ERROR: " + e.getMessage());
e.printStackTrace();
continue;
}
for (Motor m : motors) {
if (!(m instanceof ThrustCurveMotor)) {
System.err.println(file + ": Not ThrustCurveMotor: " + m);
continue;
}
String digest = MotorDigest.digestMotor((ThrustCurveMotor) m);
if (printFileNames) {
System.out.print(file + ": ");
}
System.out.println(digest);
}
}
}
}