1 package net.sf.openrocket.motor;
3 import java.io.UnsupportedEncodingException;
4 import java.security.MessageDigest;
5 import java.security.NoSuchAlgorithmException;
7 import net.sf.openrocket.util.Coordinate;
8 import net.sf.openrocket.util.TextUtil;
11 * A class that generated a "digest" of a motor. A digest is a string value that
12 * uniquely identifies a motor (like a hash code or checksum). Two motors that have
13 * the same digest behave similarly with a very high probability. The digest can
14 * therefore be used to identify motors that otherwise have the same specifications.
16 * The digest only uses a limited amount of precision, so that rounding errors won't
17 * cause differing digest results.
19 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
21 public class MotorDigest {
23 private static final double EPSILON = 0.00000000001;
25 public enum DataType {
26 /** An array of time points at which data is available (in ms) */
28 /** Mass data for a few specific points (normally initial and empty mass) (in 0.1g) */
29 MASS_SPECIFIC(1, 10000),
30 /** Mass per time (in 0.1g) */
31 MASS_PER_TIME(2, 10000),
32 /** CG position for a few specific points (normally initial and final CG) (in mm) */
34 /** CG position per time (in mm) */
36 /** Thrust force per time (in mN) */
37 FORCE_PER_TIME(5, 1000);
39 private final int order;
40 private final int multiplier;
42 DataType(int order, int multiplier) {
44 this.multiplier = multiplier;
47 public int getOrder() {
51 public int getMultiplier() {
57 private final MessageDigest digest;
58 private boolean used = false;
59 private int lastOrder = -1;
62 public MotorDigest() {
64 digest = MessageDigest.getInstance("MD5");
65 } catch (NoSuchAlgorithmException e) {
66 throw new IllegalStateException("MD5 digest not supported by JRE", e);
71 public void update(DataType type, int... values) {
73 // Check for correct order
74 if (lastOrder >= type.getOrder()) {
75 throw new IllegalArgumentException("Called with type=" + type + " order=" + type.getOrder() +
76 " while lastOrder=" + lastOrder);
78 lastOrder = type.getOrder();
81 digest.update(bytes(type.getOrder()));
83 // Digest the data length
84 digest.update(bytes(values.length));
87 for (int v : values) {
88 digest.update(bytes(v));
94 private void update(DataType type, int multiplier, double... values) {
96 int[] intValues = new int[values.length];
97 for (int i = 0; i < values.length; i++) {
102 intValues[i] = (int) Math.round(v);
104 update(type, intValues);
107 public void update(DataType type, double... values) {
108 update(type, type.getMultiplier(), values);
111 private static double next(double v) {
112 return v + Math.signum(v) * EPSILON;
116 public String getDigest() {
118 throw new IllegalStateException("MotorDigest already used");
121 byte[] result = digest.digest();
122 return TextUtil.hexString(result);
127 private byte[] bytes(int value) {
129 (byte) ((value >>> 24) & 0xFF), (byte) ((value >>> 16) & 0xFF),
130 (byte) ((value >>> 8) & 0xFF), (byte) (value & 0xFF) };
135 * Digest the contents of a thrust curve motor. The result is a string uniquely
136 * defining the functional aspects of the motor.
138 * @param m the motor to digest
141 public static String digestMotor(Motor m) {
143 // Create the motor digest from data available in RASP files
144 MotorDigest motorDigest = new MotorDigest();
145 motorDigest.update(DataType.TIME_ARRAY, m.getTimePoints());
147 Coordinate[] cg = m.getCGPoints();
148 double[] cgx = new double[cg.length];
149 double[] mass = new double[cg.length];
150 for (int i = 0; i < cg.length; i++) {
152 mass[i] = cg[i].weight;
155 motorDigest.update(DataType.MASS_PER_TIME, mass);
156 motorDigest.update(DataType.CG_PER_TIME, cgx);
157 motorDigest.update(DataType.FORCE_PER_TIME, m.getThrustPoints());
158 return motorDigest.getDigest();
162 public static String digestComment(String comment) {
163 comment = comment.replaceAll("\\s+", " ").trim();
165 MessageDigest digest;
167 digest = MessageDigest.getInstance("MD5");
168 } catch (NoSuchAlgorithmException e) {
169 throw new IllegalStateException("MD5 digest not supported by JRE", e);
173 digest.update(comment.getBytes("UTF-8"));
174 } catch (UnsupportedEncodingException e) {
175 throw new IllegalStateException("UTF-8 encoding not supported by JRE", e);
178 return TextUtil.hexString(digest.digest());