Modified thrustcurve serialization to be more robust in the case of
some common (but generally not important) errors in thrustcurves, and not enter thrustcurves that will cause simulation failures into the database. (1) Permits thrustcurves to start at time 0 with a nonzero thrust. Previous code inserted a datapoint with time 0 and thrust 0; this caused thrust interpolation code to fail while running a simulation. If the first datapoint is at a time greater than 0 it does enter a (0, 0) datapoint, as this is the correct documented behavior for RASP files. (2) Permits thrustcurves to end with a nonzero thrust. Similarly to the previous case, the old code entered a new datapoint at the same time as the previous end of thrust with thrust 0, which would break thrust interpolation. (3) Thrustcurves that have a zero-thrust datapoint and a non-zero thrust datapoint at time 0 have the first datapoint removed. (4) Thrustcurves with more serious errors -- mainly multiple thrusts at a single time -- aren't entered into the database, as they will break thrust interpolation and there is no obvious way to guess which of the datapoints is correct, or to see how to average them.
This commit is contained in:
parent
1abf0d3834
commit
6c535bff94
Binary file not shown.
@ -179,7 +179,14 @@ public abstract class AbstractMotorLoader implements MotorLoader {
|
||||
return;
|
||||
|
||||
// Start
|
||||
if (!MathUtil.equals(time.get(0), 0) || !MathUtil.equals(thrust.get(0), 0)) {
|
||||
// If there is no datapoint at t=0, put one there (this is the
|
||||
// normal case for a RASP file). If there is a nonzero thrust
|
||||
// at time 0 it's an error, but not one that calls for not
|
||||
// using the file. We *don't* want to also put a 0-thrust
|
||||
// point at time 0 in that case, as that will cause the
|
||||
// simulation to throw an exception just like in the
|
||||
// commented-out case below.
|
||||
if (!MathUtil.equals(time.get(0), 0)) {
|
||||
time.add(0, 0.0);
|
||||
thrust.add(0, 0.0);
|
||||
for (List l : lists) {
|
||||
@ -187,17 +194,33 @@ public abstract class AbstractMotorLoader implements MotorLoader {
|
||||
l.add(0, o);
|
||||
}
|
||||
}
|
||||
|
||||
// End
|
||||
int n = time.size() - 1;
|
||||
if (!MathUtil.equals(thrust.get(n), 0)) {
|
||||
time.add(time.get(n));
|
||||
thrust.add(0.0);
|
||||
for (List l : lists) {
|
||||
Object o = l.get(n);
|
||||
l.add(o);
|
||||
}
|
||||
|
||||
// Not-uncommon issue at start of thrust curves: two points
|
||||
// for t=0, one with thrust zero and one non-zero. We'll throw
|
||||
// out the 0-thrust point and go on.
|
||||
if (MathUtil.equals(time.get(0), 0) && MathUtil.equals(time.get(1), 0)) {
|
||||
time.remove(0);
|
||||
thrust.remove(0);
|
||||
}
|
||||
|
||||
// End
|
||||
// Ah, no, we don't want to do this (I'm leaving the dead code
|
||||
// in case there's a temptation to put it back in). This ends
|
||||
// up putting the new 0-thrust point at the same time as the
|
||||
// previous last datapoint, which will cause
|
||||
// ThrustCurveMotor.getAverageThrust() to fail when it tries
|
||||
// to interpolate (the exception is actually thrown by
|
||||
// MathUtil.map())
|
||||
//
|
||||
// int n = time.size() - 1;
|
||||
// if (!MathUtil.equals(thrust.get(n), 0)) {
|
||||
// time.add(time.get(n));
|
||||
// thrust.add(0.0);
|
||||
// for (List l : lists) {
|
||||
// Object o = l.get(n);
|
||||
// l.add(o);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -166,22 +166,30 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
throw new IllegalArgumentException("Too short thrust-curve, length=" + motor.time.length);
|
||||
}
|
||||
for (int i = 0; i < motor.time.length - 1; i++) {
|
||||
if (motor.time[i + 1] < motor.time[i]) {
|
||||
throw new IllegalArgumentException("Time goes backwards, " +
|
||||
if (motor.time[i + 1] <= motor.time[i]) {
|
||||
throw new IllegalArgumentException("Time stalls or goes backwards, " +
|
||||
"time[" + i + "]=" + motor.time[i] + " " +
|
||||
"time[" + (i + 1) + "]=" + motor.time[i + 1]);
|
||||
"time[" + (i + 1) + "]=" + motor.time[i + 1] +
|
||||
", thrust=(" + motor.thrust[i] + ", " + motor.thrust[i+1] + ")");
|
||||
}
|
||||
}
|
||||
if (!MathUtil.equals(motor.time[0], 0)) {
|
||||
throw new IllegalArgumentException("Curve starts at time " + motor.time[0]);
|
||||
}
|
||||
if (!MathUtil.equals(motor.thrust[0], 0)) {
|
||||
throw new IllegalArgumentException("Curve starts at thrust " + motor.thrust[0]);
|
||||
}
|
||||
if (!MathUtil.equals(motor.thrust[motor.thrust.length - 1], 0)) {
|
||||
throw new IllegalArgumentException("Curve ends at thrust " +
|
||||
motor.thrust[motor.thrust.length - 1]);
|
||||
}
|
||||
|
||||
// these conditions actually are error, but quite a few of
|
||||
// the files on thrustcurvemotor.org have one or the
|
||||
// other of them, and they make less of a difference to
|
||||
// the simulation result than the normal variation between motors.
|
||||
// if (!MathUtil.equals(motor.thrust[0], 0)) {
|
||||
// throw new IllegalArgumentException("Curve starts at thrust " + motor.thrust[0]);
|
||||
// }
|
||||
//
|
||||
// if (!MathUtil.equals(motor.thrust[motor.thrust.length - 1], 0)) {
|
||||
// throw new IllegalArgumentException("Curve ends at thrust " +
|
||||
// motor.thrust[motor.thrust.length - 1]);
|
||||
//}
|
||||
|
||||
for (double t : motor.thrust) {
|
||||
if (t < 0) {
|
||||
throw new IllegalArgumentException("Negative thrust.");
|
||||
@ -630,45 +638,51 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
maxThrust = t;
|
||||
}
|
||||
|
||||
|
||||
// Burn start time
|
||||
double thrustLimit = maxThrust * MARGINAL_THRUST;
|
||||
double burnStart, burnEnd;
|
||||
|
||||
int pos;
|
||||
for (pos = 1; pos < thrust.length; pos++) {
|
||||
if (thrust[pos] >= thrustLimit)
|
||||
break;
|
||||
|
||||
if (thrust[0] >= thrustLimit)
|
||||
burnStart = time[0];
|
||||
else {
|
||||
int startPos;
|
||||
for (startPos = 1; startPos < thrust.length; startPos++) {
|
||||
if (thrust[startPos] >= thrustLimit)
|
||||
break;
|
||||
}
|
||||
if (startPos >= thrust.length) {
|
||||
throw new BugException("Could not compute burn start time, maxThrust=" + maxThrust +
|
||||
" limit=" + thrustLimit + " thrust=" + Arrays.toString(thrust));
|
||||
}
|
||||
if (MathUtil.equals(thrust[startPos - 1], thrust[startPos])) {
|
||||
// For safety
|
||||
burnStart = (time[startPos - 1] + time[startPos]) / 2;
|
||||
} else {
|
||||
burnStart = MathUtil.map(thrustLimit, thrust[startPos - 1], thrust[startPos], time[startPos - 1], time[startPos]);
|
||||
}
|
||||
}
|
||||
if (pos >= thrust.length) {
|
||||
throw new BugException("Could not compute burn start time, maxThrust=" + maxThrust +
|
||||
" limit=" + thrustLimit + " thrust=" + Arrays.toString(thrust));
|
||||
}
|
||||
if (MathUtil.equals(thrust[pos - 1], thrust[pos])) {
|
||||
// For safety
|
||||
burnStart = (time[pos - 1] + time[pos]) / 2;
|
||||
} else {
|
||||
burnStart = MathUtil.map(thrustLimit, thrust[pos - 1], thrust[pos], time[pos - 1], time[pos]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Burn end time
|
||||
for (pos = thrust.length - 2; pos >= 0; pos--) {
|
||||
if (thrust[pos] >= thrustLimit)
|
||||
break;
|
||||
if (thrust[thrust.length-1] >= thrustLimit)
|
||||
burnEnd = time[time.length-1];
|
||||
else {
|
||||
int endPos;
|
||||
for (endPos = thrust.length - 2; endPos >= 0; endPos--) {
|
||||
if (thrust[endPos] >= thrustLimit)
|
||||
break;
|
||||
}
|
||||
if (endPos < 0) {
|
||||
throw new BugException("Could not compute burn end time, maxThrust=" + maxThrust +
|
||||
" limit=" + thrustLimit + " thrust=" + Arrays.toString(thrust));
|
||||
}
|
||||
if (MathUtil.equals(thrust[endPos], thrust[endPos + 1])) {
|
||||
// For safety
|
||||
burnEnd = (time[endPos] + time[endPos + 1]) / 2;
|
||||
} else {
|
||||
burnEnd = MathUtil.map(thrustLimit, thrust[endPos], thrust[endPos + 1],
|
||||
time[endPos], time[endPos + 1]);
|
||||
}
|
||||
}
|
||||
if (pos < 0) {
|
||||
throw new BugException("Could not compute burn end time, maxThrust=" + maxThrust +
|
||||
" limit=" + thrustLimit + " thrust=" + Arrays.toString(thrust));
|
||||
}
|
||||
if (MathUtil.equals(thrust[pos], thrust[pos + 1])) {
|
||||
// For safety
|
||||
burnEnd = (time[pos] + time[pos + 1]) / 2;
|
||||
} else {
|
||||
burnEnd = MathUtil.map(thrustLimit, thrust[pos], thrust[pos + 1],
|
||||
time[pos], time[pos + 1]);
|
||||
}
|
||||
|
||||
|
||||
// Burn time
|
||||
burnTimeEstimate = Math.max(burnEnd - burnStart, 0);
|
||||
@ -677,12 +691,12 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
|
||||
// Total impulse and average thrust
|
||||
totalImpulse = 0;
|
||||
averageThrust = 0;
|
||||
|
||||
for (pos = 0; pos < time.length - 1; pos++) {
|
||||
double t0 = time[pos];
|
||||
double t1 = time[pos + 1];
|
||||
double f0 = thrust[pos];
|
||||
double f1 = thrust[pos + 1];
|
||||
int impulsePos;
|
||||
for (impulsePos = 0; impulsePos < time.length - 1; impulsePos++) {
|
||||
double t0 = time[impulsePos];
|
||||
double t1 = time[impulsePos + 1];
|
||||
double f0 = thrust[impulsePos];
|
||||
double f1 = thrust[impulsePos + 1];
|
||||
|
||||
totalImpulse += (t1 - t0) * (f0 + f1) / 2;
|
||||
|
||||
|
@ -57,6 +57,8 @@ public class DownloadResponseParser implements ElementHandler {
|
||||
response.add(motorBurnFile);
|
||||
} else if (motor_id_tag.equals(element)) {
|
||||
motorBurnFile.setMotorId(Integer.parseInt(content));
|
||||
} else if (simfile_id_tag.equals(element)) {
|
||||
motorBurnFile.setSimfileId(Integer.parseInt(content));
|
||||
} else if (format_tag.equals(element)) {
|
||||
motorBurnFile.setFiletype(content);
|
||||
} else if (data_tag.equals(element)) {
|
||||
@ -72,4 +74,4 @@ public class DownloadResponseParser implements ElementHandler {
|
||||
public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws SAXException {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,10 @@ import net.sf.openrocket.motor.ThrustCurveMotor;
|
||||
public class MotorBurnFile {
|
||||
|
||||
private Integer motorId;
|
||||
private Integer simfileId;
|
||||
private String filetype;
|
||||
private ThrustCurveMotor.Builder thrustCurveMotor;
|
||||
private String data;
|
||||
|
||||
public void init() {
|
||||
this.motorId = null;
|
||||
@ -29,8 +31,9 @@ public class MotorBurnFile {
|
||||
return clone;
|
||||
}
|
||||
|
||||
public void decodeFile(String data) throws IOException {
|
||||
data = Base64Decoder.decodeData(data);
|
||||
public void decodeFile(String _data) throws IOException {
|
||||
_data = Base64Decoder.decodeData(_data);
|
||||
data = _data;
|
||||
try {
|
||||
if (SupportedFileTypes.RASP_FORMAT.equals(filetype)) {
|
||||
RASPMotorLoader loader = new RASPMotorLoader();
|
||||
@ -47,19 +50,33 @@ public class MotorBurnFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the motor_id
|
||||
* @return the motor id
|
||||
*/
|
||||
public Integer getMotorId() {
|
||||
return motorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param motor_id the motor_id to set
|
||||
* @param motorId the motor id to set
|
||||
*/
|
||||
public void setMotorId(Integer motorId) {
|
||||
this.motorId = motorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the simfile id
|
||||
*/
|
||||
public Integer getSimfileId() {
|
||||
return simfileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param simfileId the simfileId to set
|
||||
*/
|
||||
public void setSimfileId(Integer simfileId) {
|
||||
this.simfileId = simfileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the filetype
|
||||
*/
|
||||
@ -80,5 +97,12 @@ public class MotorBurnFile {
|
||||
public ThrustCurveMotor.Builder getThrustCurveMotor() {
|
||||
return thrustCurveMotor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the file contents
|
||||
*/
|
||||
public String getContents() {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -115,36 +115,45 @@ public class SerializeThrustcurveMotors {
|
||||
System.out.println(message);
|
||||
|
||||
List<MotorBurnFile> b = getThrustCurvesForMotorId(mi.getMotor_id());
|
||||
|
||||
for (MotorBurnFile burnFile : b) {
|
||||
|
||||
ThrustCurveMotor.Builder builder = burnFile.getThrustCurveMotor();
|
||||
if (builder == null) {
|
||||
continue;
|
||||
try {
|
||||
ThrustCurveMotor.Builder builder = burnFile.getThrustCurveMotor();
|
||||
if (builder == null) {
|
||||
continue;
|
||||
}
|
||||
if (mi.getTot_mass_g() != null) {
|
||||
builder.setInitialMass(mi.getTot_mass_g() / 1000.0);
|
||||
}
|
||||
if (mi.getProp_mass_g() != null) {
|
||||
// builder.setPropellantMass(mi.getProp_mass_g() / 1000.0);
|
||||
}
|
||||
|
||||
builder.setCaseInfo(mi.getCase_info());
|
||||
builder.setPropellantInfo(mi.getProp_info());
|
||||
builder.setDiameter(mi.getDiameter() / 1000.0);
|
||||
builder.setLength(mi.getLength() / 1000.0);
|
||||
builder.setMotorType(type);
|
||||
|
||||
if ("OOP".equals(mi.getAvailiability())) {
|
||||
builder.setDesignation(mi.getDesignation());
|
||||
builder.setAvailablity(false);
|
||||
} else if (mi.getDesignation().startsWith("Micro")) {
|
||||
builder.setDesignation(mi.getDesignation());
|
||||
} else {
|
||||
builder.setDesignation(mi.getCommon_name());
|
||||
}
|
||||
|
||||
allMotors.add(builder.build());
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.out.println("\tError in simFile " + burnFile.getSimfileId() + ": " + e.getMessage() + " (continuing)");
|
||||
try {
|
||||
FileOutputStream out = new FileOutputStream(("simfile-" + burnFile.getSimfileId()).toString());
|
||||
out.write(burnFile.getContents().getBytes());
|
||||
out.close();
|
||||
} catch (IOException i) {
|
||||
System.out.println("unable to write bad file: " + i.getMessage());
|
||||
}
|
||||
}
|
||||
if (mi.getTot_mass_g() != null) {
|
||||
builder.setInitialMass(mi.getTot_mass_g() / 1000.0);
|
||||
}
|
||||
if (mi.getProp_mass_g() != null) {
|
||||
// builder.setPropellantMass(mi.getProp_mass_g() / 1000.0);
|
||||
}
|
||||
|
||||
builder.setCaseInfo(mi.getCase_info());
|
||||
builder.setPropellantInfo(mi.getProp_info());
|
||||
builder.setDiameter(mi.getDiameter() / 1000.0);
|
||||
builder.setLength(mi.getLength() / 1000.0);
|
||||
builder.setMotorType(type);
|
||||
|
||||
if ("OOP".equals(mi.getAvailiability())) {
|
||||
builder.setDesignation(mi.getDesignation());
|
||||
builder.setAvailablity(false);
|
||||
} else if (mi.getDesignation().startsWith("Micro")) {
|
||||
builder.setDesignation(mi.getDesignation());
|
||||
} else {
|
||||
builder.setDesignation(mi.getCommon_name());
|
||||
}
|
||||
|
||||
allMotors.add(builder.build());
|
||||
|
||||
}
|
||||
|
||||
@ -160,12 +169,12 @@ public class SerializeThrustcurveMotors {
|
||||
try {
|
||||
b.addAll(ThrustCurveAPI.downloadData(motorId, "RockSim"));
|
||||
} catch (Exception ex) {
|
||||
System.out.println("\tError downloading RockSim");
|
||||
System.out.println("\tError downloading RockSim for motorID=" + motorId);
|
||||
}
|
||||
try {
|
||||
b.addAll(ThrustCurveAPI.downloadData(motorId, "RASP"));
|
||||
} catch (Exception ex) {
|
||||
System.out.println("\tError downloading RASP");
|
||||
System.out.println("\tError downloading RASP for motorID=" + motorId);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
1
openrocket.log
Normal file
1
openrocket.log
Normal file
@ -0,0 +1 @@
|
||||
Error: Unable to access jarfile OpenRocket.jar
|
Loading…
x
Reference in New Issue
Block a user