[Bugfix] Merged FlightConfiguration MotorInstanceConfiguration

fixed ignition configuration load / save
This commit is contained in:
Daniel_M_Williams 2015-12-03 21:44:34 -05:00
parent 890d390b4b
commit 729effd690
25 changed files with 548 additions and 533 deletions

View File

@ -94,9 +94,9 @@ public abstract class AbstractAerodynamicCalculator implements AerodynamicCalcul
protected final void checkCache(FlightConfiguration configuration) {
if (rocketAeroModID != configuration.getRocket().getAerodynamicModID() ||
rocketTreeModID != configuration.getRocket().getTreeModID()) {
// vvvv DEVEL vvvv
log.error("Voiding the aerodynamic cache because modIDs changed...", new BugException(" unsure why modID has changed..."));
// ^^^^ DEVEL ^^^^
// // vvvv DEVEL vvvv
// log.error("Voiding the aerodynamic cache because modIDs changed...", new BugException(" unsure why modID has changed..."));
// // ^^^^ DEVEL ^^^^
rocketAeroModID = configuration.getRocket().getAerodynamicModID();
rocketTreeModID = configuration.getRocket().getTreeModID();

View File

@ -354,7 +354,7 @@ public class OpenRocketSaver extends RocketSaver {
continue;
MotorMount mount = (MotorMount) c;
for( FlightConfiguration config : document.getRocket().getConfigurationSet()) {
for( FlightConfiguration config : document.getRocket().getConfigSet()) {
FlightConfigurationID fcid = config.getFlightConfigurationID();
if (mount.getMotorInstance(fcid) != null) {
return FILE_VERSION_DIVISOR + 4;

View File

@ -30,19 +30,6 @@ class IgnitionConfigurationHandler extends AbstractElementHandler {
return PlainTextHandler.INSTANCE;
}
// public IgnitionConfiguration getConfiguration(IgnitionConfiguration def) {
// IgnitionConfiguration config = def.clone();
// if (ignitionEvent != null) {
// config.setIgnitionEvent(ignitionEvent);
// }
// if (ignitionDelay != null) {
// config.setIgnitionDelay(ignitionDelay);
// }
// return config;
// }
//
@Override
public void closeElement(String element, HashMap<String, String> attributes,
String content, WarningSet warnings) throws SAXException {

View File

@ -64,7 +64,7 @@ class MotorConfigurationHandler extends AbstractElementHandler {
if ("true".equals(attributes.remove("default"))) {
// associate this configuration with both this FCID and the default.
ParameterSet<FlightConfiguration> fcs = rocket.getConfigurationSet();
ParameterSet<FlightConfiguration> fcs = rocket.getConfigSet();
FlightConfiguration fc = fcs.get(fcid);
fcs.setDefault(fc);
}

View File

@ -41,7 +41,7 @@ class MotorMountHandler extends AbstractElementHandler {
}
if (element.equals("ignitionconfiguration")) {
ignitionConfigHandler = new IgnitionConfigurationHandler(context);
ignitionConfigHandler = new IgnitionConfigurationHandler( context);
return ignitionConfigHandler;
}
@ -65,7 +65,7 @@ class MotorMountHandler extends AbstractElementHandler {
// System.err.println("closing MotorMount element: "+ element);
if (element.equals("motor")) {
// yes, this is confirmed to be the FLIGHT config id, instead of the motor instance id.
// yes, this is confirmed to be the FLIGHT config id == motor instance id.
FlightConfigurationID fcid = new FlightConfigurationID(attributes.get("configid"));
if (!fcid.isValid()) {
warnings.add(Warning.fromString("Illegal motor specification, ignoring."));
@ -93,11 +93,9 @@ class MotorMountHandler extends AbstractElementHandler {
return;
}
MotorInstance inst = mount.getDefaultMotorInstance();
// ignitionConfigHandler.getConfiguration(null); // all the parsing / loading into the confighandler should already be done...
MotorInstance inst = mount.getMotorInstance(fcid);
inst.setIgnitionDelay(ignitionConfigHandler.ignitionDelay);
inst.setIgnitionEvent(ignitionConfigHandler.ignitionEvent);
return;
}

View File

@ -51,10 +51,10 @@ public class AxialStageSaver extends ComponentAssemblySaver {
// Note - getFlightConfigurationIDs returns at least one element. The first element
// is null and means "default".
int configCount = rocket.getConfigurationSet().size();
int configCount = rocket.getConfigSet().size();
if (1 < configCount ){
for (FlightConfiguration curConfig : rocket.getConfigurationSet()){
for (FlightConfiguration curConfig : rocket.getConfigSet()){
FlightConfigurationID fcid = curConfig.getFlightConfigurationID();
if (fcid == null) {
continue;

View File

@ -37,7 +37,7 @@ public class RecoveryDeviceSaver extends MassObjectSaver {
//dev.getDeploymentConfigurations().printDebug();
// DEBUG
ParameterSet<FlightConfiguration> configList = rocket.getConfigurationSet();
ParameterSet<FlightConfiguration> configList = rocket.getConfigSet();
for (FlightConfigurationID fcid : configList.getSortedConfigurationIDs()) {
//System.err.println("checking FlightConfiguration:"+fcid.getShortKey()+ " save?");

View File

@ -43,7 +43,7 @@ public class RocketSaver extends RocketComponentSaver {
// Motor configurations
ParameterSet<FlightConfiguration> allConfigs = rocket.getConfigurationSet();
ParameterSet<FlightConfiguration> allConfigs = rocket.getConfigSet();
for (FlightConfigurationID fcid : allConfigs.getSortedConfigurationIDs()) {
FlightConfiguration flightConfig = allConfigs.get(fcid);
if (fcid == null)

View File

@ -58,7 +58,7 @@ public class BodyTubeDTO extends BasePartDTO implements AttachableParts {
@XmlElementRef(name = RocksimCommonConstants.STREAMER, type = StreamerDTO.class),
@XmlElementRef(name = RocksimCommonConstants.PARACHUTE, type = ParachuteDTO.class),
@XmlElementRef(name = RocksimCommonConstants.MASS_OBJECT, type = MassObjectDTO.class)})
List<BasePartDTO> attachedParts = new ArrayList();
List<BasePartDTO> attachedParts = new ArrayList<BasePartDTO>();
/**
* Constructor.

View File

@ -187,28 +187,13 @@ public class MotorInstance implements FlightConfigurableParameter<MotorInstance>
@Override
public boolean equals( Object other ){
if( other == null )
if( null == other ){
return false;
if( other instanceof MotorInstance ){
}else if( other instanceof MotorInstance ){
MotorInstance omi = (MotorInstance)other;
if( this.id.equals( omi.id)){
return true;
}
// }else if( this.mount != omi.mount ){
// return false;
// }else if( this.ignitionEvent == omi.ignitionEvent ){
// return false;
// }else if( EPSILON < Math.abs(this.ignitionDelay - omi.ignitionDelay )){
// return false;
// }else if( EPSILON < Math.abs( this.ejectionDelay - omi.ejectionDelay )){
// return false;
// }else if( ! this.position.equals( omi.position )){
// return false;
// }else if( EPSILON < Math.abs( this.ignitionTime - omi.ignitionTime )){
// return false;
// }
return true;
}
return false;
}

View File

@ -1,241 +0,0 @@
package net.sf.openrocket.motor;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.FlightConfigurationID;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.ArrayList;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Monitorable;
/**
* A configuration of motor instances identified by a MotorInstanceId. Each motor instance has
* an individual position, ingition time etc.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class MotorInstanceConfiguration implements Cloneable, Iterable<MotorInstance>, Monitorable {
protected final HashMap<MotorInstanceId, MotorInstance> motors = new HashMap<MotorInstanceId, MotorInstance>();
protected final FlightConfiguration config;
private int modID = 0;
private MotorInstanceConfiguration() {
this.config = null;
}
/**
* Create a new motor instance configuration for the rocket configuration.
*
* @param configuration the rocket configuration.
*/
public MotorInstanceConfiguration( final FlightConfiguration _configuration) {
this.config = _configuration;
update();
}
/**
* Add a motor instance to this configuration. The motor is placed at
* the specified position and with an infinite ignition time (never ignited).
*
* @param id the ID of this motor instance.
* @param motor the motor instance.
* @param mount the motor mount containing this motor
* @param ignitionEvent the ignition event for the motor
* @param ignitionDelay the ignition delay for the motor
* @param position the position of the motor in absolute coordinates.
* @throws IllegalArgumentException if a motor with the specified ID already exists.
*/
// public void addMotor(MotorId _id, Motor _motor, double _ejectionDelay, MotorMount _mount,
// IgnitionEvent _ignitionEvent, double _ignitionDelay, Coordinate _position) {
//
// MotorInstance instanceToAdd = new MotorInstance(_id, _motor, _mount, _ejectionDelay,
// _ignitionEvent, _ignitionDelay, _position);
//
//
// // this.ids.add(id);
// // this.motors.add(motor);
// // this.ejectionDelays.add(ejectionDelay);
// // this.mounts.add(mount);
// // this.ignitionEvents.add(ignitionEvent);
// // this.ignitionDelays.add(ignitionDelay);
// // this.positions.add(position);
// // this.ignitionTimes.add(Double.POSITIVE_INFINITY);
// }
/**
* Add a motor instance to this configuration.
*
* @param motor the motor instance.
* @throws IllegalArgumentException if a motor with the specified ID already exists.
*/
public void addMotor(MotorInstance motor) {
if( motor.isEmpty() ){
throw new IllegalArgumentException("MotorInstance is empty.");
}
MotorInstanceId id = motor.getID();
if (this.motors.containsKey(id)) {
throw new IllegalArgumentException("MotorInstanceConfiguration already " +
"contains a motor with id " + id);
}
this.motors.put(id, motor);
modID++;
}
public Collection<MotorInstance> getAllMotors() {
return motors.values();
}
public int getMotorCount() {
return motors.size();
}
public Set<MotorInstanceId> getMotorIDs() {
return this.motors.keySet();
}
public MotorInstance getMotorInstance(MotorInstanceId id) {
return motors.get(id);
}
public boolean hasMotors() {
return (0 < motors.size());
}
/**
* Step all of the motor instances to the specified time minus their ignition time.
* @param time the "global" time
*/
public void step(double time, double acceleration, AtmosphericConditions cond) {
for (MotorInstance inst : motors.values()) {
double t = time - inst.getIgnitionTime();
if (t >= 0) {
inst.step(t, acceleration, cond);
}
}
modID++;
}
@Override
public int getModID() {
int id = modID;
for (MotorInstance motor : motors.values()) {
id += motor.getModID();
}
return id;
}
/**
* Return a copy of this motor instance configuration with independent motor instances
* from this instance.
*/
@Override
public MotorInstanceConfiguration clone() {
MotorInstanceConfiguration clone = new MotorInstanceConfiguration();
for (MotorInstance motor : this.motors.values()) {
clone.motors.put(motor.getID(), motor.clone());
}
clone.modID = this.modID;
return clone;
}
@Override
public Iterator<MotorInstance> iterator() {
return this.motors.values().iterator();
}
public List<MotorInstance> getActiveMotors() {
List<MotorInstance> activeList = new ArrayList<MotorInstance>();
for( MotorInstance inst : this.motors.values() ){
if( inst.isActive() ){
activeList.add( inst );
}
}
return activeList;
}
public void populate( final MotorInstanceConfiguration source){
this.motors.putAll( source.motors );
}
public void update() {
this.motors.clear();
FlightConfigurationID fcid = this.config.getFlightConfigurationID();
for ( RocketComponent comp : this.config.getActiveComponents() ){
if ( comp instanceof MotorMount ){
MotorMount mount = (MotorMount)comp;
MotorInstance inst = mount.getMotorInstance( fcid);
if( inst.isEmpty()){
continue;
}
// this merely accounts for instancing of *this* component:
// int instancCount = comp.getInstanceCount();
// this includes *all* the instancing between here and the rocket root.
Coordinate[] instanceLocations= comp.getLocations();
// System.err.println(String.format(",,,,,,,, : %s (%s)",
// inst.getMotor().getDigest(), inst.getMotorID() ));
int instanceNumber = 0;
for ( Coordinate curMountLocation : instanceLocations ){
MotorInstance curInstance = inst.clone();
curInstance.setID( new MotorInstanceId( comp.getName(), instanceNumber+1) );
// motor location w/in mount: parent.refpoint -> motor.refpoint
Coordinate curMotorOffset = curInstance.getOffset();
curInstance.setPosition( curMountLocation.add(curMotorOffset) );
this.motors.put( curInstance.getID(), curInstance);
// vvvv DEVEL vvvv
// System.err.println(String.format(",,,,,,,,[ %2d]: %s. (%s)",
// instanceNumber, curInstance.getMotor().getDigest(), curInstance));
// ^^^^ DEVEL ^^^^
instanceNumber ++;
}
}
}
//System.err.println("returning "+toReturn.size()+" active motor instances for this configuration: "+this.fcid.getShortKey());
//System.err.println(this.rocket.getConfigurationSet().toDebug());
}
@Override
public String toString(){
StringBuilder buff = new StringBuilder("[");
boolean first = true;
for( MotorInstance motor : this.motors.values() ){
if( first ){
first = false;
}else{
buff.append(", ");
}
buff.append(motor.getMotor().getDesignation());
}
buff.append("]");
return buff.toString();
}
public String toDebugString(){
StringBuilder buff = new StringBuilder();
for( MotorInstance motor : this.motors.values() ){
final String idString = motor.getID().toShortKey();
final String activeString = motor.isActive()? " on": "off";
final String nameString = motor.getMotor().getDesignation();
buff.append( String.format(" ..[%8s][%s] %10s", idString, activeString, nameString));
}
return buff.toString();
}
}

View File

@ -5,16 +5,17 @@ import java.util.Collection;
import java.util.EventListener;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.motor.MotorInstanceId;
import net.sf.openrocket.util.ArrayList;
import net.sf.openrocket.util.ChangeSource;
@ -56,7 +57,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
/* Cached data */
final protected HashMap<Integer, StageFlags> stages = new HashMap<Integer, StageFlags>();
final protected MotorInstanceConfiguration motors;
protected final HashMap<MotorInstanceId, MotorInstance> motors = new HashMap<MotorInstanceId, MotorInstance>();
private int boundsModID = -1;
private ArrayList<Coordinate> cachedBounds = new ArrayList<Coordinate>();
@ -67,6 +68,8 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
private int modID = 0;
private boolean debug = false;
/**
* Create a new configuration with the specified <code>Rocket</code>.
*
@ -82,13 +85,16 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
this.rocket = rocket;
this.isNamed = false;
this.configurationName = "<WARN: attempt to access unset configurationName. WARN!> ";
this.motors = new MotorInstanceConfiguration(this);
updateStageMap();
this.motors.update();
updateStages();
updateMotors();
rocket.addComponentChangeListener(this);
}
public void enableDebugging(){
this.debug=true;
}
public Rocket getRocket() {
return rocket;
}
@ -114,17 +120,19 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
*
* @param stageNumber stage number to inactivate
*/
public void clearOnlyStage(final int stageNumber) {
setStageActive(stageNumber, false);
public void clearStage(final int stageNumber) {
setStageActive( stageNumber, false);
}
/**
* This method flags a stage active. Other stages are unaffected.
* This method flags the specified stage as active, and all other stages as inactive.
*
* @param stageNumber stage number to activate
*/
public void setOnlyStage(final int stageNumber) {
setAllStages(false);
setStageActive(stageNumber, true);
fireChangeEvent();
}
/**
@ -161,7 +169,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
if (stageNumber >= this.rocket.getStageCount()) {
return false;
}
// DEVEL
if( ! stages.containsKey(stageNumber)){
throw new IllegalArgumentException(" Configuration does not contain stage number: "+stageNumber);
}
@ -188,18 +196,6 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
return toReturn;
}
public List<MotorInstance> getActiveMotors() {
return this.motors.getActiveMotors();
}
public Collection<MotorInstance> getAllMotors() {
return this.motors.getAllMotors();
}
public boolean hasMotors() {
return this.motors.hasMotors();
}
public List<AxialStage> getActiveStages() {
List<AxialStage> activeStages = new ArrayList<AxialStage>();
@ -241,10 +237,6 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
return stages.size();
}
public MotorInstance getMotor( final MotorInstanceId mid ){
return this.motors.getMotorInstance( mid );
}
/**
* Return the reference length associated with the current configuration. The
* reference length type is retrieved from the <code>Rocket</code>.
@ -267,6 +259,10 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
return fcid;
}
public FlightConfigurationID getId() {
return getFlightConfigurationID();
}
/**
* Removes the listener connection to the rocket and listeners of this object.
* This configuration may not be used after a call to this method!
@ -304,11 +300,14 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
}
}
updateStageMap();
motors.update();
updateStages();
updateMotors();
}
private void updateStageMap() {
private void updateStages() {
if( debug){
System.err.println("updating config stages");
}
if (this.rocket.getStageCount() == this.stages.size()) {
// no changes needed
return;
@ -334,7 +333,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
return configurationName;
}else{
if( this.hasMotors()){
return fcid.toShortKey()+" - "+this.motors.toString();
return fcid.toShortKey()+" - "+this.getMotorsOneline();
}else{
return fcid.getFullKeyText();
}
@ -365,8 +364,8 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
// DEBUG / DEVEL
public String toMotorDetail(){
StringBuilder buff = new StringBuilder();
buff.append(String.format("\nDumping %2d Motors for configuration %s: \n", this.motors.getMotorCount(), this.fcid.toShortKey()));
for( MotorInstance curMotor : this.getAllMotors()){
buff.append(String.format("\nDumping %2d Motors for configuration %s: \n", this.motors.size(), this.fcid.toShortKey()));
for( MotorInstance curMotor : this.motors.values() ){
if( curMotor.isEmpty() ){
buff.append( String.format( " ..[%8s] <empty> \n", curMotor.getID().toShortKey()));
}else{
@ -378,7 +377,36 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
}
}
return buff.toString();
}
public String getMotorsOneline(){
StringBuilder buff = new StringBuilder("[");
boolean first = true;
for ( RocketComponent comp : getActiveComponents() ){
if (( comp instanceof MotorMount )&&( ((MotorMount)comp).isMotorMount())){
MotorMount mount = (MotorMount)comp;
MotorInstance inst = mount.getMotorInstance( fcid);
if( first ){
first = false;
}else{
buff.append(";");
}
if( ! inst.isEmpty()){
buff.append( inst.getMotor().getDesignation());
}
}
}
buff.append("]");
return buff.toString();
}
@Override
public String toString() {
@ -388,11 +416,155 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
@Override
public void componentChanged(ComponentChangeEvent cce) {
// update according to incoming events
updateStageMap();
this.motors.update();
updateStages();
updateMotors();
}
/**
* Add a motor instance to this configuration. The motor is placed at
* the specified position and with an infinite ignition time (never ignited).
*
* @param id the ID of this motor instance.
* @param motor the motor instance.
* @param mount the motor mount containing this motor
* @param ignitionEvent the ignition event for the motor
* @param ignitionDelay the ignition delay for the motor
* @param position the position of the motor in absolute coordinates.
* @throws IllegalArgumentException if a motor with the specified ID already exists.
*/
// public void addMotor(MotorId _id, Motor _motor, double _ejectionDelay, MotorMount _mount,
// IgnitionEvent _ignitionEvent, double _ignitionDelay, Coordinate _position) {
//
// MotorInstance instanceToAdd = new MotorInstance(_id, _motor, _mount, _ejectionDelay,
// _ignitionEvent, _ignitionDelay, _position);
//
//
// // this.ids.add(id);
// // this.motors.add(motor);
// // this.ejectionDelays.add(ejectionDelay);
// // this.mounts.add(mount);
// // this.ignitionEvents.add(ignitionEvent);
// // this.ignitionDelays.add(ignitionDelay);
// // this.positions.add(position);
// // this.ignitionTimes.add(Double.POSITIVE_INFINITY);
// }
/**
* Add a motor instance to this configuration.
*
* @param motor the motor instance.
* @throws IllegalArgumentException if a motor with the specified ID already exists.
*/
public void addMotor(MotorInstance motor) {
if( motor.isEmpty() ){
throw new IllegalArgumentException("MotorInstance is empty.");
}
MotorInstanceId id = motor.getID();
if (this.motors.containsKey(id)) {
throw new IllegalArgumentException("MotorInstanceConfiguration already " +
"contains a motor with id " + id);
}
this.motors.put(id, motor);
modID++;
}
public Collection<MotorInstance> getAllMotors() {
return motors.values();
}
public int getMotorCount() {
return motors.size();
}
public Set<MotorInstanceId> getMotorIDs() {
return this.motors.keySet();
}
public MotorInstance getMotorInstance(MotorInstanceId id) {
return motors.get(id);
}
public boolean hasMotors() {
return (0 < motors.size());
}
/**
* Step all of the motor instances to the specified time minus their ignition time.
* @param time the "global" time
*/
public void step(double time, double acceleration, AtmosphericConditions cond) {
for (MotorInstance inst : motors.values()) {
double t = time - inst.getIgnitionTime();
if (t >= 0) {
inst.step(t, acceleration, cond);
}
}
modID++;
}
public List<MotorInstance> getActiveMotors() {
List<MotorInstance> activeList = new ArrayList<MotorInstance>();
for( MotorInstance inst : this.motors.values() ){
if( inst.isActive() ){
activeList.add( inst );
}
}
return activeList;
}
public void updateMotors() {
if( debug){
System.err.println("updating config motors");
}
this.motors.clear();
for ( RocketComponent comp : getActiveComponents() ){
if (( comp instanceof MotorMount )&&( ((MotorMount)comp).isMotorMount())){
MotorMount mount = (MotorMount)comp;
MotorInstance inst = mount.getMotorInstance( fcid);
if( inst.isEmpty()){
continue;
}
// this merely accounts for instancing of *this* component:
// int instancCount = comp.getInstanceCount();
// this includes *all* the instancing between here and the rocket root.
Coordinate[] instanceLocations= comp.getLocations();
if( debug){
System.err.println(String.format(",,,,,,,, %s (%s)",
inst.getMotor().getDigest(), inst.getID() ));
}
int instanceNumber = 1;
final int instanceCount = instanceLocations.length;
for ( Coordinate curMountLocation : instanceLocations ){
MotorInstance curInstance = inst.clone();
curInstance.setID( new MotorInstanceId( comp.getName(), instanceNumber) );
// motor location w/in mount: parent.refpoint -> motor.refpoint
Coordinate curMotorOffset = curInstance.getOffset();
curInstance.setPosition( curMountLocation.add(curMotorOffset) );
this.motors.put( curInstance.getID(), curInstance);
// vvvv DEVEL vvvv
if(debug){
System.err.println(String.format(",,,,,,,, [%2d/%2d]: %s. (%s)",
instanceNumber, instanceCount, curInstance.getMotor().getDigest(), curInstance));
}
// ^^^^ DEVEL ^^^^
instanceNumber ++;
}
}
}
//System.err.println("returning "+toReturn.size()+" active motor instances for this configuration: "+this.fcid.getShortKey());
//System.err.println(this.rocket.getConfigurationSet().toDebug());
}
/////////////// Helper methods ///////////////
/**
@ -447,30 +619,34 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
return cachedLength;
}
/**
* Perform a deep-clone. The object references are also cloned and no
* listeners are listening on the cloned object. The rocket instance remains the same.
*/
@Override
public FlightConfiguration clone() {
FlightConfiguration config = new FlightConfiguration( this.getRocket(), this.fcid );
config.listenerList = new ArrayList<EventListener>();
config.stages.putAll( (Map<Integer, StageFlags>) this.stages);
config.motors.populate( this.motors );
config.cachedBounds = this.cachedBounds.clone();
config.boundsModID = -1;
config.refLengthModID = -1;
rocket.addComponentChangeListener(config);
return config;
FlightConfiguration clone = new FlightConfiguration( this.getRocket(), this.fcid );
clone.setName(this.fcid.toShortKey()+" - clone");
clone.listenerList = new ArrayList<EventListener>();
clone.stages.putAll( (Map<Integer, StageFlags>) this.stages);
clone.motors.putAll( (Map<MotorInstanceId, MotorInstance>) this.motors);
clone.cachedBounds = this.cachedBounds.clone();
clone.modID = this.modID;
clone.boundsModID = -1;
clone.refLengthModID = -1;
rocket.addComponentChangeListener(clone);
return clone;
}
@Override
public int getModID() {
return modID + rocket.getModID();
// TODO: this doesn't seem consistent...
int id = modID;
// for (MotorInstance motor : motors.values()) {
// id += motor.getModID();
// }
id += rocket.getModID();
return id;
}
public void setName( final String newName) {
@ -487,8 +663,4 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
this.configurationName = newName;
}
public void step(double time, double acceleration, AtmosphericConditions cond) {
this.motors.step( time, acceleration, cond);
}
}

View File

@ -401,14 +401,14 @@ public class Rocket extends RocketComponent {
// Update modification ID's only for normal (not undo/redo) events
{ // vvvv DEVEL vvvv
String changeString;
if (cce.isUndoChange()) {
changeString = "an 'undo' change from: "+cce.getSource().getName()+" as:"+cce.toString();
}else{
changeString = "a normal change from: "+cce.getSource().getName()+" as:"+cce.toString();
}
log.error("Processing a rocket change: "+changeString, new IllegalArgumentException());
// String changeString;
// if (cce.isUndoChange()) {
// changeString = "an 'undo' change from: "+cce.getSource().getName()+" as:"+cce.toString();
// }else{
// changeString = "a normal change from: "+cce.getSource().getName()+" as:"+cce.toString();
// }
//
// log.error("Processing a rocket change: "+changeString, new IllegalArgumentException());
} // ^^^^ DEVEL ^^^^
// Check whether frozen
@ -541,7 +541,7 @@ public class Rocket extends RocketComponent {
return this.configSet.size();
}
public ParameterSet<FlightConfiguration> getConfigurationSet(){
public ParameterSet<FlightConfiguration> getConfigSet(){
checkState();
return this.configSet;
}

View File

@ -1,10 +1,11 @@
package net.sf.openrocket.rocketcomponent;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.NoSuchElementException;
import org.slf4j.Logger;
@ -1082,17 +1083,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
}
this.offset = newOffset;
this.position = new Coordinate(newAxialPosition, this.position.y, this.position.z);
// if( this instanceof CenteringRing ){
// System.err.println("Moving "+this.getName()+"("+this.getID().substring(0, 8)+") to:"+newOffset+" via:"+positionMethod.name());
// System.err.println(" new Position = "+this.position);
// if( positionMethod == Position.BOTTOM){
// StackTraceElement[] stack = Thread.currentThread().getStackTrace();
// for( int i = 0; i < 12 ; i++){
// System.err.println( stack[i]);
// }
// }
// }
}
protected void update() {
@ -2029,7 +2019,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/
private static class RocketComponentIterator implements Iterator<RocketComponent> {
// Stack holds iterators which still have some components left.
private final Stack<Iterator<RocketComponent>> iteratorStack = new Stack<Iterator<RocketComponent>>();
private final Deque<Iterator<RocketComponent>> iteratorStack = new ArrayDeque<Iterator<RocketComponent>>();
private final Rocket root;
private final int treeModID;

View File

@ -185,6 +185,7 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
configuration.step(status.getSimulationTime() + timestep, acceleration, atmosphericConditions);
thrust = 0;
//?? needs to instance the motors...
List<MotorInstance> activeMotors = configuration.getActiveMotors();
for (MotorInstance currentMotorInstance : activeMotors) {
thrust += currentMotorInstance.getThrust();

View File

@ -1,8 +1,8 @@
package net.sf.openrocket.simulation;
import java.util.ArrayList;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -52,14 +52,17 @@ public class BasicEventSimulationEngine implements SimulationEngine {
private SimulationStepper currentStepper;
private SimulationStatus status;
private SimulationStatus currentStatus;
private FlightConfigurationID fcid;
// was a stack, but parallel staging breaks that
protected Stack<SimulationStatus> stages = new Stack<SimulationStatus>();
// protected ArrayList<SimulationStatus> burningStages = new ArrayList<SimulationStatus>();
// protected ArrayList<SimulationStatus> carriedStages = new ArrayList<SimulationStatus>();
// old: protected Stack<SimulationStatus> stages = new Stack<SimulationStatus>();
// this variable was class member stack, but parallel staging breaks metaphor:
// parallel stages may ignite before OR after their 'inner' stages
// this is just a list of simulation branches to
Deque<SimulationStatus> toSimulate = new ArrayDeque<SimulationStatus>();
@Override
@ -79,32 +82,33 @@ public class BasicEventSimulationEngine implements SimulationEngine {
throw new MotorIgnitionException(errorMessage);
}
status = new SimulationStatus(configuration, simulationConditions);
status.getEventQueue().add(new FlightEvent(FlightEvent.Type.LAUNCH, 0, simulationConditions.getRocket()));
currentStatus = new SimulationStatus(configuration, simulationConditions);
currentStatus.getEventQueue().add(new FlightEvent(FlightEvent.Type.LAUNCH, 0, simulationConditions.getRocket()));
{
// main sustainer stage
RocketComponent sustainer = configuration.getRocket().getChild(0);
status.setFlightData(new FlightDataBranch(sustainer.getName(), FlightDataType.TYPE_TIME));
// main simulation branch
final String branchName = configuration.getRocket().getTopmostStage().getName();
currentStatus.setFlightData(new FlightDataBranch( branchName, FlightDataType.TYPE_TIME));
}
stages.add(status);
toSimulate.add(currentStatus);
SimulationListenerHelper.fireStartSimulation(status);
while (true) {
if (stages.size() == 0) {
SimulationListenerHelper.fireStartSimulation(currentStatus);
do{
if( null == toSimulate.peek()){
break;
}
SimulationStatus stageStatus = stages.pop();
if (stageStatus == null) {
break;
}
status = stageStatus;
currentStatus = toSimulate.pop();
log.info(">>Starting simulation of branch: "+currentStatus.getFlightData().getBranchName());
FlightDataBranch dataBranch = simulateLoop();
flightData.addBranch(dataBranch);
flightData.getWarningSet().addAll(status.getWarnings());
}
flightData.getWarningSet().addAll(currentStatus.getWarnings());
log.info(String.format("<<Finished simulating branch: %s at:%s",
currentStatus.getFlightData().getBranchName(),
currentStatus.getFlightData().getLast(FlightDataType.TYPE_TIME)));
}while( ! toSimulate.isEmpty());
SimulationListenerHelper.fireEndSimulation(status, null);
SimulationListenerHelper.fireEndSimulation(currentStatus, null);
configuration.release();
@ -119,92 +123,95 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Initialize the simulation
currentStepper = flightStepper;
status = currentStepper.initialize(status);
currentStatus = currentStepper.initialize(currentStatus);
// Get originating position (in case listener has modified launch position)
Coordinate origin = status.getRocketPosition();
Coordinate originVelocity = status.getRocketVelocity();
Coordinate origin = currentStatus.getRocketPosition();
Coordinate originVelocity = currentStatus.getRocketVelocity();
try {
log.info(String.format(" >> Starting simulate loop."));
// Start the simulation
while (handleEvents()) {
log.info(String.format(" >> Events Handled."));
// Take the step
double oldAlt = status.getRocketPosition().z;
double oldAlt = currentStatus.getRocketPosition().z;
if (SimulationListenerHelper.firePreStep(status)) {
if (SimulationListenerHelper.firePreStep(currentStatus)) {
// Step at most to the next event
double maxStepTime = Double.MAX_VALUE;
FlightEvent nextEvent = status.getEventQueue().peek();
FlightEvent nextEvent = currentStatus.getEventQueue().peek();
if (nextEvent != null) {
maxStepTime = MathUtil.max(nextEvent.getTime() - status.getSimulationTime(), 0.001);
maxStepTime = MathUtil.max(nextEvent.getTime() - currentStatus.getSimulationTime(), 0.001);
}
log.trace("BasicEventSimulationEngine: Taking simulation step at t=" + status.getSimulationTime());
currentStepper.step(status, maxStepTime);
log.trace("BasicEventSimulationEngine: Taking simulation step at t=" + currentStatus.getSimulationTime());
currentStepper.step(currentStatus, maxStepTime);
}
SimulationListenerHelper.firePostStep(status);
SimulationListenerHelper.firePostStep(currentStatus);
// Check for NaN values in the simulation status
checkNaN();
// Add altitude event
addEvent(new FlightEvent(FlightEvent.Type.ALTITUDE, status.getSimulationTime(),
status.getConfiguration().getRocket(),
new Pair<Double, Double>(oldAlt, status.getRocketPosition().z)));
addEvent(new FlightEvent(FlightEvent.Type.ALTITUDE, currentStatus.getSimulationTime(),
currentStatus.getConfiguration().getRocket(),
new Pair<Double, Double>(oldAlt, currentStatus.getRocketPosition().z)));
if (status.getRocketPosition().z > status.getMaxAlt()) {
status.setMaxAlt(status.getRocketPosition().z);
if (currentStatus.getRocketPosition().z > currentStatus.getMaxAlt()) {
currentStatus.setMaxAlt(currentStatus.getRocketPosition().z);
}
// Position relative to start location
Coordinate relativePosition = status.getRocketPosition().sub(origin);
Coordinate relativePosition = currentStatus.getRocketPosition().sub(origin);
// Add appropriate events
if (!status.isLiftoff()) {
if (!currentStatus.isLiftoff()) {
// Avoid sinking into ground before liftoff
if (relativePosition.z < 0) {
status.setRocketPosition(origin);
status.setRocketVelocity(originVelocity);
currentStatus.setRocketPosition(origin);
currentStatus.setRocketVelocity(originVelocity);
}
// Detect lift-off
if (relativePosition.z > 0.02) {
addEvent(new FlightEvent(FlightEvent.Type.LIFTOFF, status.getSimulationTime()));
addEvent(new FlightEvent(FlightEvent.Type.LIFTOFF, currentStatus.getSimulationTime()));
}
} else {
// Check ground hit after liftoff
if (status.getRocketPosition().z < 0) {
status.setRocketPosition(status.getRocketPosition().setZ(0));
addEvent(new FlightEvent(FlightEvent.Type.GROUND_HIT, status.getSimulationTime()));
addEvent(new FlightEvent(FlightEvent.Type.SIMULATION_END, status.getSimulationTime()));
if (currentStatus.getRocketPosition().z < 0) {
currentStatus.setRocketPosition(currentStatus.getRocketPosition().setZ(0));
addEvent(new FlightEvent(FlightEvent.Type.GROUND_HIT, currentStatus.getSimulationTime()));
addEvent(new FlightEvent(FlightEvent.Type.SIMULATION_END, currentStatus.getSimulationTime()));
}
}
// Check for launch guide clearance
if (!status.isLaunchRodCleared() &&
relativePosition.length() > status.getSimulationConditions().getLaunchRodLength()) {
addEvent(new FlightEvent(FlightEvent.Type.LAUNCHROD, status.getSimulationTime(), null));
if (!currentStatus.isLaunchRodCleared() &&
relativePosition.length() > currentStatus.getSimulationConditions().getLaunchRodLength()) {
addEvent(new FlightEvent(FlightEvent.Type.LAUNCHROD, currentStatus.getSimulationTime(), null));
}
// Check for apogee
if (!status.isApogeeReached() && status.getRocketPosition().z < status.getMaxAlt() - 0.01) {
status.setMaxAltTime(status.getSimulationTime());
addEvent(new FlightEvent(FlightEvent.Type.APOGEE, status.getSimulationTime(),
status.getConfiguration().getRocket()));
if (!currentStatus.isApogeeReached() && currentStatus.getRocketPosition().z < currentStatus.getMaxAlt() - 0.01) {
currentStatus.setMaxAltTime(currentStatus.getSimulationTime());
addEvent(new FlightEvent(FlightEvent.Type.APOGEE, currentStatus.getSimulationTime(),
currentStatus.getConfiguration().getRocket()));
}
// Check for burnt out motors
for( MotorInstance motor : status.getConfiguration().getAllMotors()){
for( MotorInstance motor : currentStatus.getConfiguration().getAllMotors()){
MotorInstanceId motorId = motor.getID();
if (!motor.isActive() && status.addBurntOutMotor(motorId)) {
addEvent(new FlightEvent(FlightEvent.Type.BURNOUT, status.getSimulationTime(),
if (!motor.isActive() && currentStatus.addBurntOutMotor(motorId)) {
addEvent(new FlightEvent(FlightEvent.Type.BURNOUT, currentStatus.getSimulationTime(),
(RocketComponent) motor.getMount(), motorId));
}
}
@ -217,23 +224,23 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// and aoa > AOA_TUMBLE_CONDITION threshold
// and thrust < THRUST_TUMBLE_CONDITION threshold
if (!status.isTumbling()) {
final double t = status.getFlightData().getLast(FlightDataType.TYPE_THRUST_FORCE);
final double cp = status.getFlightData().getLast(FlightDataType.TYPE_CP_LOCATION);
final double cg = status.getFlightData().getLast(FlightDataType.TYPE_CG_LOCATION);
final double aoa = status.getFlightData().getLast(FlightDataType.TYPE_AOA);
if (!currentStatus.isTumbling()) {
final double t = currentStatus.getFlightData().getLast(FlightDataType.TYPE_THRUST_FORCE);
final double cp = currentStatus.getFlightData().getLast(FlightDataType.TYPE_CP_LOCATION);
final double cg = currentStatus.getFlightData().getLast(FlightDataType.TYPE_CG_LOCATION);
final double aoa = currentStatus.getFlightData().getLast(FlightDataType.TYPE_AOA);
final boolean wantToTumble = (cg > cp && aoa > AOA_TUMBLE_CONDITION);
if (wantToTumble) {
final boolean tooMuchThrust = t > THRUST_TUMBLE_CONDITION;
//final boolean isSustainer = status.getConfiguration().isStageActive(0);
final boolean isApogee = status.isApogeeReached();
final boolean isApogee = currentStatus.isApogeeReached();
if (tooMuchThrust) {
status.getWarnings().add(Warning.TUMBLE_UNDER_THRUST);
currentStatus.getWarnings().add(Warning.TUMBLE_UNDER_THRUST);
} else if (isApogee) {
addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, status.getSimulationTime()));
status.setTumbling(true);
addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, currentStatus.getSimulationTime()));
currentStatus.setTumbling(true);
}
}
@ -242,13 +249,13 @@ public class BasicEventSimulationEngine implements SimulationEngine {
}
} catch (SimulationException e) {
SimulationListenerHelper.fireEndSimulation(status, e);
SimulationListenerHelper.fireEndSimulation(currentStatus, e);
// Add FlightEvent for Abort.
status.getFlightData().addEvent(new FlightEvent(FlightEvent.Type.EXCEPTION, status.getSimulationTime(), status.getConfiguration().getRocket(), e.getLocalizedMessage()));
status.getWarnings().add(e.getLocalizedMessage());
currentStatus.getFlightData().addEvent(new FlightEvent(FlightEvent.Type.EXCEPTION, currentStatus.getSimulationTime(), currentStatus.getConfiguration().getRocket(), e.getLocalizedMessage()));
currentStatus.getWarnings().add(e.getLocalizedMessage());
}
return status.getFlightData();
return currentStatus.getFlightData();
}
/**
@ -260,18 +267,18 @@ public class BasicEventSimulationEngine implements SimulationEngine {
boolean ret = true;
FlightEvent event;
log.trace("HandleEvents: current branch = " + status.getFlightData().getBranchName());
log.trace("EventQueue = " + status.getEventQueue().toString());
log.trace("HandleEvents: current branch = " + currentStatus.getFlightData().getBranchName());
log.trace("EventQueue = " + currentStatus.getEventQueue().toString());
for (event = nextEvent(); event != null; event = nextEvent()) {
// Ignore events for components that are no longer attached to the rocket
if (event.getSource() != null && event.getSource().getParent() != null &&
!status.getConfiguration().isComponentActive(event.getSource())) {
!currentStatus.getConfiguration().isComponentActive(event.getSource())) {
continue;
}
// Call simulation listeners, allow aborting event handling
if (!SimulationListenerHelper.fireHandleFlightEvent(status, event)) {
if (!SimulationListenerHelper.fireHandleFlightEvent(currentStatus, event)) {
continue;
}
@ -279,25 +286,27 @@ public class BasicEventSimulationEngine implements SimulationEngine {
log.trace("BasicEventSimulationEngine: Handling event " + event);
}
log.trace(String.format(" >> about to ignite motors events"));
if (event.getType() == FlightEvent.Type.IGNITION) {
MotorMount mount = (MotorMount) event.getSource();
MotorInstanceId motorId = (MotorInstanceId) event.getData();
MotorInstance instance = status.getMotor(motorId);
if (!SimulationListenerHelper.fireMotorIgnition(status, motorId, mount, instance)) {
MotorInstance instance = currentStatus.getMotor(motorId);
if (!SimulationListenerHelper.fireMotorIgnition(currentStatus, motorId, mount, instance)) {
continue;
}
}
log.trace(String.format(" >> about to fire motors (?) events"));
if (event.getType() == FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT) {
RecoveryDevice device = (RecoveryDevice) event.getSource();
if (!SimulationListenerHelper.fireRecoveryDeviceDeployment(status, device)) {
if (!SimulationListenerHelper.fireRecoveryDeviceDeployment(currentStatus, device)) {
continue;
}
}
log.trace(String.format(" >> about to check for motors ignite events"));
// Check for motor ignition events, add ignition events to queue
for (MotorInstance motor : status.getFlightConfiguration().getActiveMotors() ){
for (MotorInstance motor : currentStatus.getFlightConfiguration().getActiveMotors() ){
MotorInstanceId mid = motor.getID();
IgnitionEvent ignitionEvent = motor.getIgnitionEvent();
MotorMount mount = motor.getMount();
@ -306,7 +315,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
if (ignitionEvent.isActivationEvent(event, component)) {
double ignitionDelay = motor.getIgnitionDelay();
addEvent(new FlightEvent(FlightEvent.Type.IGNITION,
status.getSimulationTime() + ignitionDelay,
currentStatus.getSimulationTime() + ignitionDelay,
component, mid));
}
}
@ -314,7 +323,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Check for stage separation event
for (AxialStage stage : status.getConfiguration().getActiveStages()) {
for (AxialStage stage : currentStatus.getConfiguration().getActiveStages()) {
int stageNo = stage.getStageNumber();
if (stageNo == 0)
continue;
@ -328,7 +337,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Check for recovery device deployment, add events to queue
for (RocketComponent c : status.getConfiguration().getActiveComponents()) {
for (RocketComponent c : currentStatus.getConfiguration().getActiveComponents()) {
if (!(c instanceof RecoveryDevice))
continue;
DeploymentConfiguration deployConfig = ((RecoveryDevice) c).getDeploymentConfigurations().get(this.fcid);
@ -339,92 +348,92 @@ public class BasicEventSimulationEngine implements SimulationEngine {
}
}
log.trace(String.format(" >> about to handle events"));
// Handle event
switch (event.getType()) {
case LAUNCH: {
status.getFlightData().addEvent(event);
currentStatus.getFlightData().addEvent(event);
break;
}
case IGNITION: {
// Ignite the motor
MotorInstanceId motorId = (MotorInstanceId) event.getData();
MotorInstance inst = status.getMotor( motorId);
MotorInstance inst = currentStatus.getMotor( motorId);
inst.setIgnitionTime(event.getTime());
status.setMotorIgnited(true);
status.getFlightData().addEvent(event);
currentStatus.setMotorIgnited(true);
currentStatus.getFlightData().addEvent(event);
break;
}
case LIFTOFF: {
// Mark lift-off as occurred
status.setLiftoff(true);
status.getFlightData().addEvent(event);
currentStatus.setLiftoff(true);
currentStatus.getFlightData().addEvent(event);
break;
}
case LAUNCHROD: {
// Mark launch rod as cleared
status.setLaunchRodCleared(true);
status.getFlightData().addEvent(event);
currentStatus.setLaunchRodCleared(true);
currentStatus.getFlightData().addEvent(event);
break;
}
case BURNOUT: {
// If motor burnout occurs without lift-off, abort
if (!status.isLiftoff()) {
if (!currentStatus.isLiftoff()) {
throw new SimulationLaunchException(trans.get("BasicEventSimulationEngine.error.earlyMotorBurnout"));
}
// Add ejection charge event
MotorInstanceId motorId = (MotorInstanceId) event.getData();
MotorInstance motor = status.getMotor( motorId);
MotorInstance motor = currentStatus.getMotor( motorId);
double delay = motor.getEjectionDelay();
if (delay != Motor.PLUGGED) {
addEvent(new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, status.getSimulationTime() + delay,
addEvent(new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, currentStatus.getSimulationTime() + delay,
event.getSource(), event.getData()));
}
status.getFlightData().addEvent(event);
currentStatus.getFlightData().addEvent(event);
break;
}
case EJECTION_CHARGE: {
status.getFlightData().addEvent(event);
currentStatus.getFlightData().addEvent(event);
break;
}
case STAGE_SEPARATION: {
// Record the event.
status.getFlightData().addEvent(event);
RocketComponent stage = event.getSource();
int n = stage.getStageNumber();
currentStatus.getFlightData().addEvent(event);
RocketComponent boosterStage = event.getSource();
int stageNumber = boosterStage.getStageNumber();
// Prepare the booster status for simulation.
SimulationStatus boosterStatus = new SimulationStatus(status);
boosterStatus.setFlightData(new FlightDataBranch(stage.getName(), FlightDataType.TYPE_TIME));
stages.add(boosterStatus);
SimulationStatus boosterStatus = new SimulationStatus(currentStatus);
boosterStatus.setFlightData(new FlightDataBranch(boosterStage.getName(), FlightDataType.TYPE_TIME));
// Mark the booster status as only having the booster.
boosterStatus.getConfiguration().setOnlyStage(stageNumber);
toSimulate.add(boosterStatus);
// Mark the status as having dropped the booster
status.getConfiguration().clearOnlyStage(n);
currentStatus.getConfiguration().clearStage( stageNumber);
// Mark the booster status as only having the booster.
boosterStatus.getConfiguration().setOnlyStage(n);
break;
}
case APOGEE:
// Mark apogee as reached
status.setApogeeReached(true);
status.getFlightData().addEvent(event);
currentStatus.setApogeeReached(true);
currentStatus.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());
if (currentStatus.getSimulationConditions().isCalculateExtras() && currentStatus.getDeployedRecoveryDevices().size() == 0) {
currentStatus.getFlightData().setOptimumAltitude(currentStatus.getMaxAlt());
currentStatus.getFlightData().setTimeToOptimumAltitude(currentStatus.getMaxAltTime());
}
break;
@ -432,54 +441,54 @@ public class BasicEventSimulationEngine implements SimulationEngine {
RocketComponent c = event.getSource();
int n = c.getStageNumber();
// Ignore event if stage not active
if (status.getConfiguration().isStageActive(n)) {
if (currentStatus.getConfiguration().isStageActive(n)) {
// TODO: HIGH: Check stage activeness for other events as well?
// Check whether any motor in the active stages is active anymore
List<MotorInstance> activeMotors = status.getConfiguration().getActiveMotors();
List<MotorInstance> activeMotors = currentStatus.getConfiguration().getActiveMotors();
for (MotorInstance curMotor : activeMotors) {
RocketComponent comp = ((RocketComponent) curMotor.getMount());
int stageNumber = comp.getStageNumber();
if (!status.getConfiguration().isStageActive(stageNumber))
if (!currentStatus.getConfiguration().isStageActive(stageNumber))
continue;
status.getWarnings().add(Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING);
currentStatus.getWarnings().add(Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING);
}
// Check for launch rod
if (!status.isLaunchRodCleared()) {
status.getWarnings().add(Warning.RECOVERY_LAUNCH_ROD);
if (!currentStatus.isLaunchRodCleared()) {
currentStatus.getWarnings().add(Warning.RECOVERY_LAUNCH_ROD);
}
// Check current velocity
if (status.getRocketVelocity().length() > 20) {
status.getWarnings().add(new Warning.HighSpeedDeployment(status.getRocketVelocity().length()));
if (currentStatus.getRocketVelocity().length() > 20) {
currentStatus.getWarnings().add(new Warning.HighSpeedDeployment(currentStatus.getRocketVelocity().length()));
}
status.setLiftoff(true);
status.getDeployedRecoveryDevices().add((RecoveryDevice) c);
currentStatus.setLiftoff(true);
currentStatus.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()) {
if (currentStatus.getSimulationConditions().isCalculateExtras() && !currentStatus.isApogeeReached()) {
FlightData coastStatus = computeCoastTime();
status.getFlightData().setOptimumAltitude(coastStatus.getMaxAltitude());
status.getFlightData().setTimeToOptimumAltitude(coastStatus.getTimeToApogee());
currentStatus.getFlightData().setOptimumAltitude(coastStatus.getMaxAltitude());
currentStatus.getFlightData().setTimeToOptimumAltitude(coastStatus.getTimeToApogee());
}
this.currentStepper = this.landingStepper;
this.status = currentStepper.initialize(status);
this.currentStatus = currentStepper.initialize(currentStatus);
status.getFlightData().addEvent(event);
currentStatus.getFlightData().addEvent(event);
}
break;
case GROUND_HIT:
status.getFlightData().addEvent(event);
currentStatus.getFlightData().addEvent(event);
break;
case SIMULATION_END:
ret = false;
status.getFlightData().addEvent(event);
currentStatus.getFlightData().addEvent(event);
break;
case ALTITUDE:
@ -487,8 +496,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
case TUMBLE:
this.currentStepper = this.tumbleStepper;
this.status = currentStepper.initialize(status);
status.getFlightData().addEvent(event);
this.currentStatus = currentStepper.initialize(currentStatus);
currentStatus.getFlightData().addEvent(event);
break;
}
@ -496,7 +505,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// If no motor has ignited, abort
if (!status.isMotorIgnited()) {
if (!currentStatus.isMotorIgnited()) {
throw new MotorIgnitionException(trans.get("BasicEventSimulationEngine.error.noIgnition"));
}
@ -509,8 +518,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
* @param event the event to add to the queue.
*/
private void addEvent(FlightEvent event) throws SimulationException {
if (SimulationListenerHelper.fireAddFlightEvent(status, event)) {
status.getEventQueue().add(event);
if (SimulationListenerHelper.fireAddFlightEvent(currentStatus, event)) {
currentStatus.getEventQueue().add(event);
}
}
@ -524,16 +533,16 @@ public class BasicEventSimulationEngine implements SimulationEngine {
* @return the flight event to handle, or null
*/
private FlightEvent nextEvent() {
EventQueue queue = status.getEventQueue();
EventQueue queue = currentStatus.getEventQueue();
FlightEvent event = queue.peek();
if (event == null)
return null;
// Jump to event if no motors have been ignited
if (!status.isMotorIgnited() && event.getTime() > status.getSimulationTime()) {
status.setSimulationTime(event.getTime());
if (!currentStatus.isMotorIgnited() && event.getTime() > currentStatus.getSimulationTime()) {
currentStatus.setSimulationTime(event.getTime());
}
if (event.getTime() <= status.getSimulationTime()) {
if (event.getTime() <= currentStatus.getSimulationTime()) {
return queue.poll();
} else {
return null;
@ -545,30 +554,30 @@ public class BasicEventSimulationEngine implements SimulationEngine {
private void checkNaN() throws SimulationException {
double d = 0;
boolean b = false;
d += status.getSimulationTime();
d += status.getPreviousTimeStep();
b |= status.getRocketPosition().isNaN();
b |= status.getRocketVelocity().isNaN();
b |= status.getRocketOrientationQuaternion().isNaN();
b |= status.getRocketRotationVelocity().isNaN();
d += status.getEffectiveLaunchRodLength();
d += currentStatus.getSimulationTime();
d += currentStatus.getPreviousTimeStep();
b |= currentStatus.getRocketPosition().isNaN();
b |= currentStatus.getRocketVelocity().isNaN();
b |= currentStatus.getRocketOrientationQuaternion().isNaN();
b |= currentStatus.getRocketRotationVelocity().isNaN();
d += currentStatus.getEffectiveLaunchRodLength();
if (Double.isNaN(d) || b) {
log.error("Simulation resulted in NaN value:" +
" simulationTime=" + status.getSimulationTime() +
" previousTimeStep=" + status.getPreviousTimeStep() +
" rocketPosition=" + status.getRocketPosition() +
" rocketVelocity=" + status.getRocketVelocity() +
" rocketOrientationQuaternion=" + status.getRocketOrientationQuaternion() +
" rocketRotationVelocity=" + status.getRocketRotationVelocity() +
" effectiveLaunchRodLength=" + status.getEffectiveLaunchRodLength());
" simulationTime=" + currentStatus.getSimulationTime() +
" previousTimeStep=" + currentStatus.getPreviousTimeStep() +
" rocketPosition=" + currentStatus.getRocketPosition() +
" rocketVelocity=" + currentStatus.getRocketVelocity() +
" rocketOrientationQuaternion=" + currentStatus.getRocketOrientationQuaternion() +
" rocketRotationVelocity=" + currentStatus.getRocketRotationVelocity() +
" effectiveLaunchRodLength=" + currentStatus.getEffectiveLaunchRodLength());
throw new SimulationException(trans.get("BasicEventSimulationEngine.error.NaNResult"));
}
}
private FlightData computeCoastTime() {
try {
SimulationConditions conds = status.getSimulationConditions().clone();
SimulationConditions conds = currentStatus.getSimulationConditions().clone();
conds.getSimulationListenerList().add(OptimumCoastListener.INSTANCE);
BasicEventSimulationEngine e = new BasicEventSimulationEngine();

View File

@ -25,8 +25,9 @@ import net.sf.openrocket.util.WorldCoordinate;
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class SimulationStatus implements Monitorable {
private SimulationConditions simulationConditions;
private FlightConfiguration configuration;
private FlightDataBranch flightData;
@ -235,7 +236,7 @@ public class SimulationStatus implements Monitorable {
}
public MotorInstance getMotor( final MotorInstanceId motorId ){
return this.getFlightConfiguration().getMotor(motorId);
return this.getFlightConfiguration().getMotorInstance(motorId);
}
public double getPreviousTimeStep() {

View File

@ -9,7 +9,12 @@ import java.util.EventObject;
import org.junit.Test;
import net.sf.openrocket.motor.Manufacturer;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.ThrustCurveMotor;
import net.sf.openrocket.rocketcomponent.RocketComponent.Position;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.StateChangeListener;
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
@ -97,7 +102,6 @@ public class ConfigurationTest extends BaseTestCase {
// TODO rocket has no motors! assertTrue(config.hasMotors());
// rocket info tests
double length = config.getLength();
double refLength = config.getReferenceLength();
double refArea = config.getReferenceArea();
@ -191,16 +195,17 @@ public class ConfigurationTest extends BaseTestCase {
// test explicitly setting all stages up to second stage active
config.setOnlyStage(1);
assertThat(config.toStageListDetail() + "Setting single stage active: ", config.isStageActive(0), equalTo(false));
assertThat(config.toStageListDetail() + "Setting single stage active: ", config.isStageActive(1), equalTo(true));
config.clearOnlyStage(0);
config.clearStage(0);
assertThat(" deactivate stage #0: ", config.isStageActive(0), equalTo(false));
assertThat(" deactive stage #0: ", config.isStageActive(1), equalTo(true));
assertThat(" active stage #1: ", config.isStageActive(1), equalTo(true));
// test explicitly setting all two stages active
config.setAllStages();
assertThat(" activate all stages: check #0: ", config.isStageActive(0), equalTo(true));
assertThat(" activate all stages: check #1: ", config.isStageActive(1), equalTo(true));
assertThat(" activate all stages: check stage #0: ", config.isStageActive(0), equalTo(true));
assertThat(" activate all stages: check stage #1: ", config.isStageActive(1), equalTo(true));
// test toggling single stage
config.setAllStages();
@ -218,6 +223,48 @@ public class ConfigurationTest extends BaseTestCase {
}
/**
* Multi stage rocket specific configuration tests
*/
@Test
public void testMotorClusters() {
/* Setup */
Rocket rkt = makeTwoStageMotorRocket();
FlightConfiguration config = rkt.getDefaultConfiguration();
config.clearAllStages();
int expectedMotorCount = 0;
int actualMotorCount = config.getActiveMotors().size();
assertThat("motor count doesn't match", actualMotorCount, equalTo(expectedMotorCount));
config.setOnlyStage(0);
expectedMotorCount = 1;
actualMotorCount = config.getActiveMotors().size();
assertThat("motor count doesn't match: ", actualMotorCount, equalTo(expectedMotorCount));
config.setOnlyStage(1);
expectedMotorCount = 2;
actualMotorCount = config.getActiveMotors().size();
assertThat("motor count doesn't match: ", actualMotorCount, equalTo(expectedMotorCount));
config.setAllStages();
expectedMotorCount = 3;
// {
// System.err.println("Booster Stage only: config set detail: "+rkt.getConfigSet().toDebug());
// System.err.println("Booster Stage only: config stage detail: "+config.toStageListDetail());
// System.err.println("Booster Stage only: config motor detail: "+config.toMotorDetail());
// config.enableDebugging();
// config.updateMotors();
// config.getActiveMotors();
// }
actualMotorCount = config.getActiveMotors().size();
assertThat("motor count doesn't match: ", actualMotorCount, equalTo(expectedMotorCount));
}
///////////////////// Helper Methods ////////////////////////////
//
// public void validateStages(Configuration config, int expectedStageCount, BitSet activeStageFlags) {
@ -344,7 +391,7 @@ public class ConfigurationTest extends BaseTestCase {
// Motor mount
InnerTube inner = new InnerTube();
inner.setName("Sustainer MMT");
inner.setPositionValue(0.5);
inner.setRelativePosition(Position.BOTTOM);
inner.setOuterRadius(1.9 / 2);
@ -402,11 +449,20 @@ public class ConfigurationTest extends BaseTestCase {
// Stage construction
rocket.addChild(stage);
rocket.setPerfectFinish(false);
rocket.enableEvents();
final int expectedStageCount = 1;
FlightConfiguration config = rocket.getDefaultConfiguration();
assertThat(" configuration updates stage Count correctly: ", config.getActiveStageCount(), equalTo(expectedStageCount));
assertThat(" configuration list contains : ", rocket.getConfigurationSet().size(), equalTo(1));
assertThat(" rocket has incorrect stage count: ", rocket.getStageCount(), equalTo(expectedStageCount));
int expectedConfigurationCount = 0;
assertThat(" configuration list contains : ", rocket.getConfigSet().size(), equalTo(expectedConfigurationCount));
FlightConfiguration newConfig = new FlightConfiguration(rocket,null);
rocket.setFlightConfiguration( newConfig.getId(), newConfig);
rocket.setDefaultConfiguration( newConfig.getId());
assertThat(" configuration updates stage Count correctly: ", newConfig.getActiveStageCount(), equalTo(expectedStageCount));
expectedConfigurationCount = 1;
assertThat(" configuration list contains : ", rocket.getConfigSet().size(), equalTo(expectedConfigurationCount));
//FlightConfigurationID fcid = config.getFlightConfigurationID();
// Motor m = Application.getMotorSetDatabase().findMotors(null, null, "L540", Double.NaN, Double.NaN).get(0);
@ -465,10 +521,66 @@ public class ConfigurationTest extends BaseTestCase {
finset.setBaseRotation(Math.PI / 2);
boosterTube.addChild(finset);
// Motor mount
InnerTube inner = new InnerTube();
inner.setName("Booster MMT");
inner.setPositionValue(0.5);
inner.setRelativePosition(Position.BOTTOM);
inner.setOuterRadius(1.9 / 2);
inner.setInnerRadius(1.8 / 2);
inner.setLength(7.5);
boosterTube.addChild(inner);
rocket.addChild(stage);
return rocket;
// already set in "makeSingleStageTestRocket()" above...
// rocket.enableEvents();
// FlightConfiguration newConfig = new FlightConfiguration(rocket,null);
// rocket.setFlightConfiguration( newConfig.getId(), newConfig);
return rocket;
}
public static Rocket makeTwoStageMotorRocket() {
Rocket rocket = makeTwoStageTestRocket();
FlightConfigurationID fcid = rocket.getDefaultConfiguration().getId();
{
// public ThrustCurveMotor(Manufacturer manufacturer, String designation, String description,
// Motor.Type type, double[] delays, double diameter, double length,
// double[] time, double[] thrust,
// Coordinate[] cg, String digest);
ThrustCurveMotor sustainerMotor = new ThrustCurveMotor(
Manufacturer.getManufacturer("AeroTech"),"D10", "Desc",
Motor.Type.SINGLE, new double[] {3,5,7},0.018, 0.07,
new double[] { 0, 1, 2 }, new double[] { 0, 25, 0 },
new Coordinate[] {
new Coordinate(.035, 0, 0, 0.026),new Coordinate(.035, 0, 0, .021),new Coordinate(.035, 0, 0, 0.016)},
"digest D10 test");
InnerTube sustainerMount = (InnerTube) rocket.getChild(0).getChild(1).getChild(3);
sustainerMount.setMotorMount(true);
sustainerMount.setMotorInstance(fcid, sustainerMotor.getNewInstance());
}
{
// public ThrustCurveMotor(Manufacturer manufacturer, String designation, String description,
// Motor.Type type, double[] delays, double diameter, double length,
// double[] time, double[] thrust,
// Coordinate[] cg, String digest);
ThrustCurveMotor boosterMotor = new ThrustCurveMotor(
Manufacturer.getManufacturer("AeroTech"),"D21", "Desc",
Motor.Type.SINGLE, new double[] {}, 0.018, 0.07,
new double[] { 0, 1, 2 }, new double[] { 0, 32, 0 },
new Coordinate[] {
new Coordinate(.035, 0, 0, 0.025),new Coordinate(.035, 0, 0, .020),new Coordinate(.035, 0, 0, 0.0154)},
"digest D21 test");
InnerTube boosterMount = (InnerTube) rocket.getChild(1).getChild(0).getChild(2);
boosterMount.setMotorMount(true);
boosterMount.setMotorInstance(fcid, boosterMotor.getNewInstance());
boosterMount.setClusterConfiguration( ClusterConfiguration.CONFIGURATIONS[1]); // double-mount
}
return rocket;
}
}

View File

@ -177,7 +177,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
label.setHorizontalAlignment(JLabel.RIGHT);
panel.add(label, "growx, right");
ParameterSetModel<FlightConfiguration> psm = new ParameterSetModel<FlightConfiguration>( configuration.getRocket().getConfigurationSet());
ParameterSetModel<FlightConfiguration> psm = new ParameterSetModel<FlightConfiguration>( configuration.getRocket().getConfigSet());
JComboBox<FlightConfiguration> combo = new JComboBox<FlightConfiguration>(psm);
panel.add(combo, "wrap");

View File

@ -40,7 +40,7 @@ public class RenameConfigDialog extends JDialog {
public void actionPerformed(ActionEvent e) {
String newName = textbox.getText();
rocket.getFlightConfiguration(fcid).setName( newName);
System.err.println(rocket.getConfigurationSet().toDebug());
System.err.println(rocket.getConfigSet().toDebug());
RenameConfigDialog.this.setVisible(false);
}
});

View File

@ -960,7 +960,7 @@ public class GeneralOptimizationDialog extends JDialog {
simulations.add(new Named<Simulation>(s, name));
}
for (FlightConfiguration config : rocket.getConfigurationSet()) {
for (FlightConfiguration config : rocket.getConfigSet()) {
FlightConfigurationID fcid = config.getFlightConfigurationID();
if ( fcid == null) {
throw new NullPointerException(" flightconfiguration has a null id... bug.");

View File

@ -221,6 +221,7 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel<MotorMount>
MotorInstance curInstance = mtr.getNewInstance();
//System.err.println(" >> new instance: "+curInstance.toString());
curInstance.setEjectionDelay(d);
curInstance.setIgnitionEvent( IgnitionEvent.AUTOMATIC);
curMount.setMotorInstance( fcid, curInstance);
// DEBUG

View File

@ -225,7 +225,7 @@ public class DesignReport {
List<Simulation> simulations = rocketDocument.getSimulations();
int motorNumber = 0;
for( FlightConfiguration curConfig : rocket.getConfigurationSet()){
for( FlightConfiguration curConfig : rocket.getConfigSet()){
FlightConfigurationID fcid = curConfig.getFlightConfigurationID();
PdfPTable parent = new PdfPTable(2);

View File

@ -306,7 +306,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
label.setHorizontalAlignment(JLabel.RIGHT);
add(label, "growx, right");
ParameterSetModel<FlightConfiguration> psm = new ParameterSetModel<FlightConfiguration>( configuration.getRocket().getConfigurationSet());
ParameterSetModel<FlightConfiguration> psm = new ParameterSetModel<FlightConfiguration>( configuration.getRocket().getConfigSet());
JComboBox<FlightConfiguration> flightConfigurationComboBox = new JComboBox<FlightConfiguration>(psm);
add(flightConfigurationComboBox, "wrap, width 16%, wmin 100");
@ -318,7 +318,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
@SuppressWarnings("unchecked")
JComboBox<FlightConfigurationID> box = (JComboBox<FlightConfigurationID>) source;
FlightConfiguration newConfig = (FlightConfiguration)box.getSelectedItem();
document.getRocket().getConfigurationSet().setDefault( newConfig);
document.getRocket().getConfigSet().setDefault( newConfig);
updateExtras();
updateFigures();
}

View File

@ -149,7 +149,7 @@ public class SimulationEditDialog extends JDialog {
label.setToolTipText(trans.get("simedtdlg.lbl.ttip.Flightcfg"));
panel.add(label, "growx 0, gapright para");
ParameterSetModel<FlightConfiguration> psm = new ParameterSetModel<FlightConfiguration>( document.getRocket().getConfigurationSet());
ParameterSetModel<FlightConfiguration> psm = new ParameterSetModel<FlightConfiguration>( document.getRocket().getConfigSet());
final JComboBox<FlightConfiguration> configCombo = new JComboBox<FlightConfiguration>(psm);
FlightConfiguration config = document.getRocket().getFlightConfiguration(simulation[0].getId());
configCombo.setSelectedItem( config );