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.motor.GeneralMotorLoader;
import net.sf.openrocket.file.motor.MotorLoader;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.ThrustCurveMotor;
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) {
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) + ": " + ((ThrustCurveMotor) motors.get(i)).getDigest());
}
// 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)));
}
}
}
}