From: plaa Date: Fri, 30 Jul 2010 21:06:51 +0000 (+0000) Subject: motor selection enhancements X-Git-Tag: upstream/1.1.2^2~8 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=908a72a1c4bc9aaff9c4cc7b874f8a2d2c5300fb;p=debian%2Fopenrocket motor selection enhancements git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@62 180e2498-e6e9-4542-8430-84ac67f01cd8 --- diff --git a/ChangeLog b/ChangeLog index f958fe54..5223bc38 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 * Implemented enhanced motor selection dialog diff --git a/doc/properties.txt b/doc/properties.txt index cf298256..ed069041 100644 --- a/doc/properties.txt +++ b/doc/properties.txt @@ -37,6 +37,9 @@ openrocket.debug.bugurl openrocket.debug.updateurl URL used for retrieving update notifications. +openrocket.debug.motordigest + If defined the motor digest will be displayed in the selection dialog. + openrocket.debug.coordinatecount 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. diff --git a/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java b/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java index db0ca84e..47e76a4f 100644 --- a/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java +++ b/src/net/sf/openrocket/file/openrocket/OpenRocketLoader.java @@ -21,8 +21,10 @@ import net.sf.openrocket.file.RocketLoader; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.file.simplesax.SimpleSAX; +import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.material.Material; import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.MotorDigest; import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.rocketcomponent.BodyComponent; import net.sf.openrocket.rocketcomponent.BodyTube; @@ -89,12 +91,15 @@ import org.xml.sax.SAXException; * * @author Sampo Niskanen */ - public class OpenRocketLoader extends RocketLoader { + private static final LogHelper log = Application.getLogger(); + @Override public OpenRocketDocument loadFromStream(InputStream source) throws RocketLoadException, IOException { + log.info("Loading .ork file"); + InputSource xmlSource = new InputSource(source); OpenRocketHandler handler = new OpenRocketHandler(); @@ -102,6 +107,7 @@ public class OpenRocketLoader extends RocketLoader { try { SimpleSAX.readXML(xmlSource, handler, warnings); } catch (SAXException e) { + log.warn("Malformed XML in input"); throw new RocketLoadException("Malformed XML in input.", e); } @@ -141,6 +147,7 @@ public class OpenRocketLoader extends RocketLoader { doc.getDefaultStorageOptions().setExplicitlySet(false); doc.clearUndo(); + log.info("Loading done"); return doc; } @@ -151,7 +158,7 @@ public class OpenRocketLoader extends RocketLoader { class DocumentConfig { /* 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 @@ -971,6 +978,7 @@ class MotorHandler extends ElementHandler { private Motor.Type type = null; private String manufacturer = null; private String designation = null; + private String digest = null; private double diameter = Double.NaN; private double length = Double.NaN; private double delay = Double.NaN; @@ -990,25 +998,68 @@ class MotorHandler extends ElementHandler { warnings.add(Warning.fromString("No motor specified, ignoring.")); return null; } + List motors = Application.getMotorSetDatabase().findMotors(type, manufacturer, designation, diameter, length); + + // No motors if (motors.size() == 0) { String str = "No motor with designation '" + designation + "'"; if (manufacturer != null) str += " for manufacturer '" + manufacturer + "'"; - warnings.add(Warning.fromString(str + " found.")); + str += " found."; + warnings.add(str); 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 + "'"; if (manufacturer != null) 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 the delay to use for the motor. */ @@ -1032,7 +1083,7 @@ class MotorHandler extends ElementHandler { // Motor type type = null; for (Motor.Type t : Motor.Type.values()) { - if (t.name().toLowerCase().equals(content)) { + if (t.name().toLowerCase().equals(content.trim())) { type = t; break; } @@ -1044,19 +1095,24 @@ class MotorHandler extends ElementHandler { } else if (element.equals("manufacturer")) { // Manufacturer - manufacturer = content; + manufacturer = content.trim(); } else if (element.equals("designation")) { // Designation - designation = content; + designation = content.trim(); + + } else if (element.equals("digest")) { + + // Digest + digest = content.trim(); } else if (element.equals("diameter")) { // Diameter diameter = Double.NaN; try { - diameter = Double.parseDouble(content); + diameter = Double.parseDouble(content.trim()); } catch (NumberFormatException e) { // Ignore } @@ -1069,7 +1125,7 @@ class MotorHandler extends ElementHandler { // Length length = Double.NaN; try { - length = Double.parseDouble(content); + length = Double.parseDouble(content.trim()); } catch (NumberFormatException ignore) { } @@ -1085,7 +1141,7 @@ class MotorHandler extends ElementHandler { delay = Motor.PLUGGED; } else { try { - delay = Double.parseDouble(content); + delay = Double.parseDouble(content.trim()); } catch (NumberFormatException ignore) { } diff --git a/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java b/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java index 294fff7f..7cadcf4b 100644 --- a/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java +++ b/src/net/sf/openrocket/file/openrocket/OpenRocketSaver.java @@ -15,7 +15,9 @@ import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.file.RocketSaver; +import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; 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.FlightEvent; import net.sf.openrocket.simulation.GUISimulationConditions; +import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Prefs; @@ -31,7 +34,9 @@ import net.sf.openrocket.util.Reflection; import net.sf.openrocket.util.TextUtil; 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 @@ -63,21 +68,25 @@ public class OpenRocketSaver extends RocketSaver { public void save(OutputStream output, OpenRocketDocument document, StorageOptions options) throws IOException { + log.info("Saving .ork file"); + if (options.isCompressionEnabled()) { + log.debug("Enabling compression"); output = new GZIPOutputStream(output); } dest = new BufferedWriter(new OutputStreamWriter(output, OPENROCKET_CHARSET)); + // Select file version number final int fileVersion = calculateNecessaryFileVersion(document, options); final String fileVersionString = (fileVersion / FILE_VERSION_DIVISOR) + "." + (fileVersion % FILE_VERSION_DIVISOR); + log.debug("Storing file version " + fileVersionString); this.indent = 0; - System.out.println("Writing..."); - + writeln(""); writeln(""); @@ -104,6 +113,7 @@ public class OpenRocketSaver extends RocketSaver { indent--; writeln(""); + log.debug("Writing complete, flushing buffers"); dest.flush(); if (options.isCompressionEnabled()) { ((GZIPOutputStream) output).finish(); @@ -173,6 +183,9 @@ public class OpenRocketSaver extends RocketSaver { */ private int calculateNecessaryFileVersion(OpenRocketDocument document, StorageOptions opts) { /* + * File version 1.2 is required for: + * - saving motor data + * * File version 1.1 is required for: * - fin tabs * - components attached to tube coupler @@ -180,8 +193,23 @@ public class OpenRocketSaver extends RocketSaver { * Otherwise use version 1.0. */ - // Check for fin tabs (version 1.1) + // Check for motor definitions (version 1.2) Iterator 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()) { RocketComponent c = iterator.next(); @@ -211,6 +239,8 @@ public class OpenRocketSaver extends RocketSaver { @SuppressWarnings("unchecked") private void saveComponent(RocketComponent component) throws IOException { + log.debug("Saving component " + component.getComponentName()); + Reflection.Method m = Reflection.findMethod(METHOD_PACKAGE, component, METHOD_SUFFIX, "getElements", RocketComponent.class); 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. * diff --git a/src/net/sf/openrocket/file/openrocket/PreferredMotorDigests.java b/src/net/sf/openrocket/file/openrocket/PreferredMotorDigests.java new file mode 100644 index 00000000..d05cccbb --- /dev/null +++ b/src/net/sf/openrocket/file/openrocket/PreferredMotorDigests.java @@ -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. + *

+ * 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 + */ +final class PreferredMotorDigests { + + /** + * A set containing the preferred motor digests. + */ + public static final Set 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 set = new HashSet(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 + } + +} diff --git a/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java b/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java index fdc830ae..ab0a0038 100644 --- a/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java +++ b/src/net/sf/openrocket/file/openrocket/savers/RocketComponentSaver.java @@ -8,6 +8,7 @@ import java.util.List; import net.sf.openrocket.file.RocketSaver; import net.sf.openrocket.material.Material; import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.MotorDigest; import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.MotorMount; @@ -124,7 +125,10 @@ public class RocketComponentSaver { elements.add(" " + motor.getMotorType().name().toLowerCase() + ""); } if (motor instanceof ThrustCurveMotor) { - elements.add(" " + RocketSaver.escapeXML(((ThrustCurveMotor) motor).getManufacturer().getSimpleName()) + ""); + ThrustCurveMotor m = (ThrustCurveMotor) motor; + elements.add(" " + RocketSaver.escapeXML(m.getManufacturer().getSimpleName()) + + ""); + elements.add(" " + MotorDigest.digestMotor(m) + ""); } elements.add(" " + RocketSaver.escapeXML(motor.getDesignation()) + ""); elements.add(" " + motor.getDiameter() + ""); diff --git a/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorComparator.java b/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorComparator.java new file mode 100644 index 00000000..0096bf17 --- /dev/null +++ b/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorComparator.java @@ -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 + */ +public class ThrustCurveMotorComparator implements Comparator { + + + @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; + } + + +} diff --git a/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorPlotDialog.java b/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorPlotDialog.java index da6fb516..314219a3 100644 --- a/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorPlotDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorPlotDialog.java @@ -13,7 +13,6 @@ import javax.swing.JDialog; import javax.swing.JPanel; import net.miginfocom.swing.MigLayout; -import net.sf.openrocket.database.ThrustCurveMotorSet; import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.GUIUtil; @@ -29,7 +28,7 @@ import org.jfree.data.xy.XYSeriesCollection; public class ThrustCurveMotorPlotDialog extends JDialog { - public ThrustCurveMotorPlotDialog(ThrustCurveMotorSet motorSet, ThrustCurveMotor selectedMotor, Window parent) { + public ThrustCurveMotorPlotDialog(List motors, int selected, Window parent) { super(parent, "Motor thrust curves", ModalityType.APPLICATION_MODAL); JPanel panel = new JPanel(new MigLayout("fill")); @@ -73,21 +72,19 @@ public class ThrustCurveMotorPlotDialog extends JDialog { // Create the plot data set XYSeriesCollection dataset = new XYSeriesCollection(); - List motors = motorSet.getMotors(); // Selected thrust curve - int index = motors.indexOf(selectedMotor); int n = 0; - dataset.addSeries(generateSeries(selectedMotor)); - renderer.setSeriesStroke(n, new BasicStroke(1.5f)); - if (index >= 0) { - renderer.setSeriesPaint(n, ThrustCurveMotorSelectionPanel.getColor(index)); + if (selected >= 0) { + dataset.addSeries(generateSeries(motors.get(selected))); + renderer.setSeriesStroke(n, new BasicStroke(1.5f)); + renderer.setSeriesPaint(n, ThrustCurveMotorSelectionPanel.getColor(selected)); } n++; // Other thrust curves for (int i = 0; i < motors.size(); i++) { - if (i == index) + if (i == selected) continue; ThrustCurveMotor m = motors.get(i); diff --git a/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java b/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java index 038359d7..94f38a97 100644 --- a/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java +++ b/src/net/sf/openrocket/gui/dialogs/motor/thrustcurve/ThrustCurveMotorSelectionPanel.java @@ -18,6 +18,7 @@ import java.util.prefs.Preferences; import javax.swing.BorderFactory; import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; 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.Icons; import net.sf.openrocket.util.Prefs; +import net.sf.openrocket.utils.MotorCorrelation; import org.jfree.chart.ChartColor; import org.jfree.chart.ChartFactory; @@ -71,6 +73,8 @@ import org.jfree.data.xy.XYSeriesCollection; public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelector { 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_SMALLER = 1; 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 WITH_COMMENT_COLOR = Color.BLACK; + private static final ThrustCurveMotorComparator MOTOR_COMPARATOR = new ThrustCurveMotorComparator(); + + + private final List database; private final double diameter; @@ -99,6 +107,8 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec private final JTable table; private final TableRowSorter sorter; + private final JCheckBox hideSimilarBox; + private final JTextField searchField; private String[] searchTerms = new String[0]; @@ -114,6 +124,7 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec private final JLabel launchMassLabel; private final JLabel emptyMassLabel; private final JLabel dataPointsLabel; + private final JLabel digestLabel; private final JTextArea comment; private final Font noCommentFont; @@ -212,9 +223,21 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec 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 model = new ThrustCurveMotorDatabaseModel(database); @@ -402,6 +425,14 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec dataPointsLabel = new JLabel(); 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); GUIUtil.changeFontSize(comment, -2); @@ -461,7 +492,9 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec return; if (e.getButton() == MouseEvent.BUTTON1) { // Open plot dialog - ThrustCurveMotorPlotDialog plotDialog = new ThrustCurveMotorPlotDialog(selectedMotorSet, selectedMotor, + List motors = getFilteredCurves(); + ThrustCurveMotorPlotDialog plotDialog = new ThrustCurveMotorPlotDialog(motors, + motors.indexOf(selectedMotor), SwingUtilities.getWindowAncestor(ThrustCurveMotorSelectionPanel.this)); plotDialog.setVisible(true); } @@ -488,7 +521,7 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec // 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); @@ -500,7 +533,6 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec } - @Override public Motor getSelectedMotor() { return selectedMotor; @@ -590,15 +622,21 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec launchMassLabel.setText(""); emptyMassLabel.setText(""); dataPointsLabel.setText(""); + if (digestLabel != null) { + digestLabel.setText(""); + } setComment(""); chart.getXYPlot().setDataset(new XYSeriesCollection()); return; } - List motors = selectedMotorSet.getMotors(); + // Check which thrust curves to display + List motors = getFilteredCurves(); final int index = motors.indexOf(selectedMotor); + + // Update the thrust curve selection box curveSelectionModel.removeAllElements(); for (int i = 0; i < motors.size(); i++) { curveSelectionModel.addElement(new MotorHolder(motors.get(i), i)); @@ -613,6 +651,8 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec curveSelectionLabel.setEnabled(false); } + + // Update thrust curve data totalImpulseLabel.setText(UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit( selectedMotor.getTotalImpulseEstimate())); 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( selectedMotor.getEmptyCG().weight)); dataPointsLabel.setText("" + (selectedMotor.getTimePoints().length - 1)); + if (digestLabel != null) { + digestLabel.setText(MotorDigest.digestMotor(selectedMotor)); + } setComment(selectedMotor.getDescription()); @@ -656,6 +699,32 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec } + private List getFilteredCurves() { + List motors = selectedMotorSet.getMotors(); + if (hideSimilarBox.isSelected()) { + List filtered = new ArrayList(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) { s = s.trim(); if (s.length() == 0) { @@ -721,18 +790,20 @@ public class ThrustCurveMotorSelectionPanel extends JPanel implements MotorSelec return set.getMotors().get(0); } + // Find which motor has been used the most recently + List list = set.getMotors(); Preferences prefs = Prefs.getNode(Prefs.PREFERRED_THRUST_CURVE_MOTOR_NODE); - for (ThrustCurveMotor m : set.getMotors()) { + for (ThrustCurveMotor m : list) { String digest = MotorDigest.digestMotor(m); if (prefs.getBoolean(digest, false)) { return m; } } - // No motor has been used, use heuristics to select motor - // TODO: CRITICAL: Heuristics - return set.getMotors().get(0); + // No motor has been used + Collections.sort(list, MOTOR_COMPARATOR); + return list.get(0); } diff --git a/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/src/net/sf/openrocket/motor/ThrustCurveMotor.java index ce3108dd..2bbddd14 100644 --- a/src/net/sf/openrocket/motor/ThrustCurveMotor.java +++ b/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -280,12 +280,11 @@ public class ThrustCurveMotor implements Motor, Comparable { throw new BugException("Could not compute burn start time, maxThrust=" + maxThrust + " limit=" + thrustLimit + " thrust=" + Arrays.toString(thrust)); } - if (MathUtil.equals(thrust[pos], thrust[pos + 1])) { + if (MathUtil.equals(thrust[pos - 1], thrust[pos])) { // For safety - burnStart = (time[pos] + time[pos + 1]) / 2; + burnStart = (time[pos - 1] + time[pos]) / 2; } else { - burnStart = MathUtil.map(thrustLimit, thrust[pos], thrust[pos + 1], - time[pos], time[pos + 1]); + burnStart = MathUtil.map(thrustLimit, thrust[pos - 1], thrust[pos], time[pos - 1], time[pos]); } @@ -319,9 +318,9 @@ public class ThrustCurveMotor implements Motor, Comparable { double t0 = time[pos]; double t1 = time[pos + 1]; 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) { double fStart = MathUtil.map(burnStart, t0, t1, f0, f1); diff --git a/src/net/sf/openrocket/startup/Application.java b/src/net/sf/openrocket/startup/Application.java index a00c9535..b8ef7834 100644 --- a/src/net/sf/openrocket/startup/Application.java +++ b/src/net/sf/openrocket/startup/Application.java @@ -18,12 +18,10 @@ public final class Application { private static ThrustCurveMotorSetDatabase motorSetDatabase; + // Initialize the logger to something sane for testing without executing Startup static { - logger = new PrintStreamLogger(); - for (LogLevel l : LogLevel.values()) { - ((PrintStreamLogger) logger).setOutput(l, System.out); - } + setLogOutputLevel(LogLevel.DEBUG); } @@ -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. */ diff --git a/src/net/sf/openrocket/util/Prefs.java b/src/net/sf/openrocket/util/Prefs.java index 8f5a75ec..53d9ff26 100644 --- a/src/net/sf/openrocket/util/Prefs.java +++ b/src/net/sf/openrocket/util/Prefs.java @@ -128,6 +128,9 @@ public class Prefs { private static final String CHECK_UPDATES = "CheckUpdates"; 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 public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors"; diff --git a/src/net/sf/openrocket/utils/MotorCorrelation.java b/src/net/sf/openrocket/utils/MotorCorrelation.java new file mode 100644 index 00000000..aab9b171 --- /dev/null +++ b/src/net/sf/openrocket/utils/MotorCorrelation.java @@ -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. + *

+ * 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 motors = new ArrayList(); + List files = new ArrayList(); + + // Load files + for (String file : args) { + List 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))); + } + } + + } + +} \ No newline at end of file diff --git a/src/net/sf/openrocket/utils/MotorDigester.java b/src/net/sf/openrocket/utils/MotorDigester.java new file mode 100644 index 00000000..d9e53a9b --- /dev/null +++ b/src/net/sf/openrocket/utils/MotorDigester.java @@ -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 "); + printFileNames = false; + System.exit(1); + } else if (args.length == 1) { + printFileNames = false; + } else { + printFileNames = true; + } + + + for (String file : args) { + + List 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); + } + } + + } + +}