Implement feature to compute the optimum delay for a simulation.
This commit is contained in:
parent
4565cdcd86
commit
a34d41d6af
@ -430,6 +430,8 @@ simpanel.col.Motors = Motors
|
|||||||
simpanel.col.Configuration = Configuration
|
simpanel.col.Configuration = Configuration
|
||||||
simpanel.col.Velocityoffrod = Velocity off rod
|
simpanel.col.Velocityoffrod = Velocity off rod
|
||||||
simpanel.col.Velocityatdeploy = Velocity at deployment
|
simpanel.col.Velocityatdeploy = Velocity at deployment
|
||||||
|
simpanel.col.OptimumCoastTime = Optimum delay
|
||||||
|
simpanel.col.OptimumCoastTime.ttip = The time between last motor burnout and maximum possible altitude.
|
||||||
simpanel.col.Apogee = Apogee
|
simpanel.col.Apogee = Apogee
|
||||||
simpanel.col.Maxvelocity = Max. velocity
|
simpanel.col.Maxvelocity = Max. velocity
|
||||||
simpanel.col.Maxacceleration = Max. acceleration
|
simpanel.col.Maxacceleration = Max. acceleration
|
||||||
|
@ -536,6 +536,7 @@ public class OpenRocketSaver extends RocketSaver {
|
|||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("<databranch name=\"");
|
sb.append("<databranch name=\"");
|
||||||
sb.append(TextUtil.escapeXML(branch.getBranchName()));
|
sb.append(TextUtil.escapeXML(branch.getBranchName()));
|
||||||
|
sb.append("\" ");
|
||||||
|
|
||||||
// Kevins version where typekeys are used
|
// Kevins version where typekeys are used
|
||||||
/*
|
/*
|
||||||
@ -547,7 +548,19 @@ public class OpenRocketSaver extends RocketSaver {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sb.append("\" types=\"");
|
if (!Double.isNaN(branch.getOptimumAltitude())) {
|
||||||
|
sb.append("optimumAltitude=\"");
|
||||||
|
sb.append(branch.getOptimumAltitude());
|
||||||
|
sb.append("\" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Double.isNaN(branch.getTimeToOptimumAltitude())) {
|
||||||
|
sb.append("timeToOptimumAltitude=\"");
|
||||||
|
sb.append(branch.getTimeToOptimumAltitude());
|
||||||
|
sb.append("\" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("types=\"");
|
||||||
for (int i = 0; i < types.length; i++) {
|
for (int i = 0; i < types.length; i++) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
sb.append(",");
|
sb.append(",");
|
||||||
@ -590,8 +603,6 @@ public class OpenRocketSaver extends RocketSaver {
|
|||||||
writeln("</databranch>");
|
writeln("</databranch>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* TODO: LOW: This is largely duplicated from above! */
|
/* TODO: LOW: This is largely duplicated from above! */
|
||||||
private int countFlightDataBranchPoints(FlightDataBranch branch, double timeSkip) {
|
private int countFlightDataBranchPoints(FlightDataBranch branch, double timeSkip) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@ -42,6 +42,22 @@ class FlightDataBranchHandler extends AbstractElementHandler {
|
|||||||
branch = new FlightDataBranch(name, types);
|
branch = new FlightDataBranch(name, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param timeToOptimumAltitude
|
||||||
|
* @see net.sf.openrocket.simulation.FlightDataBranch#setTimeToOptimumAltitude(double)
|
||||||
|
*/
|
||||||
|
public void setTimeToOptimumAltitude(double timeToOptimumAltitude) {
|
||||||
|
branch.setTimeToOptimumAltitude(timeToOptimumAltitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param optimumAltitude
|
||||||
|
* @see net.sf.openrocket.simulation.FlightDataBranch#setOptimumAltitude(double)
|
||||||
|
*/
|
||||||
|
public void setOptimumAltitude(double optimumAltitude) {
|
||||||
|
branch.setOptimumAltitude(optimumAltitude);
|
||||||
|
}
|
||||||
|
|
||||||
// Find the full flight data type given name only
|
// Find the full flight data type given name only
|
||||||
// Note: this way of doing it requires that custom expressions always come before flight data in the file,
|
// Note: this way of doing it requires that custom expressions always come before flight data in the file,
|
||||||
// not the nicest but this is always the case anyway.
|
// not the nicest but this is always the case anyway.
|
||||||
|
@ -48,6 +48,23 @@ class FlightDataHandler extends AbstractElementHandler {
|
|||||||
dataHandler = new FlightDataBranchHandler(attributes.get("name"),
|
dataHandler = new FlightDataBranchHandler(attributes.get("name"),
|
||||||
attributes.get("types"),
|
attributes.get("types"),
|
||||||
simHandler, context);
|
simHandler, context);
|
||||||
|
|
||||||
|
if (attributes.get("optimumAltitude") != null) {
|
||||||
|
double optimumAltitude = Double.NaN;
|
||||||
|
try {
|
||||||
|
optimumAltitude = Double.parseDouble(attributes.get("optimumAltitude"));
|
||||||
|
} catch (NumberFormatException ignore) {
|
||||||
|
}
|
||||||
|
dataHandler.setOptimumAltitude(optimumAltitude);
|
||||||
|
}
|
||||||
|
if (attributes.get("timeToOptimumAltitude") != null) {
|
||||||
|
double timeToOptimumAltitude = Double.NaN;
|
||||||
|
try {
|
||||||
|
timeToOptimumAltitude = Double.parseDouble(attributes.get("timeToOptimumAltitude"));
|
||||||
|
} catch (NumberFormatException ignore) {
|
||||||
|
}
|
||||||
|
dataHandler.setTimeToOptimumAltitude(timeToOptimumAltitude);
|
||||||
|
}
|
||||||
return dataHandler;
|
return dataHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import net.sf.openrocket.simulation.exception.MotorIgnitionException;
|
|||||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||||
import net.sf.openrocket.simulation.exception.SimulationLaunchException;
|
import net.sf.openrocket.simulation.exception.SimulationLaunchException;
|
||||||
import net.sf.openrocket.simulation.listeners.SimulationListenerHelper;
|
import net.sf.openrocket.simulation.listeners.SimulationListenerHelper;
|
||||||
|
import net.sf.openrocket.simulation.listeners.system.OptimumCoastListener;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
import net.sf.openrocket.unit.UnitGroup;
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
import net.sf.openrocket.util.Coordinate;
|
import net.sf.openrocket.util.Coordinate;
|
||||||
@ -120,8 +121,6 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
Coordinate originVelocity = status.getRocketVelocity();
|
Coordinate originVelocity = status.getRocketVelocity();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
double maxAlt = Double.NEGATIVE_INFINITY;
|
|
||||||
|
|
||||||
// Start the simulation
|
// Start the simulation
|
||||||
while (handleEvents()) {
|
while (handleEvents()) {
|
||||||
|
|
||||||
@ -149,8 +148,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
status.getConfiguration().getRocket(),
|
status.getConfiguration().getRocket(),
|
||||||
new Pair<Double, Double>(oldAlt, status.getRocketPosition().z)));
|
new Pair<Double, Double>(oldAlt, status.getRocketPosition().z)));
|
||||||
|
|
||||||
if (status.getRocketPosition().z > maxAlt) {
|
if (status.getRocketPosition().z > status.getMaxAlt()) {
|
||||||
maxAlt = status.getRocketPosition().z;
|
status.setMaxAlt(status.getRocketPosition().z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -189,7 +188,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
|
|
||||||
|
|
||||||
// Check for apogee
|
// Check for apogee
|
||||||
if (!status.isApogeeReached() && status.getRocketPosition().z < maxAlt - 0.01) {
|
if (!status.isApogeeReached() && status.getRocketPosition().z < status.getMaxAlt() - 0.01) {
|
||||||
|
status.setMaxAltTime(status.getSimulationTime());
|
||||||
addEvent(new FlightEvent(FlightEvent.Type.APOGEE, status.getSimulationTime(),
|
addEvent(new FlightEvent(FlightEvent.Type.APOGEE, status.getSimulationTime(),
|
||||||
status.getConfiguration().getRocket()));
|
status.getConfiguration().getRocket()));
|
||||||
}
|
}
|
||||||
@ -464,6 +464,11 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
// Mark apogee as reached
|
// Mark apogee as reached
|
||||||
status.setApogeeReached(true);
|
status.setApogeeReached(true);
|
||||||
status.getFlightData().addEvent(event);
|
status.getFlightData().addEvent(event);
|
||||||
|
// This apogee event might be the optimum if recovery has not already happened.
|
||||||
|
if (status.getSimulationConditions().isCalculateExtras() && status.getDeployedRecoveryDevices().size() == 0) {
|
||||||
|
status.getFlightData().setOptimumAltitude(status.getMaxAlt());
|
||||||
|
status.getFlightData().setTimeToOptimumAltitude(status.getMaxAltTime());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RECOVERY_DEVICE_DEPLOYMENT:
|
case RECOVERY_DEVICE_DEPLOYMENT:
|
||||||
@ -501,6 +506,14 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
status.setLiftoff(true);
|
status.setLiftoff(true);
|
||||||
status.getDeployedRecoveryDevices().add((RecoveryDevice) c);
|
status.getDeployedRecoveryDevices().add((RecoveryDevice) c);
|
||||||
|
|
||||||
|
// If we haven't already reached apogee, then we need to compute the actual coast time
|
||||||
|
// to determine the optimum altitude.
|
||||||
|
if (status.getSimulationConditions().isCalculateExtras() && !status.isApogeeReached()) {
|
||||||
|
FlightData coastStatus = computeCoastTime();
|
||||||
|
status.getFlightData().setOptimumAltitude(coastStatus.getMaxAltitude());
|
||||||
|
status.getFlightData().setTimeToOptimumAltitude(coastStatus.getTimeToApogee());
|
||||||
|
}
|
||||||
|
|
||||||
this.currentStepper = this.landingStepper;
|
this.currentStepper = this.landingStepper;
|
||||||
this.status = currentStepper.initialize(status);
|
this.status = currentStepper.initialize(status);
|
||||||
|
|
||||||
@ -601,5 +614,17 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FlightData computeCoastTime() {
|
||||||
|
try {
|
||||||
|
SimulationConditions conds = status.getSimulationConditions().clone();
|
||||||
|
conds.getSimulationListenerList().add(OptimumCoastListener.INSTANCE);
|
||||||
|
BasicEventSimulationEngine e = new BasicEventSimulationEngine();
|
||||||
|
|
||||||
|
FlightData d = e.simulate(conds);
|
||||||
|
return d;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Exception computing coast time: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,14 @@ public class FlightDataBranch implements Monitorable {
|
|||||||
private final Map<FlightDataType, Double> maxValues = new HashMap<FlightDataType, Double>();
|
private final Map<FlightDataType, Double> maxValues = new HashMap<FlightDataType, Double>();
|
||||||
private final Map<FlightDataType, Double> minValues = new HashMap<FlightDataType, Double>();
|
private final Map<FlightDataType, Double> minValues = new HashMap<FlightDataType, Double>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* time for the rocket to reach apogee if the flight had been no recovery deployment
|
||||||
|
*/
|
||||||
|
private double timeToOptimumAltitude = Double.NaN;
|
||||||
|
/**
|
||||||
|
* Altitude the rocket would reach if there had been no recovery deployment.
|
||||||
|
*/
|
||||||
|
private double optimumAltitude = Double.NaN;
|
||||||
|
|
||||||
private final ArrayList<FlightEvent> events = new ArrayList<FlightEvent>();
|
private final ArrayList<FlightEvent> events = new ArrayList<FlightEvent>();
|
||||||
|
|
||||||
@ -73,7 +81,7 @@ public class FlightDataBranch implements Monitorable {
|
|||||||
*/
|
*/
|
||||||
public FlightDataBranch() {
|
public FlightDataBranch() {
|
||||||
branchName = "Empty branch";
|
branchName = "Empty branch";
|
||||||
for (FlightDataType type : FlightDataType.ALL_TYPES){
|
for (FlightDataType type : FlightDataType.ALL_TYPES) {
|
||||||
this.setValue(type, Double.NaN);
|
this.setValue(type, Double.NaN);
|
||||||
}
|
}
|
||||||
this.immute();
|
this.immute();
|
||||||
@ -118,7 +126,7 @@ public class FlightDataBranch implements Monitorable {
|
|||||||
maxValues.put(type, value);
|
maxValues.put(type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list.size() > 0){
|
if (list.size() > 0) {
|
||||||
list.set(list.size() - 1, value);
|
list.set(list.size() - 1, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,6 +227,50 @@ public class FlightDataBranch implements Monitorable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the timeToOptimumAltitude
|
||||||
|
*/
|
||||||
|
public double getTimeToOptimumAltitude() {
|
||||||
|
return timeToOptimumAltitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param timeToOptimumAltitude the timeToOptimumAltitude to set
|
||||||
|
*/
|
||||||
|
public void setTimeToOptimumAltitude(double timeToOptimumAltitude) {
|
||||||
|
this.timeToOptimumAltitude = timeToOptimumAltitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the optimumAltitude
|
||||||
|
*/
|
||||||
|
public double getOptimumAltitude() {
|
||||||
|
return optimumAltitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param optimumAltitude the optimumAltitude to set
|
||||||
|
*/
|
||||||
|
public void setOptimumAltitude(double optimumAltitude) {
|
||||||
|
this.optimumAltitude = optimumAltitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getOptimumDelay() {
|
||||||
|
|
||||||
|
if (Double.isNaN(timeToOptimumAltitude)) {
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
// TODO - we really want the first burnout of this stage. which
|
||||||
|
// could be computed as the first burnout after the last stage separation event.
|
||||||
|
// however, that's not quite so concise
|
||||||
|
FlightEvent e = getLastEvent(FlightEvent.Type.BURNOUT);
|
||||||
|
if (e != null) {
|
||||||
|
return timeToOptimumAltitude - e.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a flight event to this branch.
|
* Add a flight event to this branch.
|
||||||
*
|
*
|
||||||
@ -241,6 +293,34 @@ public class FlightDataBranch implements Monitorable {
|
|||||||
return events.clone();
|
return events.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the first event of the given type.
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public FlightEvent getFirstEvent(FlightEvent.Type type) {
|
||||||
|
for (FlightEvent e : events) {
|
||||||
|
if (e.getType() == type) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the last event of the given type.
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public FlightEvent getLastEvent(FlightEvent.Type type) {
|
||||||
|
FlightEvent retval = null;
|
||||||
|
for (FlightEvent e : events) {
|
||||||
|
if (e.getType() == type) {
|
||||||
|
retval = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make this FlightDataBranch immutable. Any calls to the set methods that would
|
* Make this FlightDataBranch immutable. Any calls to the set methods that would
|
||||||
|
@ -268,7 +268,7 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
|||||||
this.simulation = sim;
|
this.simulation = sim;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Simulation getSimulation(){
|
public Simulation getSimulation() {
|
||||||
return this.simulation;
|
return this.simulation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +291,12 @@ public class SimulationConditions implements Monitorable, Cloneable {
|
|||||||
public SimulationConditions clone() {
|
public SimulationConditions clone() {
|
||||||
try {
|
try {
|
||||||
// TODO: HIGH: Deep clone models
|
// TODO: HIGH: Deep clone models
|
||||||
return (SimulationConditions) super.clone();
|
SimulationConditions clone = (SimulationConditions) super.clone();
|
||||||
|
clone.simulationListeners = new ArrayList<SimulationListener>(this.simulationListeners.size());
|
||||||
|
for (SimulationListener listener : this.simulationListeners) {
|
||||||
|
clone.simulationListeners.add(listener.clone());
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
} catch (CloneNotSupportedException e) {
|
} catch (CloneNotSupportedException e) {
|
||||||
throw new BugException(e);
|
throw new BugException(e);
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,6 @@ import net.sf.openrocket.util.WorldCoordinate;
|
|||||||
*/
|
*/
|
||||||
public class SimulationStatus implements Monitorable {
|
public class SimulationStatus implements Monitorable {
|
||||||
|
|
||||||
/*
|
|
||||||
* NOTE! All fields must be added to copyFrom() method!!
|
|
||||||
*/
|
|
||||||
|
|
||||||
private SimulationConditions simulationConditions;
|
private SimulationConditions simulationConditions;
|
||||||
private Configuration configuration;
|
private Configuration configuration;
|
||||||
private MotorInstanceConfiguration motorConfiguration;
|
private MotorInstanceConfiguration motorConfiguration;
|
||||||
@ -83,13 +79,15 @@ public class SimulationStatus implements Monitorable {
|
|||||||
/** Available for special purposes by the listeners. */
|
/** Available for special purposes by the listeners. */
|
||||||
private final Map<String, Object> extraData = new HashMap<String, Object>();
|
private final Map<String, Object> extraData = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
double maxAlt = Double.NEGATIVE_INFINITY;
|
||||||
|
double maxAltTime = 0;
|
||||||
|
|
||||||
private int modID = 0;
|
private int modID = 0;
|
||||||
private int modIDadd = 0;
|
private int modIDadd = 0;
|
||||||
|
|
||||||
public SimulationStatus( Configuration configuration,
|
public SimulationStatus(Configuration configuration,
|
||||||
MotorInstanceConfiguration motorConfiguration,
|
MotorInstanceConfiguration motorConfiguration,
|
||||||
SimulationConditions simulationConditions ) {
|
SimulationConditions simulationConditions) {
|
||||||
|
|
||||||
this.simulationConditions = simulationConditions;
|
this.simulationConditions = simulationConditions;
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
@ -163,7 +161,7 @@ public class SimulationStatus implements Monitorable {
|
|||||||
*
|
*
|
||||||
* @param orig the object from which to copy
|
* @param orig the object from which to copy
|
||||||
*/
|
*/
|
||||||
public SimulationStatus( SimulationStatus orig ) {
|
public SimulationStatus(SimulationStatus orig) {
|
||||||
this.simulationConditions = orig.simulationConditions.clone();
|
this.simulationConditions = orig.simulationConditions.clone();
|
||||||
this.configuration = orig.configuration.clone();
|
this.configuration = orig.configuration.clone();
|
||||||
this.motorConfiguration = orig.motorConfiguration.clone();
|
this.motorConfiguration = orig.motorConfiguration.clone();
|
||||||
@ -292,7 +290,7 @@ public class SimulationStatus implements Monitorable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean addBurntOutMotor( MotorId motor ) {
|
public boolean addBurntOutMotor(MotorId motor) {
|
||||||
return motorBurntOut.add(motor);
|
return motorBurntOut.add(motor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +382,7 @@ public class SimulationStatus implements Monitorable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setTumbling( boolean tumbling ) {
|
public void setTumbling(boolean tumbling) {
|
||||||
this.tumbling = tumbling;
|
this.tumbling = tumbling;
|
||||||
this.modID++;
|
this.modID++;
|
||||||
}
|
}
|
||||||
@ -393,6 +391,24 @@ public class SimulationStatus implements Monitorable {
|
|||||||
return tumbling;
|
return tumbling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getMaxAlt() {
|
||||||
|
return maxAlt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxAlt(double maxAlt) {
|
||||||
|
this.maxAlt = maxAlt;
|
||||||
|
this.modID++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMaxAltTime() {
|
||||||
|
return maxAltTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxAltTime(double maxAltTime) {
|
||||||
|
this.maxAltTime = maxAltTime;
|
||||||
|
this.modID++;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<RecoveryDevice> getDeployedRecoveryDevices() {
|
public Set<RecoveryDevice> getDeployedRecoveryDevices() {
|
||||||
return deployedRecoveryDevices;
|
return deployedRecoveryDevices;
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ public class AbstractSimulationListener implements SimulationListener, Simulatio
|
|||||||
* Return an array of any flight data types this listener creates.
|
* Return an array of any flight data types this listener creates.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public FlightDataType[] getFlightDataTypes(){
|
public FlightDataType[] getFlightDataTypes() {
|
||||||
return new FlightDataType[] {};
|
return new FlightDataType[] {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,4 +183,9 @@ public class AbstractSimulationListener implements SimulationListener, Simulatio
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractSimulationListener clone() throws CloneNotSupportedException {
|
||||||
|
return (AbstractSimulationListener) super.clone();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,13 @@ import net.sf.openrocket.simulation.FlightDataType;
|
|||||||
import net.sf.openrocket.simulation.SimulationStatus;
|
import net.sf.openrocket.simulation.SimulationStatus;
|
||||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to simulation events and possibly take action.
|
||||||
public interface SimulationListener {
|
*
|
||||||
|
* If the implementation maintains any state, it should be properly cloned.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface SimulationListener extends Cloneable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of this simulation listener. Ideally this should be localized, as
|
* Get the name of this simulation listener. Ideally this should be localized, as
|
||||||
@ -83,5 +87,5 @@ public interface SimulationListener {
|
|||||||
*/
|
*/
|
||||||
public FlightDataType[] getFlightDataTypes();
|
public FlightDataType[] getFlightDataTypes();
|
||||||
|
|
||||||
|
public SimulationListener clone() throws CloneNotSupportedException;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package net.sf.openrocket.simulation.listeners.system;
|
||||||
|
|
||||||
|
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
|
||||||
|
import net.sf.openrocket.simulation.FlightEvent;
|
||||||
|
import net.sf.openrocket.simulation.SimulationStatus;
|
||||||
|
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||||
|
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulation listener which ignores recovery deployment events and ends the simulation
|
||||||
|
* when apogee is reached.
|
||||||
|
*
|
||||||
|
* @author kevin
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OptimumCoastListener extends AbstractSimulationListener {
|
||||||
|
|
||||||
|
public static final OptimumCoastListener INSTANCE = new OptimumCoastListener();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) {
|
||||||
|
if (event.getType() == FlightEvent.Type.APOGEE) {
|
||||||
|
status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean recoveryDeviceDeployment(SimulationStatus status, RecoveryDevice recoveryDevice) throws SimulationException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSystemListener() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -6,6 +6,7 @@ import javax.swing.table.TableColumnModel;
|
|||||||
|
|
||||||
public abstract class Column {
|
public abstract class Column {
|
||||||
private final String name;
|
private final String name;
|
||||||
|
private final String toolTip;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new column with specified name. Additionally, the {@link #getValueAt(int)}
|
* Create a new column with specified name. Additionally, the {@link #getValueAt(int)}
|
||||||
@ -15,6 +16,17 @@ public abstract class Column {
|
|||||||
*/
|
*/
|
||||||
public Column(String name) {
|
public Column(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.toolTip = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new column with specified name and toolTip.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Column(String name, String toolTip ) {
|
||||||
|
this.name = name;
|
||||||
|
this.toolTip = toolTip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,4 +100,8 @@ public abstract class Column {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getToolTip() {
|
||||||
|
return toolTip;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
28
swing/src/net/sf/openrocket/gui/adaptors/ColumnTable.java
Normal file
28
swing/src/net/sf/openrocket/gui/adaptors/ColumnTable.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package net.sf.openrocket.gui.adaptors;
|
||||||
|
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.table.JTableHeader;
|
||||||
|
|
||||||
|
public class ColumnTable extends JTable {
|
||||||
|
|
||||||
|
public ColumnTable( ColumnTableModel model ) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JTableHeader createDefaultTableHeader() {
|
||||||
|
return new JTableHeader( columnModel ) {
|
||||||
|
public String getToolTipText(MouseEvent e) {
|
||||||
|
String tip = null;
|
||||||
|
java.awt.Point p = e.getPoint();
|
||||||
|
int index = columnModel.getColumnIndexAtX(p.x);
|
||||||
|
int realIndex = columnModel.getColumn(index).getModelIndex();
|
||||||
|
tip = ((ColumnTableModel) getModel()).getColumn(realIndex).getToolTip();
|
||||||
|
return tip;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -15,6 +15,11 @@ public abstract class ValueColumn extends Column {
|
|||||||
this.unit = unit;
|
this.unit = unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ValueColumn( String name, String toolTip, UnitGroup unit ) {
|
||||||
|
super( name, toolTip );
|
||||||
|
this.unit = unit;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getValueAt(int row) {
|
public Object getValueAt(int row) {
|
||||||
Double d = valueAt(row);
|
Double d = valueAt(row);
|
||||||
|
@ -43,6 +43,7 @@ import net.sf.openrocket.aerodynamics.FlightConditions;
|
|||||||
import net.sf.openrocket.aerodynamics.Warning;
|
import net.sf.openrocket.aerodynamics.Warning;
|
||||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||||
import net.sf.openrocket.gui.adaptors.Column;
|
import net.sf.openrocket.gui.adaptors.Column;
|
||||||
|
import net.sf.openrocket.gui.adaptors.ColumnTable;
|
||||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||||
import net.sf.openrocket.gui.adaptors.DoubleModel;
|
import net.sf.openrocket.gui.adaptors.DoubleModel;
|
||||||
import net.sf.openrocket.gui.adaptors.FlightConfigurationModel;
|
import net.sf.openrocket.gui.adaptors.FlightConfigurationModel;
|
||||||
@ -243,7 +244,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
table = new JTable(cpTableModel);
|
table = new ColumnTable(cpTableModel);
|
||||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
table.setSelectionBackground(Color.LIGHT_GRAY);
|
table.setSelectionBackground(Color.LIGHT_GRAY);
|
||||||
table.setSelectionForeground(Color.BLACK);
|
table.setSelectionForeground(Color.BLACK);
|
||||||
|
@ -40,6 +40,7 @@ import javax.swing.table.TableRowSorter;
|
|||||||
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import net.sf.openrocket.gui.adaptors.Column;
|
import net.sf.openrocket.gui.adaptors.Column;
|
||||||
|
import net.sf.openrocket.gui.adaptors.ColumnTable;
|
||||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||||
import net.sf.openrocket.gui.components.SelectableLabel;
|
import net.sf.openrocket.gui.components.SelectableLabel;
|
||||||
import net.sf.openrocket.gui.util.GUIUtil;
|
import net.sf.openrocket.gui.util.GUIUtil;
|
||||||
@ -266,7 +267,7 @@ public class DebugLogDialog extends JDialog {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
table = new JTable(model);
|
table = new ColumnTable(model);
|
||||||
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
table.setSelectionBackground(Color.LIGHT_GRAY);
|
table.setSelectionBackground(Color.LIGHT_GRAY);
|
||||||
table.setSelectionForeground(Color.BLACK);
|
table.setSelectionForeground(Color.BLACK);
|
||||||
|
@ -23,6 +23,7 @@ import net.miginfocom.swing.MigLayout;
|
|||||||
import net.sf.openrocket.database.Database;
|
import net.sf.openrocket.database.Database;
|
||||||
import net.sf.openrocket.database.Databases;
|
import net.sf.openrocket.database.Databases;
|
||||||
import net.sf.openrocket.gui.adaptors.Column;
|
import net.sf.openrocket.gui.adaptors.Column;
|
||||||
|
import net.sf.openrocket.gui.adaptors.ColumnTable;
|
||||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||||
import net.sf.openrocket.gui.components.StyledLabel;
|
import net.sf.openrocket.gui.components.StyledLabel;
|
||||||
import net.sf.openrocket.gui.components.StyledLabel.Style;
|
import net.sf.openrocket.gui.components.StyledLabel.Style;
|
||||||
@ -108,7 +109,7 @@ public class MaterialEditPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
table = new JTable(model);
|
table = new ColumnTable(model);
|
||||||
model.setColumnWidths(table.getColumnModel());
|
model.setColumnWidths(table.getColumnModel());
|
||||||
table.setAutoCreateRowSorter(true);
|
table.setAutoCreateRowSorter(true);
|
||||||
table.setDefaultRenderer(Object.class, new MaterialCellRenderer());
|
table.setDefaultRenderer(Object.class, new MaterialCellRenderer());
|
||||||
|
@ -35,6 +35,7 @@ import net.sf.openrocket.document.events.DocumentChangeListener;
|
|||||||
import net.sf.openrocket.document.events.SimulationChangeEvent;
|
import net.sf.openrocket.document.events.SimulationChangeEvent;
|
||||||
import net.sf.openrocket.formatting.RocketDescriptor;
|
import net.sf.openrocket.formatting.RocketDescriptor;
|
||||||
import net.sf.openrocket.gui.adaptors.Column;
|
import net.sf.openrocket.gui.adaptors.Column;
|
||||||
|
import net.sf.openrocket.gui.adaptors.ColumnTable;
|
||||||
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
import net.sf.openrocket.gui.adaptors.ColumnTableModel;
|
||||||
import net.sf.openrocket.gui.adaptors.ColumnTableRowSorter;
|
import net.sf.openrocket.gui.adaptors.ColumnTableRowSorter;
|
||||||
import net.sf.openrocket.gui.adaptors.ValueColumn;
|
import net.sf.openrocket.gui.adaptors.ValueColumn;
|
||||||
@ -48,6 +49,7 @@ import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
|
|||||||
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
|
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
|
||||||
import net.sf.openrocket.rocketcomponent.Configuration;
|
import net.sf.openrocket.rocketcomponent.Configuration;
|
||||||
import net.sf.openrocket.simulation.FlightData;
|
import net.sf.openrocket.simulation.FlightData;
|
||||||
|
import net.sf.openrocket.simulation.FlightEvent;
|
||||||
import net.sf.openrocket.startup.Application;
|
import net.sf.openrocket.startup.Application;
|
||||||
import net.sf.openrocket.startup.Preferences;
|
import net.sf.openrocket.startup.Preferences;
|
||||||
import net.sf.openrocket.unit.UnitGroup;
|
import net.sf.openrocket.unit.UnitGroup;
|
||||||
@ -389,6 +391,27 @@ public class SimulationPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//// Deployment Time from Apogee
|
||||||
|
new ValueColumn(trans.get("simpanel.col.OptimumCoastTime"),
|
||||||
|
trans.get("simpanel.col.OptimumCoastTime.ttip"),
|
||||||
|
UnitGroup.UNITS_SHORT_TIME) {
|
||||||
|
@Override
|
||||||
|
public Double valueAt(int row) {
|
||||||
|
if (row < 0 || row >= document.getSimulationCount())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
FlightData data = document.getSimulation(row).getSimulatedData();
|
||||||
|
if (data == null || data.getBranchCount() == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
double val = data.getBranch(0).getOptimumDelay();
|
||||||
|
if ( Double.isNaN(val) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
//// Maximum velocity
|
//// Maximum velocity
|
||||||
new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) {
|
new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) {
|
||||||
@Override
|
@Override
|
||||||
@ -473,7 +496,7 @@ public class SimulationPanel extends JPanel {
|
|||||||
|
|
||||||
// Override processKeyBinding so that the JTable does not catch
|
// Override processKeyBinding so that the JTable does not catch
|
||||||
// key bindings used in menu accelerators
|
// key bindings used in menu accelerators
|
||||||
simulationTable = new JTable(simulationTableModel) {
|
simulationTable = new ColumnTable(simulationTableModel) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean processKeyBinding(KeyStroke ks,
|
protected boolean processKeyBinding(KeyStroke ks,
|
||||||
KeyEvent e,
|
KeyEvent e,
|
||||||
|
@ -125,6 +125,7 @@ public class DesignReport {
|
|||||||
private static final String SIZE = "Size";
|
private static final String SIZE = "Size";
|
||||||
private static final String ALTITUDE = "Altitude";
|
private static final String ALTITUDE = "Altitude";
|
||||||
private static final String FLIGHT_TIME = "Flight Time";
|
private static final String FLIGHT_TIME = "Flight Time";
|
||||||
|
private static final String OPTIMUM_DELAY = "Optimum Delay";
|
||||||
private static final String TIME_TO_APOGEE = "Time to Apogee";
|
private static final String TIME_TO_APOGEE = "Time to Apogee";
|
||||||
private static final String VELOCITY_OFF_PAD = "Velocity off Pad";
|
private static final String VELOCITY_OFF_PAD = "Velocity off Pad";
|
||||||
private static final String MAX_VELOCITY = "Max Velocity";
|
private static final String MAX_VELOCITY = "Max Velocity";
|
||||||
@ -468,6 +469,9 @@ public class DesignReport {
|
|||||||
labelTable.addCell(ITextHelper.createCell(TIME_TO_APOGEE, 2, 2));
|
labelTable.addCell(ITextHelper.createCell(TIME_TO_APOGEE, 2, 2));
|
||||||
labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getTimeToApogee()), 2, 2));
|
labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getTimeToApogee()), 2, 2));
|
||||||
|
|
||||||
|
labelTable.addCell(ITextHelper.createCell(OPTIMUM_DELAY, 2, 2));
|
||||||
|
labelTable.addCell(ITextHelper.createCell(flightTimeUnit.toStringUnit(flight.getBranch(0).getOptimumDelay()), 2, 2));
|
||||||
|
|
||||||
labelTable.addCell(ITextHelper.createCell(VELOCITY_OFF_PAD, 2, 2));
|
labelTable.addCell(ITextHelper.createCell(VELOCITY_OFF_PAD, 2, 2));
|
||||||
labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getLaunchRodVelocity()), 2, 2));
|
labelTable.addCell(ITextHelper.createCell(velocityUnit.toStringUnit(flight.getLaunchRodVelocity()), 2, 2));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user