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:
JoePfeiffer 2019-02-09 18:00:30 -07:00
parent 1abf0d3834
commit 6c535bff94
7 changed files with 168 additions and 95 deletions

View File

@ -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);
// }
// }
}
}

View File

@ -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;

View File

@ -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 {
}
}
}

View File

@ -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;
}
}

View File

@ -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
View File

@ -0,0 +1 @@
Error: Unable to access jarfile OpenRocket.jar