diff --git a/core/resources/datafiles/thrustcurves/thrustcurves.ser b/core/resources/datafiles/thrustcurves/thrustcurves.ser index eef07e2d8..785f8b208 100644 Binary files a/core/resources/datafiles/thrustcurves/thrustcurves.ser and b/core/resources/datafiles/thrustcurves/thrustcurves.ser differ diff --git a/core/src/net/sf/openrocket/file/motor/AbstractMotorLoader.java b/core/src/net/sf/openrocket/file/motor/AbstractMotorLoader.java index 478e9447a..f15b43148 100644 --- a/core/src/net/sf/openrocket/file/motor/AbstractMotorLoader.java +++ b/core/src/net/sf/openrocket/file/motor/AbstractMotorLoader.java @@ -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); + // } + // } } } diff --git a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java index 3e851f260..c19c72b02 100644 --- a/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java +++ b/core/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -166,22 +166,30 @@ public class ThrustCurveMotor implements Motor, Comparable, 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, 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, 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; diff --git a/core/src/net/sf/openrocket/thrustcurve/DownloadResponseParser.java b/core/src/net/sf/openrocket/thrustcurve/DownloadResponseParser.java index 3941afad2..42eb695cd 100644 --- a/core/src/net/sf/openrocket/thrustcurve/DownloadResponseParser.java +++ b/core/src/net/sf/openrocket/thrustcurve/DownloadResponseParser.java @@ -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 attributes, String content, WarningSet warnings) throws SAXException { } -} \ No newline at end of file +} diff --git a/core/src/net/sf/openrocket/thrustcurve/MotorBurnFile.java b/core/src/net/sf/openrocket/thrustcurve/MotorBurnFile.java index 4f07c9551..420553376 100644 --- a/core/src/net/sf/openrocket/thrustcurve/MotorBurnFile.java +++ b/core/src/net/sf/openrocket/thrustcurve/MotorBurnFile.java @@ -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; + } } diff --git a/core/src/net/sf/openrocket/thrustcurve/SerializeThrustcurveMotors.java b/core/src/net/sf/openrocket/thrustcurve/SerializeThrustcurveMotors.java index e43d1a3a0..173e26375 100644 --- a/core/src/net/sf/openrocket/thrustcurve/SerializeThrustcurveMotors.java +++ b/core/src/net/sf/openrocket/thrustcurve/SerializeThrustcurveMotors.java @@ -115,36 +115,45 @@ public class SerializeThrustcurveMotors { System.out.println(message); List 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; } diff --git a/openrocket.log b/openrocket.log new file mode 100644 index 000000000..d5c1f8088 --- /dev/null +++ b/openrocket.log @@ -0,0 +1 @@ +Error: Unable to access jarfile OpenRocket.jar