[Bugfix] Refactored Configurations:

-Configuration refactor
  -stages are toggled individually
  -removed set-up-to-stage method
-MotorInstance refactor: multiple Lists -> one map<MotorId,MotorInstance>
    moved most of the fields to MotorInstance.java
This commit is contained in:
Daniel_M_Williams 2015-09-16 15:12:28 -04:00
parent 85dff39fb9
commit 6c11fb2751
32 changed files with 847 additions and 764 deletions

View File

@ -74,11 +74,16 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
new LinkedHashMap<RocketComponent, AerodynamicForces>();
// Add all components to the map
for (RocketComponent c : configuration) {
f = new AerodynamicForces();
f.setComponent(c);
for (RocketComponent component : configuration.getActiveComponents()) {
map.put(c, f);
// Skip non-aerodynamic components
if (!component.isAerodynamic())
continue;
f = new AerodynamicForces();
f.setComponent(component);
map.put(component, f);
}
@ -172,7 +177,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
if (calcMap == null)
buildCalcMap(configuration);
for (RocketComponent component : configuration) {
for (RocketComponent component : configuration.getActiveComponents()) {
// Skip non-aerodynamic components
if (!component.isAerodynamic())
@ -367,7 +372,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
double[] roughnessLimited = new double[Finish.values().length];
Arrays.fill(roughnessLimited, Double.NaN);
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
// Consider only SymmetricComponents and FinSets:
if (!(c instanceof SymmetricComponent) &&
@ -469,7 +474,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
base = calculateBaseCD(conditions.getMach());
total = 0;
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (!c.isAerodynamic())
continue;
@ -517,7 +522,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
base = calculateBaseCD(conditions.getMach());
total = 0;
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (!(c instanceof SymmetricComponent))
continue;
@ -646,7 +651,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
cacheLength = 0;
cacheDiameter = 0;
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (c instanceof SymmetricComponent) {
SymmetricComponent s = (SymmetricComponent) c;
area += s.getComponentPlanformArea();
@ -665,7 +670,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
// Fins
// TODO: LOW: This could be optimized a lot...
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (c instanceof FinSet) {
FinSet f = (FinSet) c;
mul += 0.6 * Math.min(f.getFinCount(), 4) * f.getFinArea() *

View File

@ -2,7 +2,6 @@ package net.sf.openrocket.document;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
@ -10,14 +9,9 @@ import net.sf.openrocket.aerodynamics.BarrowmanCalculator;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.formatting.RocketDescriptor;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.IgnitionConfiguration;
import net.sf.openrocket.rocketcomponent.MotorConfiguration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.BasicEventSimulationEngine;
import net.sf.openrocket.simulation.DefaultSimulationOptionFactory;
import net.sf.openrocket.simulation.FlightData;
@ -266,28 +260,14 @@ public class Simulation implements ChangeSource, Cloneable {
}
}
Configuration c = new Configuration(this.getRocket());
MotorInstanceConfiguration motors = new MotorInstanceConfiguration(c);
c.setFlightConfigurationID(options.getMotorConfigurationID());
//Make sure this simulation has motors.
Configuration c = new Configuration(this.getRocket());
MotorInstanceConfiguration motors = new MotorInstanceConfiguration();
c.setFlightConfigurationID(options.getMotorConfigurationID());
final String flightConfigId = c.getFlightConfigurationID();
Iterator<MotorMount> iterator = c.motorIterator();
boolean no_motors = true;
while (iterator.hasNext()) {
MotorMount mount = iterator.next();
RocketComponent component = (RocketComponent) mount;
MotorConfiguration motorConfig = mount.getMotorConfiguration().get(flightConfigId);
IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(flightConfigId);
Motor motor = motorConfig.getMotor();
if (motor != null)
no_motors = false;
}
if (no_motors)
if (0 == motors.getMotorCount()) {
status = Status.CANT_RUN;
}
return status;
}

View File

@ -4,16 +4,13 @@ import static net.sf.openrocket.util.MathUtil.pow2;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorId;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.MassData;
import net.sf.openrocket.util.Coordinate;
@ -80,35 +77,21 @@ public class MassCalculator implements Monitorable {
checkCache(configuration);
calculateStageCache(configuration);
Coordinate totalCG = null;
Coordinate dryCG = null;
// Stage contribution
for (int stage : configuration.getActiveStages()) {
totalCG = cgCache[stage].average(totalCG);
for (AxialStage stage : configuration.getActiveStages()) {
int stageNumber = stage.getStageNumber();
dryCG = cgCache[stageNumber].average(dryCG);
}
if (totalCG == null)
totalCG = Coordinate.NUL;
if (dryCG == null)
dryCG = Coordinate.NUL;
// Add motor CGs
String motorId = configuration.getFlightConfigurationID();
if (type != MassCalcType.NO_MOTORS && motorId != null) {
Iterator<MotorMount> iterator = configuration.motorIterator();
while (iterator.hasNext()) {
MotorMount mount = iterator.next();
RocketComponent comp = (RocketComponent) mount;
Motor motor = mount.getMotorConfiguration().get(motorId).getMotor();
if (motor == null)
continue;
Coordinate motorCG = type.getCG(motor).add(mount.getMotorPosition(motorId));
Coordinate[] cgs = comp.toAbsolute(motorCG);
for (Coordinate cg : cgs) {
totalCG = totalCG.average(cg);
}
}
}
MotorInstanceConfiguration motorConfig = new MotorInstanceConfiguration(configuration);
Coordinate motorCG = getMotorCG(configuration, motorConfig, MassCalcType.LAUNCH_MASS);
Coordinate totalCG = dryCG.average(motorCG);
return totalCG;
}
@ -123,23 +106,27 @@ public class MassCalculator implements Monitorable {
checkCache(configuration);
calculateStageCache(configuration);
Coordinate totalCG = getCG(configuration, MassCalcType.NO_MOTORS);
Coordinate dryCG = getCG(configuration, MassCalcType.NO_MOTORS);
Coordinate motorCG = getMotorCG(configuration, motors, MassCalcType.LAUNCH_MASS);
Coordinate totalCG = dryCG.average(motorCG);
return totalCG;
}
public Coordinate getMotorCG(Configuration config, MotorInstanceConfiguration motors, MassCalcType type) {
Coordinate motorCG = Coordinate.ZERO;
// Add motor CGs
if (motors != null) {
for (MotorId id : motors.getMotorIDs()) {
int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber();
if (configuration.isStageActive(stage)) {
MotorInstance motor = motors.getMotorInstance(id);
Coordinate position = motors.getMotorPosition(id);
Coordinate cg = motor.getCG().add(position);
totalCG = totalCG.average(cg);
for (MotorInstance inst : config.getActiveMotors(motors)) {
int stage = ((RocketComponent) inst.getMount()).getStageNumber();
if (config.isStageActive(stage)) {
Coordinate position = inst.getPosition();
Coordinate curCG = type.getCG(inst.getMotor()).add(position);
motorCG = motorCG.average(curCG);
}
}
}
return totalCG;
return motorCG;
}
/**
@ -158,21 +145,21 @@ public class MassCalculator implements Monitorable {
double totalInertia = 0;
// Stages
for (int stage : configuration.getActiveStages()) {
Coordinate stageCG = cgCache[stage];
for (AxialStage stage : configuration.getActiveStages()) {
int stageNumber = stage.getStageNumber();
Coordinate stageCG = cgCache[stageNumber];
totalInertia += (longitudinalInertiaCache[stage] +
totalInertia += (longitudinalInertiaCache[stageNumber] +
stageCG.weight * MathUtil.pow2(stageCG.x - totalCG.x));
}
// Motors
if (motors != null) {
for (MotorId id : motors.getMotorIDs()) {
int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber();
for (MotorInstance motor : configuration.getActiveMotors(motors)) {
int stage = ((RocketComponent) motor.getMount()).getStageNumber();
if (configuration.isStageActive(stage)) {
MotorInstance motor = motors.getMotorInstance(id);
Coordinate position = motors.getMotorPosition(id);
Coordinate position = motor.getPosition();
Coordinate cg = motor.getCG().add(position);
double inertia = motor.getLongitudinalInertia();
@ -200,10 +187,11 @@ public class MassCalculator implements Monitorable {
double totalInertia = 0;
// Stages
for (int stage : configuration.getActiveStages()) {
Coordinate stageCG = cgCache[stage];
for (AxialStage stage : configuration.getActiveStages()) {
int stageNumber = stage.getStageNumber();
Coordinate stageCG = cgCache[stageNumber];
totalInertia += (rotationalInertiaCache[stage] +
totalInertia += (rotationalInertiaCache[stageNumber] +
stageCG.weight * (MathUtil.pow2(stageCG.y - totalCG.y) +
MathUtil.pow2(stageCG.z - totalCG.z)));
}
@ -211,11 +199,10 @@ public class MassCalculator implements Monitorable {
// Motors
if (motors != null) {
for (MotorId id : motors.getMotorIDs()) {
int stage = ((RocketComponent) motors.getMotorMount(id)).getStageNumber();
for (MotorInstance motor : configuration.getActiveMotors(motors)) {
int stage = ((RocketComponent) motor.getMount()).getStageNumber();
if (configuration.isStageActive(stage)) {
MotorInstance motor = motors.getMotorInstance(id);
Coordinate position = motors.getMotorPosition(id);
Coordinate position = motor.getPosition();
Coordinate cg = motor.getCG().add(position);
double inertia = motor.getRotationalInertia();
@ -241,9 +228,8 @@ public class MassCalculator implements Monitorable {
// add up the masses of all motors in the rocket
if (motors != null) {
for (MotorId id : motors.getMotorIDs()) {
MotorInstance motor = motors.getMotorInstance(id);
mass = mass + motor.getCG().weight - motor.getParentMotor().getEmptyCG().weight;
for (MotorInstance motor : configuration.getActiveMotors(motors)) {
mass = mass + motor.getCG().weight - motor.getMotor().getEmptyCG().weight;
}
}
return mass;
@ -266,13 +252,13 @@ public class MassCalculator implements Monitorable {
Map<RocketComponent, Coordinate> map = new HashMap<RocketComponent, Coordinate>();
for (RocketComponent c : configuration) {
Coordinate[] cgs = c.toAbsolute(c.getCG());
for (RocketComponent comp : configuration.getActiveComponents()) {
Coordinate[] cgs = comp.toAbsolute(comp.getCG());
Coordinate totalCG = Coordinate.NUL;
for (Coordinate cg : cgs) {
totalCG = totalCG.average(cg);
}
map.put(c, totalCG);
map.put(comp, totalCG);
}
map.put(configuration.getRocket(), getCG(configuration, type));
@ -284,7 +270,8 @@ public class MassCalculator implements Monitorable {
private void calculateStageCache(Configuration config) {
if (cgCache == null) {
ArrayList<AxialStage> stageList = config.getRocket().getStageList();
ArrayList<AxialStage> stageList = new ArrayList<AxialStage>();
stageList.addAll(config.getRocket().getStageList());
int stageCount = stageList.size();
cgCache = new Coordinate[stageCount];

View File

@ -12,6 +12,14 @@ public final class MotorId {
private final String componentId;
private final int number;
private final String COMPONENT_ERROR_ID = "Error Motor Instance";
private final int ERROR_NUMBER = -1;
public final static MotorId ERROR_ID = new MotorId();
public MotorId() {
this.componentId = COMPONENT_ERROR_ID;
this.number = ERROR_NUMBER;
}
/**
* Sole constructor.
@ -20,7 +28,6 @@ public final class MotorId {
* @param number a positive motor doun5 number
*/
public MotorId(String componentId, int number) {
super();
if (componentId == null) {
throw new IllegalArgumentException("Component ID was null");

View File

@ -1,10 +1,123 @@
package net.sf.openrocket.motor;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.rocketcomponent.IgnitionConfiguration;
import net.sf.openrocket.rocketcomponent.IgnitionConfiguration.IgnitionEvent;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Monitorable;
public interface MotorInstance extends Cloneable, Monitorable {
public abstract class MotorInstance implements Cloneable, Monitorable {
protected MotorId id = null;
protected Motor parentMotor = null;
protected MotorMount mount = null;
protected IgnitionConfiguration.IgnitionEvent ignitionEvent = null;
protected double ejectionDelay = 0.0;
protected double ignitionDelay = 0.0;
protected Coordinate position = null;
protected double ignitionTime = 0.0;
protected int modID = 0;
public MotorInstance() {
modID++;
}
/**
* 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 MotorInstance(MotorId _id, Motor _motor, MotorMount _mount, double _ejectionDelay,
IgnitionEvent _ignitionEvent, double _ignitionDelay, Coordinate _position) {
this.id = _id;
this.parentMotor = _motor;
this.mount = _mount;
this.ejectionDelay = _ejectionDelay;
this.ignitionEvent = _ignitionEvent;
this.ignitionDelay = _ignitionDelay;
this.position = _position;
this.ignitionTime = Double.POSITIVE_INFINITY;
modID++;
}
public MotorId getID() {
return this.id;
}
public void setID(final MotorId _id) {
this.id = _id;
}
public double getEjectionDelay() {
return this.ejectionDelay;
}
public void setEjectionDelay(final double newDelay) {
this.ejectionDelay = newDelay;
}
@Override
public int getModID() {
return this.modID;
}
public Motor getMotor() {
return this.parentMotor;
}
public MotorMount getMount() {
return this.mount;
}
public void setMount(final MotorMount _mount) {
this.mount = _mount;
}
public Coordinate getPosition() {
return this.position;
}
public void setPosition(Coordinate _position) {
this.position = _position;
modID++;
}
public double getIgnitionTime() {
return this.ignitionTime;
}
public void setIgnitionTime(double _time) {
this.ignitionTime = _time;
modID++;
}
public double getIgnitionDelay() {
return ignitionDelay;
}
public void setIgnitionDelay(final double _delay) {
this.ignitionDelay = _delay;
}
public IgnitionEvent getIgnitionEvent() {
return ignitionEvent;
}
public void setIgnitionEvent(final IgnitionEvent _event) {
this.ignitionEvent = _event;
}
/**
* Step the motor instance forward in time.
@ -13,51 +126,49 @@ public interface MotorInstance extends Cloneable, Monitorable {
* @param acceleration the average acceleration during the step.
* @param cond the average atmospheric conditions during the step.
*/
public void step(double time, double acceleration, AtmosphericConditions cond);
public abstract void step(double time, double acceleration, AtmosphericConditions cond);
/**
* Return the time to which this motor has been stepped.
* @return the current step time.
*/
public double getTime();
public abstract double getTime();
/**
* Return the average thrust during the last step.
*/
public double getThrust();
public abstract double getThrust();
/**
* Return the average CG location during the last step.
*/
public Coordinate getCG();
public abstract Coordinate getCG();
/**
* Return the average longitudinal moment of inertia during the last step.
* This is the actual inertia, not the unit inertia!
*/
public double getLongitudinalInertia();
public abstract double getLongitudinalInertia();
/**
* Return the average rotational moment of inertia during the last step.
* This is the actual inertia, not the unit inertia!
*/
public double getRotationalInertia();
public abstract double getRotationalInertia();
/**
* Return whether this motor still produces thrust. If this method returns false
* the motor has burnt out, and will not produce any significant thrust anymore.
*/
public boolean isActive();
public abstract boolean isActive();
/**
* Create a new instance of this motor instance. The state of the motor is
* identical to this instance and can be used independently from this one.
*/
public MotorInstance clone();
public Motor getParentMotor();
@Override
public abstract MotorInstance clone();
}

View File

@ -1,13 +1,16 @@
package net.sf.openrocket.motor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.IgnitionConfiguration;
import net.sf.openrocket.rocketcomponent.IgnitionConfiguration.IgnitionEvent;
import net.sf.openrocket.rocketcomponent.MotorConfiguration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.Monitorable;
@ -17,21 +20,55 @@ import net.sf.openrocket.util.Monitorable;
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public final class MotorInstanceConfiguration implements Monitorable, Cloneable {
private final List<MotorId> ids = new ArrayList<MotorId>();
private final List<MotorId> unmodifiableIds = Collections.unmodifiableList(ids);
private final List<MotorInstance> motors = new ArrayList<MotorInstance>();
private final List<Double> ejectionDelays = new ArrayList<Double>();
private final List<MotorMount> mounts = new ArrayList<MotorMount>();
private final List<IgnitionConfiguration.IgnitionEvent> ignitionEvents = new ArrayList<IgnitionConfiguration.IgnitionEvent>();
private final List<Double> ignitionDelays = new ArrayList<Double>();
private final List<Coordinate> positions = new ArrayList<Coordinate>();
private final List<Double> ignitionTimes = new ArrayList<Double>();
public class MotorInstanceConfiguration implements Cloneable, Iterable<MotorInstance>, Monitorable {
protected final HashMap<MotorId, MotorInstance> motors = new HashMap<MotorId, MotorInstance>();
private int modID = 0;
private MotorInstanceConfiguration() {
}
/**
* Create a new motor instance configuration for the rocket configuration.
*
* @param configuration the rocket configuration.
*/
public MotorInstanceConfiguration(Configuration configuration) {
// motors == this
final String flightConfigId = configuration.getFlightConfigurationID();
Iterator<RocketComponent> iterator = configuration.getRocket().iterator(false);
while (iterator.hasNext()) {
RocketComponent component = iterator.next();
if (component instanceof MotorMount) {
MotorMount mount = (MotorMount) component;
MotorConfiguration motorConfig = mount.getMotorConfiguration().get(flightConfigId);
IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(flightConfigId);
Motor motor = motorConfig.getMotor();
if (motor != null) {
int count = mount.getMotorConfiguration().size();
Coordinate[] positions = component.toAbsolute(mount.getMotorPosition(flightConfigId));
for (int i = 0; i < positions.length; i++) {
Coordinate position = positions[i];
MotorId id = new MotorId(component.getID(), i + 1);
MotorInstance inst = motor.getInstance();
inst.setID(id);
inst.setEjectionDelay(motorConfig.getEjectionDelay());
inst.setMount(mount);
inst.setIgnitionDelay(ignitionConfig.getIgnitionDelay());
inst.setIgnitionEvent(ignitionConfig.getIgnitionEvent());
inst.setPosition(position);
motors.put(id, inst);
}
}
}
}
}
/**
* Add a motor instance to this configuration. The motor is placed at
@ -45,89 +82,70 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable
* @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, MotorInstance motor, double ejectionDelay, MotorMount mount,
IgnitionEvent ignitionEvent, double ignitionDelay, Coordinate position) {
if (this.ids.contains(id)) {
// 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) {
MotorId id = motor.getID();
if (this.motors.containsKey(id)) {
throw new IllegalArgumentException("MotorInstanceConfiguration already " +
"contains a motor with id " + id);
}
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);
this.motors.put(id, motor);
modID++;
}
/**
* Return a list of all motor IDs in this configuration (not only ones in active stages).
*/
public List<MotorId> getMotorIDs() {
return unmodifiableIds;
public Collection<MotorInstance> getAllMotors() {
return motors.values();
}
public int getMotorCount() {
return motors.size();
}
public Set<MotorId> getMotorIDs() {
return this.motors.keySet();
}
public MotorInstance getMotorInstance(MotorId id) {
return motors.get(indexOf(id));
return motors.get(id);
}
public double getEjectionDelay(MotorId id) {
return ejectionDelays.get(indexOf(id));
public boolean hasMotors() {
return (0 < motors.size());
}
public MotorMount getMotorMount(MotorId id) {
return mounts.get(indexOf(id));
}
public Coordinate getMotorPosition(MotorId id) {
return positions.get(indexOf(id));
}
public void setMotorPosition(MotorId id, Coordinate position) {
positions.set(indexOf(id), position);
modID++;
}
public double getMotorIgnitionTime(MotorId id) {
return ignitionTimes.get(indexOf(id));
}
public void setMotorIgnitionTime(MotorId id, double time) {
this.ignitionTimes.set(indexOf(id), time);
modID++;
}
public double getMotorIgnitionDelay(MotorId id) {
return ignitionDelays.get(indexOf(id));
}
public IgnitionEvent getMotorIgnitionEvent(MotorId id) {
return ignitionEvents.get(indexOf(id));
}
private int indexOf(MotorId id) {
int index = ids.indexOf(id);
if (index < 0) {
throw new IllegalArgumentException("MotorInstanceConfiguration does not " +
"contain a motor with id " + id);
}
return index;
}
/**
* 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 (int i = 0; i < motors.size(); i++) {
double t = time - ignitionTimes.get(i);
for (MotorInstance inst : motors.values()) {
double t = time - inst.getIgnitionTime();
if (t >= 0) {
motors.get(i).step(t, acceleration, cond);
inst.step(t, acceleration, cond);
}
}
modID++;
@ -136,7 +154,7 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable
@Override
public int getModID() {
int id = modID;
for (MotorInstance motor : motors) {
for (MotorInstance motor : motors.values()) {
id += motor.getModID();
}
return id;
@ -149,18 +167,17 @@ public final class MotorInstanceConfiguration implements Monitorable, Cloneable
@Override
public MotorInstanceConfiguration clone() {
MotorInstanceConfiguration clone = new MotorInstanceConfiguration();
clone.ids.addAll(this.ids);
clone.mounts.addAll(this.mounts);
clone.positions.addAll(this.positions);
clone.ejectionDelays.addAll(this.ejectionDelays);
clone.ignitionTimes.addAll(this.ignitionTimes);
clone.ignitionEvents.addAll(this.ignitionEvents);
clone.ignitionDelays.addAll(this.ignitionDelays);
for (MotorInstance motor : this.motors) {
clone.motors.add(motor.clone());
for (MotorInstance motor : this.motors.values()) {
clone.motors.put(motor.id, motor.clone());
}
clone.modID = this.modID;
return clone;
}
@Override
public Iterator<MotorInstance> iterator() {
return this.motors.values().iterator();
}
}

View File

@ -415,9 +415,9 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
//////// Motor instance implementation ////////
private class ThrustCurveMotorInstance implements MotorInstance {
private class ThrustCurveMotorInstance extends MotorInstance {
private int position;
private int timeIndex;
// Previous time step value
private double prevTime;
@ -434,13 +434,12 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
private final double unitRotationalInertia;
private final double unitLongitudinalInertia;
private final Motor parentMotor;
private int modID = 0;
public ThrustCurveMotorInstance() {
log.debug("ThrustCurveMotor: Creating motor instance of " + ThrustCurveMotor.this);
position = 0;
timeIndex = 0;
prevTime = 0;
instThrust = 0;
stepThrust = 0;
@ -451,11 +450,6 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
parentMotor = ThrustCurveMotor.this;
}
@Override
public Motor getParentMotor() {
return parentMotor;
}
@Override
public double getTime() {
return prevTime;
@ -500,7 +494,7 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
modID++;
if (position >= time.length - 1) {
if (timeIndex >= time.length - 1) {
// Thrust has ended
prevTime = nextTime;
stepThrust = 0;
@ -511,33 +505,33 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
// Compute average & instantaneous thrust
if (nextTime < time[position + 1]) {
if (nextTime < time[timeIndex + 1]) {
// Time step between time points
double nextF = MathUtil.map(nextTime, time[position], time[position + 1],
thrust[position], thrust[position + 1]);
double nextF = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1],
thrust[timeIndex], thrust[timeIndex + 1]);
stepThrust = (instThrust + nextF) / 2;
instThrust = nextF;
} else {
// Portion of previous step
stepThrust = (instThrust + thrust[position + 1]) / 2 * (time[position + 1] - prevTime);
stepThrust = (instThrust + thrust[timeIndex + 1]) / 2 * (time[timeIndex + 1] - prevTime);
// Whole steps
position++;
while ((position < time.length - 1) && (nextTime >= time[position + 1])) {
stepThrust += (thrust[position] + thrust[position + 1]) / 2 *
(time[position + 1] - time[position]);
position++;
timeIndex++;
while ((timeIndex < time.length - 1) && (nextTime >= time[timeIndex + 1])) {
stepThrust += (thrust[timeIndex] + thrust[timeIndex + 1]) / 2 *
(time[timeIndex + 1] - time[timeIndex]);
timeIndex++;
}
// End step
if (position < time.length - 1) {
instThrust = MathUtil.map(nextTime, time[position], time[position + 1],
thrust[position], thrust[position + 1]);
stepThrust += (thrust[position] + instThrust) / 2 *
(nextTime - time[position]);
if (timeIndex < time.length - 1) {
instThrust = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1],
thrust[timeIndex], thrust[timeIndex + 1]);
stepThrust += (thrust[timeIndex] + instThrust) / 2 *
(nextTime - time[timeIndex]);
} else {
// Thrust ended during this step
instThrust = 0;
@ -549,9 +543,9 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
// Compute average and instantaneous CG (simple average between points)
Coordinate nextCG;
if (position < time.length - 1) {
nextCG = MathUtil.map(nextTime, time[position], time[position + 1],
cg[position], cg[position + 1]);
if (timeIndex < time.length - 1) {
nextCG = MathUtil.map(nextTime, time[timeIndex], time[timeIndex + 1],
cg[timeIndex], cg[timeIndex + 1]);
} else {
nextCG = cg[cg.length - 1];
}
@ -564,11 +558,12 @@ public class ThrustCurveMotor implements Motor, Comparable<ThrustCurveMotor>, Se
@Override
public MotorInstance clone() {
try {
return (MotorInstance) super.clone();
} catch (CloneNotSupportedException e) {
throw new BugException("CloneNotSupportedException", e);
}
throw new BugException("CloneNotSupportedException");
//try {
// return (MotorInstance) super.clone();
//} catch (CloneNotSupportedException e) {
// throw new BugException("CloneNotSupportedException", e);
//}
}
@Override

View File

@ -91,7 +91,7 @@ public class StabilityDomain implements SimulationDomain {
absolute = cpx - cgx;
double diameter = 0;
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (c instanceof SymmetricComponent) {
double d1 = ((SymmetricComponent) c).getForeRadius() * 2;
double d2 = ((SymmetricComponent) c).getAftRadius() * 2;

View File

@ -84,7 +84,7 @@ public class StabilityParameter implements OptimizableParameter {
if (!absolute) {
double diameter = 0;
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (c instanceof SymmetricComponent) {
double d1 = ((SymmetricComponent) c).getForeRadius() * 2;
double d2 = ((SymmetricComponent) c).getAftRadius() * 2;

View File

@ -15,13 +15,11 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
private FlightConfigurationImpl<StageSeparationConfiguration> separationConfigurations;
protected int stageNumber;
private static int stageCount;
public AxialStage() {
this.separationConfigurations = new FlightConfigurationImpl<StageSeparationConfiguration>(this, ComponentChangeEvent.EVENT_CHANGE, new StageSeparationConfiguration());
this.relativePosition = Position.AFTER;
stageNumber = AxialStage.stageCount;
AxialStage.stageCount++;
this.stageNumber = 0;
}
@Override
@ -35,10 +33,6 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
return trans.get("Stage.Stage");
}
public static int getStageCount() {
return AxialStage.stageCount;
}
public FlightConfiguration<StageSeparationConfiguration> getStageSeparationConfiguration() {
return separationConfigurations;
}
@ -107,23 +101,21 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC
if (null == this.parent) {
return -1;
} else if (this.isCenterline()) {
if (0 < this.stageNumber) {
return --this.stageNumber;
} else {
return this.parent.getStageNumber();
}
}
return -1;
}
public static void resetStageCount() {
AxialStage.stageCount = 0;
}
@Override
public int getStageNumber() {
return this.stageNumber;
}
public void setStageNumber(final int newStageNumber) {
this.stageNumber = newStageNumber;
}
@Override
public Coordinate[] shiftCoordinates(Coordinate[] c) {
checkState();

View File

@ -130,32 +130,6 @@ public class BoosterSet extends AxialStage implements FlightConfigurableComponen
return this.getAxialOffset();
}
/**
* Stages may be positioned relative to other stages. In that case, this will set the stage number
* against which this stage is positioned.
*
* @return the stage number which this stage is positioned relative to
*/
@Override
public int getRelativeToStage() {
if (null == this.parent) {
return -1;
} else if (this.parent instanceof BoosterSet) {
return this.parent.parent.getChildPosition(this.parent);
} else if (this.isCenterline()) {
if (0 < this.stageNumber) {
return --this.stageNumber;
}
}
return -1;
}
@Override
public int getStageNumber() {
return this.stageNumber;
}
@Override
public Coordinate[] shiftCoordinates(Coordinate[] c) {
checkState();

View File

@ -1,13 +1,13 @@
package net.sf.openrocket.rocketcomponent;
import java.util.BitSet;
import java.util.Collection;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;
import java.util.NoSuchElementException;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.util.ArrayList;
import net.sf.openrocket.util.BugException;
import net.sf.openrocket.util.ChangeSource;
@ -16,26 +16,38 @@ import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Monitorable;
import net.sf.openrocket.util.StateChangeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A class defining a rocket configuration, including motors and which stages are active.
* A class defining a rocket configuration, including which stages are active.
*
* TODO: HIGH: Remove motor ignition times from this class.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class Configuration implements Cloneable, ChangeSource, ComponentChangeListener,
Iterable<RocketComponent>, Monitorable {
private Rocket rocket;
private BitSet stagesActive = new BitSet();
private String flightConfigurationId = null;
public class Configuration implements Cloneable, ChangeSource, ComponentChangeListener, Monitorable {
private static final Logger log = LoggerFactory.getLogger(Configuration.class);
protected Rocket rocket;
protected String flightConfigurationId = null;
private List<EventListener> listenerList = new ArrayList<EventListener>();
protected class StageFlags {
public boolean active = true;
public int prev = -1;
public AxialStage stage = null;
public StageFlags(AxialStage _stage, int _prev, boolean _active) {
this.stage = _stage;
this.prev = _prev;
this.active = _active;
}
}
/* Cached data */
protected HashMap<Integer, StageFlags> stageMap = new HashMap<Integer, StageFlags>();
private int boundsModID = -1;
private ArrayList<Coordinate> cachedBounds = new ArrayList<Coordinate>();
private double cachedLength = -1;
@ -43,7 +55,6 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
private int refLengthModID = -1;
private double cachedRefLength = -1;
private int modID = 0;
@ -55,98 +66,156 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
*/
public Configuration(Rocket rocket) {
this.rocket = rocket;
setAllStages();
updateStageMap();
rocket.addComponentChangeListener(this);
}
public Rocket getRocket() {
return rocket;
}
public void clearAllStages() {
this.setAllStages(false);
}
public void setAllStages() {
stagesActive.clear();
stagesActive.set(0, AxialStage.getStageCount());
fireChangeEvent();
this.setAllStages(true);
}
/**
* Set all stages up to and including the given stage number. For example,
* <code>setToStage(0)</code> will set only the first stage active.
*
* @param stage the stage number.
*/
public void setToStage(int stage) {
stagesActive.clear();
stagesActive.set(0, stage + 1, true);
// stages.set(stage+1, rocket.getStageCount(), false);
fireChangeEvent();
public void setAllStages(final boolean _value) {
for (StageFlags cur : stageMap.values()) {
cur.active = _value;
}
public void setOnlyStage(int stage) {
stagesActive.clear();
stagesActive.set(stage, stage + 1, true);
fireChangeEvent();
}
/**
* Check whether the up-most stage of the rocket is in this configuration.
* This method flags a stage inactive. Other stages are unaffected.
*
* @return <code>true</code> if the first stage is active in this configuration.
* @param stageNumber stage number to inactivate
*/
public boolean isHead() {
return isStageActive(0);
public void clearOnlyStage(final int stageNumber) {
setStage(stageNumber, false);
}
/**
* This method flags a stage active. Other stages are unaffected.
*
* @param stageNumber stage number to activate
*/
public void setOnlyStage(final int stageNumber) {
setStage(stageNumber, true);
}
/**
* This method flags the specified stage as requested. Other stages are unaffected.
*
* @param stageNumber stage number to flag
* @param _active inactive (<code>false</code>) or active (<code>true</code>)
*/
public void setStage(final int stageNumber, final boolean _active) {
if ((0 <= stageNumber) && (stageMap.containsKey(stageNumber))) {
log.error("debug: setting stage " + stageNumber + " to " + _active);
stageMap.get(stageNumber).active = _active;
fireChangeEvent();
return;
}
log.error("error: attempt to retrieve via a bad stage number: " + stageNumber);
}
public void toggleStage(final int stageNumber) {
if ((0 <= stageNumber) && (stageMap.containsKey(stageNumber))) {
StageFlags flags = stageMap.get(stageNumber);
log.error("debug: toggling stage " + stageNumber + " to " + !flags.active);
flags.active = !flags.active;
fireChangeEvent();
return;
}
log.error("error: attempt to retrieve via a bad stage number: " + stageNumber);
}
/**
* Check whether the stage is active.
*/
public boolean isStageActive(final AxialStage stage) {
return this.isStageActive(stage.getStageNumber());
}
/**
* Check whether the stage specified by the index is active.
*/
public boolean isStageActive(int stage) {
if (stage >= AxialStage.getStageCount())
public boolean isStageActive(int stageNumber) {
if (stageNumber >= this.rocket.getStageCount()) {
return false;
return stagesActive.get(stage);
}
return stageMap.get(stageNumber).active;
}
public int getStageCount() {
return AxialStage.getStageCount();
public List<RocketComponent> getActiveComponents() {
ArrayList<RocketComponent> toReturn = new ArrayList<RocketComponent>();
for (StageFlags curFlags : this.stageMap.values()) {
if (curFlags.active) {
toReturn.add(curFlags.stage);
}
}
return toReturn;
}
public List<MotorInstance> getActiveMotors(final MotorInstanceConfiguration mic) {
ArrayList<MotorInstance> toReturn = new ArrayList<MotorInstance>();
for (MotorInstance inst : mic.getAllMotors()) {
MotorMount mount = inst.getMount();
if (mount instanceof RocketComponent) {
RocketComponent comp = (RocketComponent) mount;
if (this.isStageActive(comp.getStage().getStageNumber())) {
toReturn.add(inst);
}
}
}
return toReturn;
}
public List<AxialStage> getActiveStages() {
List<AxialStage> activeStages = new ArrayList<AxialStage>();
for (StageFlags flags : this.stageMap.values()) {
activeStages.add(flags.stage);
}
return activeStages;
}
public int getActiveStageCount() {
int count = 0;
int s = AxialStage.getStageCount();
for (int i = 0; i < s; i++) {
if (stagesActive.get(i))
count++;
}
return count;
}
public int[] getActiveStages() {
// temporary hack fix
//int stageCount = Stage.getStageCount();
int stageCount = getRocket().getChildCount();
List<Integer> active = new ArrayList<Integer>();
int[] ret;
for (int i = 0; i < stageCount; i++) {
if (stagesActive.get(i)) {
active.add(i);
int activeCount = 0;
for (StageFlags cur : this.stageMap.values()) {
if (cur.active) {
activeCount++;
}
}
ret = new int[active.size()];
for (int i = 0; i < ret.length; i++) {
ret[i] = active.get(i);
return activeCount;
}
return ret;
/**
* Retrieve the bottom-most active stage.
* @return
*/
public AxialStage getBottomStage() {
AxialStage bottomStage = null;
for (StageFlags curFlags : this.stageMap.values()) {
if (curFlags.active) {
bottomStage = curFlags.stage;
}
}
return bottomStage;
}
public int getStageCount() {
return stageMap.size();
}
@ -221,8 +290,38 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
((StateChangeListener) l).stateChanged(e);
}
}
updateStageMap();
}
private void updateStageMap() {
if (this.rocket.getStageCount() == this.stageMap.size()) {
// no changes needed
return;
}
this.stageMap.clear();
for (AxialStage curStage : this.rocket.getStageList()) {
int prevStageNum = curStage.getStageNumber() - 1;
if (curStage.getParent() instanceof AxialStage) {
prevStageNum = curStage.getParent().getStageNumber();
}
StageFlags flagsToAdd = new StageFlags(curStage, prevStageNum, true);
this.stageMap.put(curStage.getStageNumber(), flagsToAdd);
}
}
// DEBUG / DEVEL
public String toDebug() {
StringBuilder buf = new StringBuilder();
buf.append(String.format("\nDumping stage config: \n"));
for (StageFlags flags : this.stageMap.values()) {
AxialStage curStage = flags.stage;
buf.append(String.format(" [%d]: %24s: %b\n", curStage.getStageNumber(), curStage.getName(), flags.active));
}
buf.append("\n\n");
return buf.toString();
}
@Override
public void componentChanged(ComponentChangeEvent e) {
@ -232,26 +331,6 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
/////////////// Helper methods ///////////////
/**
* Return whether this configuration has any motors defined to it.
*
* @return true if this configuration has active motor mounts with motors defined to them.
*/
public boolean hasMotors() {
for (RocketComponent c : this) {
if (c instanceof MotorMount) {
MotorMount mount = (MotorMount) c;
if (!mount.isMotorMount())
continue;
if (mount.getMotor(this.flightConfigurationId) != null) {
return true;
}
}
}
return false;
}
/**
* Return whether a component is in the currently active stages.
*/
@ -272,7 +351,7 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
cachedBounds.clear();
double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY;
for (RocketComponent component : this) {
for (RocketComponent component : this.getActiveComponents()) {
for (Coordinate coord : component.getComponentBounds()) {
cachedBounds.add(coord);
if (coord.x < minX)
@ -308,57 +387,17 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
/**
* Return an iterator that iterates over the currently active components.
* The <code>Rocket</code> and <code>Stage</code> components are not returned,
* but instead all components that are within currently active stages.
*/
@Override
public Iterator<RocketComponent> iterator() {
List<RocketComponent> accumulator = new ArrayList<RocketComponent>();
accumulator = this.getActiveComponents(accumulator, rocket.getChildren());
return accumulator.iterator();
}
private List<RocketComponent> getActiveComponents(List<RocketComponent> accumulator, final List<RocketComponent> toScan) {
for (RocketComponent rc : toScan) {
if (rc instanceof AxialStage) {
if (!isStageActive(rc.getStageNumber())) {
continue;
}
} else {
accumulator.add(rc);
}
// recurse to children
getActiveComponents(accumulator, rc.getChildren());
}
return accumulator;
}
/**
* Return an iterator that iterates over all <code>MotorMount</code>s within the
* current configuration that have an active motor.
*
* @return an iterator over active motor mounts.
*/
public Iterator<MotorMount> motorIterator() {
return new MotorIterator();
}
/**
* 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.
*/
@SuppressWarnings("unchecked")
@Override
public Configuration clone() {
try {
Configuration config = (Configuration) super.clone();
config.listenerList = new ArrayList<EventListener>();
config.stagesActive = (BitSet) this.stagesActive.clone();
config.stageMap = (HashMap<Integer, StageFlags>) this.stageMap.clone();
config.cachedBounds = new ArrayList<Coordinate>();
config.boundsModID = -1;
config.refLengthModID = -1;
@ -375,51 +414,5 @@ public class Configuration implements Cloneable, ChangeSource, ComponentChangeLi
return modID + rocket.getModID();
}
private class MotorIterator implements Iterator<MotorMount> {
private final Iterator<RocketComponent> iterator;
private MotorMount next = null;
public MotorIterator() {
this.iterator = iterator();
}
@Override
public boolean hasNext() {
getNext();
return (next != null);
}
@Override
public MotorMount next() {
getNext();
if (next == null) {
throw new NoSuchElementException("iterator called for too long");
}
MotorMount ret = next;
next = null;
return ret;
}
@Override
public void remove() {
throw new UnsupportedOperationException("remove unsupported");
}
private void getNext() {
if (next != null)
return;
while (iterator.hasNext()) {
RocketComponent c = iterator.next();
if (c instanceof MotorMount) {
MotorMount mount = (MotorMount) c;
if (mount.isMotorMount() && mount.getMotor(flightConfigurationId) != null) {
next = mount;
return;
}
}
}
}
}
}

View File

@ -8,7 +8,7 @@ public enum ReferenceType {
NOSECONE {
@Override
public double getReferenceLength(Configuration config) {
for (RocketComponent c: config) {
for (RocketComponent c : config.getActiveComponents()) {
if (c instanceof SymmetricComponent) {
SymmetricComponent s = (SymmetricComponent) c;
if (s.getForeRadius() >= 0.0005)
@ -25,7 +25,7 @@ public enum ReferenceType {
@Override
public double getReferenceLength(Configuration config) {
double r = 0;
for (RocketComponent c: config) {
for (RocketComponent c : config.getActiveComponents()) {
if (c instanceof SymmetricComponent) {
SymmetricComponent s = (SymmetricComponent) c;
r = Math.max(r, s.getForeRadius());

View File

@ -80,7 +80,7 @@ public class Rocket extends RocketComponent {
// Does the rocket have a perfect finish (a notable amount of laminar flow)
private boolean perfectFinish = false;
private final HashMap<Integer, AxialStage> stageMap = new HashMap<Integer, AxialStage>();
///////////// Constructor /////////////
@ -93,7 +93,6 @@ public class Rocket extends RocketComponent {
functionalModID = modID;
defaultConfiguration = new Configuration(this);
AxialStage.resetStageCount();
}
@ -130,7 +129,7 @@ public class Rocket extends RocketComponent {
*/
public int getStageCount() {
checkState();
return AxialStage.getStageCount();
return this.stageMap.size();
}
/**
@ -195,29 +194,34 @@ public class Rocket extends RocketComponent {
return functionalModID;
}
public ArrayList<AxialStage> getStageList() {
ArrayList<AxialStage> toReturn = new ArrayList<AxialStage>();
toReturn = Rocket.getStages(toReturn, this);
return toReturn;
public Collection<AxialStage> getStageList() {
return this.stageMap.values();
}
private static ArrayList<AxialStage> getStages(ArrayList<AxialStage> accumulator, final RocketComponent parent) {
if ((null == accumulator) || (null == parent)) {
return new ArrayList<AxialStage>();
private int getNewStageNumber() {
int guess = 0;
while (stageMap.containsKey(guess)) {
guess++;
}
return guess;
}
for (RocketComponent curChild : parent.getChildren()) {
if (curChild instanceof AxialStage) {
AxialStage curStage = (AxialStage) curChild;
accumulator.add(curStage);
public void trackStage(final AxialStage newStage) {
int stageNumber = newStage.getStageNumber();
AxialStage value = stageMap.get(stageNumber);
if (newStage.equals(value)) {
// stage is already added. skip.
} else {
stageNumber = getNewStageNumber();
newStage.setStageNumber(stageNumber);
this.stageMap.put(stageNumber, newStage);
}
getStages(accumulator, curChild);
}
return accumulator;
}
public void forgetStage(final AxialStage oldStage) {
this.stageMap.remove(oldStage.getStageNumber());
}
public ReferenceType getReferenceType() {
checkState();

View File

@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent> {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(RocketComponent.class);
// Because of changes to Java 1.7.0-45's mechanism to construct DataFlavor objects (used in Drag and Drop)
@ -985,8 +986,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
* it should override this with a public method that simply calls this
* supermethod AND fire a suitable ComponentChangeEvent.
*
* @deprecated name is ambiguous in three-dimensional space: value may refer to any of the three dimensions. Please use 'setPositionX' instead.
*
* @deprecated name is ambiguous in three-dimensional space: value may refer to any of the three dimensions. Please use 'setAxialOffset' instead.
* @param value the position value of the component.
*/
public void setPositionValue(double value) {
@ -1346,6 +1346,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
children.add(index, component);
component.parent = this;
if (component instanceof AxialStage) {
AxialStage nStage = (AxialStage) component;
this.getRocket().trackStage(nStage);
}
this.checkComponentStructure();
component.checkComponentStructure();
@ -1363,6 +1368,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
RocketComponent component = children.remove(n);
component.parent = null;
if (component instanceof AxialStage) {
AxialStage nStage = (AxialStage) component;
this.getRocket().forgetStage(nStage);
}
this.checkComponentStructure();
component.checkComponentStructure();

View File

@ -1,12 +1,12 @@
package net.sf.openrocket.simulation;
import java.util.List;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.motor.MotorId;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.listeners.SimulationListenerHelper;
import net.sf.openrocket.util.BugException;
@ -171,19 +171,18 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
}
Configuration configuration = status.getConfiguration();
MotorInstanceConfiguration mic = status.getMotorConfiguration();
// Iterate over the motors and calculate combined thrust
MotorInstanceConfiguration mic = status.getMotorConfiguration();
if (!stepMotors) {
mic = mic.clone();
}
mic.step(status.getSimulationTime() + timestep, acceleration, atmosphericConditions);
thrust = 0;
for (MotorId id : mic.getMotorIDs()) {
if (configuration.isComponentActive((RocketComponent) mic.getMotorMount(id))) {
MotorInstance motor = mic.getMotorInstance(id);
thrust += motor.getThrust();
}
List<MotorInstance> activeMotors = configuration.getActiveMotors(mic);
for (MotorInstance currentMotorInstance : activeMotors) {
thrust += currentMotorInstance.getThrust();
}
// Post-listeners

View File

@ -1,6 +1,6 @@
package net.sf.openrocket.simulation;
import java.util.Iterator;
import java.util.List;
import net.sf.openrocket.aerodynamics.Warning;
import net.sf.openrocket.l10n.Translator;
@ -8,14 +8,13 @@ import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorId;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.DeploymentConfiguration;
import net.sf.openrocket.rocketcomponent.IgnitionConfiguration;
import net.sf.openrocket.rocketcomponent.MotorConfiguration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration;
import net.sf.openrocket.simulation.exception.MotorIgnitionException;
import net.sf.openrocket.simulation.exception.SimulationException;
@ -68,7 +67,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Set up rocket configuration
Configuration configuration = setupConfiguration(simulationConditions);
flightConfigurationId = configuration.getFlightConfigurationID();
MotorInstanceConfiguration motorConfiguration = setupMotorConfiguration(configuration);
MotorInstanceConfiguration motorConfiguration = new MotorInstanceConfiguration(configuration);
if (motorConfiguration.getMotorIDs().isEmpty()) {
throw new MotorIgnitionException(trans.get("BasicEventSimulationEngine.error.noMotorsDefined"));
}
@ -199,7 +198,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
MotorInstance motor = status.getMotorConfiguration().getMotorInstance(motorId);
if (!motor.isActive() && status.addBurntOutMotor(motorId)) {
addEvent(new FlightEvent(FlightEvent.Type.BURNOUT, status.getSimulationTime(),
(RocketComponent) status.getMotorConfiguration().getMotorMount(motorId), motorId));
(RocketComponent) status.getMotorConfiguration().getMotorInstance(motorId).getMount(), motorId));
}
}
@ -221,7 +220,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
if (wantToTumble) {
final boolean tooMuchThrust = t > THRUST_TUMBLE_CONDITION;
final boolean isSustainer = status.getConfiguration().isStageActive(0);
//final boolean isSustainer = status.getConfiguration().isStageActive(0);
final boolean isApogee = status.isApogeeReached();
if (tooMuchThrust) {
status.getWarnings().add(Warning.TUMBLE_UNDER_THRUST);
@ -261,37 +260,6 @@ public class BasicEventSimulationEngine implements SimulationEngine {
/**
* Create a new motor instance configuration for the rocket configuration.
*
* @param configuration the rocket configuration.
* @return a new motor instance configuration with all motors in place.
*/
private MotorInstanceConfiguration setupMotorConfiguration(Configuration configuration) {
MotorInstanceConfiguration motors = new MotorInstanceConfiguration();
final String flightConfigId = configuration.getFlightConfigurationID();
Iterator<MotorMount> iterator = configuration.motorIterator();
while (iterator.hasNext()) {
MotorMount mount = iterator.next();
RocketComponent component = (RocketComponent) mount;
MotorConfiguration motorConfig = mount.getMotorConfiguration().get(flightConfigId);
IgnitionConfiguration ignitionConfig = mount.getIgnitionConfiguration().get(flightConfigId);
Motor motor = motorConfig.getMotor();
if (motor != null) {
Coordinate[] positions = component.toAbsolute(mount.getMotorPosition(flightConfigId));
for (int i = 0; i < positions.length; i++) {
Coordinate position = positions[i];
MotorId id = new MotorId(component.getID(), i + 1);
motors.addMotor(id, motor.getInstance(), motorConfig.getEjectionDelay(), mount,
ignitionConfig.getIgnitionEvent(), ignitionConfig.getIgnitionDelay(), position);
}
}
}
return motors;
}
/**
* Handles events occurring during the flight from the event queue.
* Each event that has occurred before or at the current simulation time is
@ -340,12 +308,13 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Check for motor ignition events, add ignition events to queue
for (MotorId id : status.getMotorConfiguration().getMotorIDs()) {
IgnitionConfiguration.IgnitionEvent ignitionEvent = status.getMotorConfiguration().getMotorIgnitionEvent(id);
MotorMount mount = status.getMotorConfiguration().getMotorMount(id);
MotorInstance inst = status.getMotorConfiguration().getMotorInstance(id);
IgnitionConfiguration.IgnitionEvent ignitionEvent = inst.getIgnitionEvent();
MotorMount mount = inst.getMount();
RocketComponent component = (RocketComponent) mount;
if (ignitionEvent.isActivationEvent(event, component)) {
double ignitionDelay = status.getMotorConfiguration().getMotorIgnitionDelay(id);
double ignitionDelay = inst.getIgnitionDelay();
addEvent(new FlightEvent(FlightEvent.Type.IGNITION,
status.getSimulationTime() + ignitionDelay,
component, id));
@ -354,11 +323,12 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Check for stage separation event
for (int stageNo : status.getConfiguration().getActiveStages()) {
for (AxialStage stage : status.getConfiguration().getActiveStages()) {
int stageNo = stage.getStageNumber();
if (stageNo == 0)
continue;
AxialStage stage = (AxialStage) status.getConfiguration().getRocket().getChild(stageNo);
StageSeparationConfiguration separationConfig = stage.getStageSeparationConfiguration().get(flightConfigurationId);
if (separationConfig.getSeparationEvent().isSeparationEvent(event, stage)) {
addEvent(new FlightEvent(FlightEvent.Type.STAGE_SEPARATION,
@ -368,9 +338,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// Check for recovery device deployment, add events to queue
Iterator<RocketComponent> rci = status.getConfiguration().iterator();
while (rci.hasNext()) {
RocketComponent c = rci.next();
for (RocketComponent c : status.getConfiguration().getActiveComponents()) {
if (!(c instanceof RecoveryDevice))
continue;
DeploymentConfiguration deployConfig = ((RecoveryDevice) c).getDeploymentConfiguration().get(flightConfigurationId);
@ -393,8 +361,10 @@ public class BasicEventSimulationEngine implements SimulationEngine {
case IGNITION: {
// Ignite the motor
MotorId motorId = (MotorId) event.getData();
MotorInstanceConfiguration config = status.getMotorConfiguration();
config.setMotorIgnitionTime(motorId, event.getTime());
MotorInstanceConfiguration motorConfig = status.getMotorConfiguration();
MotorInstance inst = motorConfig.getMotorInstance(motorId);
inst.setIgnitionTime(event.getTime());
status.setMotorIgnited(true);
status.getFlightData().addEvent(event);
@ -422,7 +392,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
}
// Add ejection charge event
MotorId motorId = (MotorId) event.getData();
double delay = status.getMotorConfiguration().getEjectionDelay(motorId);
double delay = status.getMotorConfiguration().getMotorInstance(motorId).getEjectionDelay();
if (delay != Motor.PLUGGED) {
addEvent(new FlightEvent(FlightEvent.Type.EJECTION_CHARGE, status.getSimulationTime() + delay,
event.getSource(), event.getData()));
@ -450,7 +420,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
stages.add(boosterStatus);
// Mark the status as having dropped the booster
status.getConfiguration().setToStage(n - 1);
status.getConfiguration().clearOnlyStage(n);
// Mark the booster status as only having the booster.
boosterStatus.getConfiguration().setOnlyStage(n);
@ -476,12 +446,14 @@ public class BasicEventSimulationEngine implements SimulationEngine {
// TODO: HIGH: Check stage activeness for other events as well?
// Check whether any motor in the active stages is active anymore
for (MotorId motorId : status.getMotorConfiguration().getMotorIDs()) {
int stage = ((RocketComponent) status.getMotorConfiguration().
getMotorMount(motorId)).getStageNumber();
List<MotorInstance> activeMotors = status.getConfiguration().getActiveMotors(status.getMotorConfiguration());
for (MotorInstance curInstance : activeMotors) {
MotorId curID = curInstance.getID();
RocketComponent comp = ((RocketComponent) curInstance.getMount());
int stage = comp.getStageNumber();
if (!status.getConfiguration().isStageActive(stage))
continue;
if (!status.getMotorConfiguration().getMotorInstance(motorId).isActive())
if (!status.getMotorConfiguration().getMotorInstance(curID).isActive())
continue;
status.getWarnings().add(Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING);
}

View File

@ -119,7 +119,7 @@ public class SimulationStatus implements Monitorable {
*/
double length = this.simulationConditions.getLaunchRodLength();
double lugPosition = Double.NaN;
for (RocketComponent c : this.configuration) {
for (RocketComponent c : this.configuration.getActiveComponents()) {
if (c instanceof LaunchLug) {
double pos = c.toAbsolute(new Coordinate(c.getLength()))[0].x;
if (Double.isNaN(lugPosition) || pos > lugPosition) {

View File

@ -6,8 +6,9 @@ import java.util.Map;
import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
import net.sf.openrocket.aerodynamics.AerodynamicForces;
import net.sf.openrocket.aerodynamics.FlightConditions;
import net.sf.openrocket.motor.MotorId;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.simulation.FlightDataBranch;
import net.sf.openrocket.simulation.FlightDataType;
@ -20,7 +21,9 @@ import net.sf.openrocket.util.Coordinate;
public class DampingMoment extends AbstractSimulationListener {
private static final FlightDataType type = FlightDataType.getType("Damping moment coefficient", "Cdm", UnitGroup.UNITS_COEFFICIENT);
private static final FlightDataType[] typeList = { type };
// unused
//private static final FlightDataType[] typeList = { type };
@Override
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException {
@ -66,11 +69,12 @@ public class DampingMoment extends AbstractSimulationListener {
// find the maximum distance from nose to nozzle.
double nozzleDistance = 0;
for (MotorId id : status.getMotorConfiguration().getMotorIDs()) {
MotorInstanceConfiguration config = status.getMotorConfiguration();
Coordinate position = config.getMotorPosition(id);
Configuration config = status.getConfiguration();
MotorInstanceConfiguration motorConfig = status.getMotorConfiguration();
for (MotorInstance inst : config.getActiveMotors(motorConfig)) {
Coordinate position = inst.getPosition();
double x = position.x + config.getMotorInstance(id).getParentMotor().getLength();
double x = position.x + inst.getMotor().getLength();
if (x > nozzleDistance) {
nozzleDistance = x;
}

View File

@ -78,7 +78,7 @@ public class RollControlListener extends AbstractSimulationListener {
// Find the fin set named CONTROL
FinSet finset = null;
for (RocketComponent c : status.getConfiguration()) {
for (RocketComponent c : status.getConfiguration().getActiveComponents()) {
if ((c instanceof FinSet) && (c.getName().equals(CONTROL_FIN_NAME))) {
finset = (FinSet) c;
break;

View File

@ -98,7 +98,7 @@ public class CaliberUnit extends GeneralUnit {
* @return the caliber of the rocket, or the default caliber.
*/
public static double calculateCaliber(Configuration config) {
return calculateCaliber(config.iterator());
return calculateCaliber(config.getActiveComponents().iterator());
}
/**

View File

@ -647,7 +647,6 @@ public class BoosterSetTest extends BaseTestCase {
assertEquals(" init order error: " + treeDump + " Booster B: resultant positions: ", expectedOffset, resultantOffsetB, EPSILON);
}
@Test
public void testStageNumbering() {
Rocket rocket = createTestRocket();
@ -679,8 +678,29 @@ public class BoosterSetTest extends BaseTestCase {
actualStageNumber = boosterB.getStageNumber();
assertEquals(" init order error: Booster B: resultant positions: ", expectedStageNumber, actualStageNumber);
}
//rocket.getDefaultConfiguration().dumpConfig();
core.removeChild(2);
String treedump = rocket.toDebugTree();
int expectedStageCount = 3;
int actualStageCount = rocket.getStageCount();
assertEquals(" Stage tracking error: removed booster A, but count not updated: " + treedump, expectedStageCount, actualStageCount);
actualStageCount = rocket.getDefaultConfiguration().getStageCount();
assertEquals(" Stage tracking error: removed booster A, but configuration not updated: " + treedump, expectedStageCount, actualStageCount);
BoosterSet boosterC = createBooster();
boosterC.setName("Booster C Stage");
core.addChild(boosterC);
boosterC.setAxialOffset(Position.BOTTOM, 0);
expectedStageNumber = 2;
actualStageNumber = boosterC.getStageNumber();
assertEquals(" init order error: Booster B: resultant positions: ", expectedStageNumber, actualStageNumber);
//rocket.getDefaultConfiguration().dumpConfig();
}
@Test
public void testToAbsolute() {

View File

@ -1,11 +1,12 @@
package net.sf.openrocket.rocketcomponent;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.BitSet;
import java.util.EventObject;
import java.util.Iterator;
import net.sf.openrocket.rocketcomponent.RocketComponent.Position;
import net.sf.openrocket.util.StateChangeListener;
@ -72,20 +73,9 @@ public class ConfigurationTest extends BaseTestCase {
Configuration config = r1.getDefaultConfiguration();
/* Test */
// Test rocket component iterator
// TODO: validate iterator iterates correctly
for (Iterator<RocketComponent> i = config.iterator(); i.hasNext();) {
i.next();
}
// Rocket component iterator remove method is unsupported, should throw exception
try {
Iterator<RocketComponent> configIterator = config.iterator();
configIterator.remove();
} catch (UnsupportedOperationException e) {
assertTrue(e.getMessage().equals("remove unsupported"));
}
// the component iterator is no longer a custom iterator....
// use the standard iterator over the Collection<> returned by config.getActiveComponents()
// Test motor iterator
/* TODO: no motors in model Iterator<MotorMount> motorIterator()
@ -95,14 +85,14 @@ public class ConfigurationTest extends BaseTestCase {
}
*/
// Motor iterator remove method is unsupported, should throw exception
try {
Iterator<MotorMount> motorIterator = config.motorIterator();
motorIterator.remove();
} catch (UnsupportedOperationException e) {
assertTrue(e.getMessage().equals("remove unsupported"));
}
// // Motor iterator remove method is unsupported, should throw exception
// try {
// Iterator<MotorMount> motorIterator = config.motorIterator();
// motorIterator.remove();
// } catch (UnsupportedOperationException e) {
// assertTrue(e.getMessage().equals("remove unsupported"));
// }
//
/* Cleanup */
config.release();
@ -119,7 +109,7 @@ public class ConfigurationTest extends BaseTestCase {
Configuration configClone = config.clone(); // TODO validate clone worked
assertFalse(config.getRocket() == null);
assertFalse(config.hasMotors());
// assertFalse(config.hasMotors());
config.release();
}
@ -211,31 +201,34 @@ public class ConfigurationTest extends BaseTestCase {
Rocket r1 = makeSingleStageTestRocket();
Configuration config = r1.getDefaultConfiguration();
BitSet activeStageFlags = new BitSet();
activeStageFlags.set(0, false); // first stage
/* Test */
// test cloning of single stage rocket
Configuration configClone = config.clone(); // TODO validate clone worked
configClone.release();
// test explicitly setting only first stage active
config.clearAllStages();
config.setOnlyStage(0);
activeStageFlags.clear();
activeStageFlags.set(0, true);
validateStages(config, 1, activeStageFlags);
//config.dumpConfig();
//System.err.println("treedump: \n" + treedump);
// test that getStageCount() returns correct value
int expectedStageCount = 1;
int stageCount = config.getStageCount();
assertTrue("stage count doesn't match", stageCount == expectedStageCount);
expectedStageCount = 1;
stageCount = config.getActiveStageCount();
assertThat("active stage count doesn't match", stageCount, equalTo(expectedStageCount));
// test explicitly setting all stages up to first stage active
config.setToStage(0);
activeStageFlags.clear();
activeStageFlags.set(0, true);
validateStages(config, 1, activeStageFlags);
config.setOnlyStage(0);
// test explicitly setting all stages active
config.setAllStages();
activeStageFlags.clear();
activeStageFlags.set(0, true);
validateStages(config, 1, activeStageFlags);
// Cleanup
config.release();
@ -252,43 +245,57 @@ public class ConfigurationTest extends BaseTestCase {
Rocket r1 = makeTwoStageTestRocket();
Configuration config = r1.getDefaultConfiguration();
BitSet activeStageFlags = new BitSet();
activeStageFlags.set(0, false); // booster (first) stage
activeStageFlags.set(1, false); // sustainer (second) stage
/* Test */
// test cloning of two stage rocket
Configuration configClone = config.clone(); // TODO validate clone worked
configClone.release();
int expectedStageCount;
int stageCount;
expectedStageCount = 2;
stageCount = config.getStageCount();
assertThat("stage count doesn't match", stageCount, equalTo(expectedStageCount));
config.clearAllStages();
assertThat(" clear all stages: check #0: ", config.isStageActive(0), equalTo(false));
assertThat(" clear all stages: check #1: ", config.isStageActive(1), equalTo(false));
// test explicitly setting only first stage active
config.setOnlyStage(0);
activeStageFlags.clear();
activeStageFlags.set(0, true);
validateStages(config, 2, activeStageFlags);
// test explicitly setting all stages up to first stage active
config.setToStage(0);
activeStageFlags.clear();
activeStageFlags.set(0, true);
validateStages(config, 2, activeStageFlags);
expectedStageCount = 1;
stageCount = config.getActiveStageCount();
assertThat("active stage count doesn't match", stageCount, equalTo(expectedStageCount));
assertThat(" setting single stage active: ", config.isStageActive(0), equalTo(true));
// test explicitly setting all stages up to second stage active
config.setToStage(1);
activeStageFlags.clear();
activeStageFlags.set(0, 2, true);
validateStages(config, 2, activeStageFlags);
config.setOnlyStage(1);
assertThat(config.toDebug() + "Setting single stage active: ", config.isStageActive(1), equalTo(true));
config.clearOnlyStage(0);
assertThat(" deactivate stage #0: ", config.isStageActive(0), equalTo(false));
assertThat(" deactive stage #0: ", config.isStageActive(1), equalTo(true));
// test explicitly setting all two stages active
config.setAllStages();
activeStageFlags.clear();
activeStageFlags.set(0, 2, true);
validateStages(config, 2, activeStageFlags);
assertThat(" activate all stages: check #0: ", config.isStageActive(0), equalTo(true));
assertThat(" activate all stages: check #1: ", config.isStageActive(1), equalTo(true));
// test toggling single stage
config.setAllStages();
config.toggleStage(0);
assertThat(" toggle stage #0: ", config.isStageActive(0), equalTo(false));
config.toggleStage(0);
assertThat(" toggle stage #0: ", config.isStageActive(0), equalTo(true));
config.toggleStage(0);
assertThat(" toggle stage #0: ", config.isStageActive(0), equalTo(false));
// Cleanup
config.release();
}
///////////////////// Helper Methods ////////////////////////////
@ -307,43 +314,45 @@ public class ConfigurationTest extends BaseTestCase {
}
}
assertTrue(config.getActiveStageCount() == expectedActiveStageCount);
int[] stages = config.getActiveStages();
assertTrue(stages.length == expectedActiveStageCount);
// test if isHead() detects first stage being active or inactive
if (activeStageFlags.get(0)) {
assertTrue(config.isHead());
} else {
assertFalse(config.isHead());
}
// test if isStageActive() detects stage x being active or inactive
for (int i = 0; i < expectedStageCount; i++) {
if (activeStageFlags.get(i)) {
assertTrue(config.isStageActive(i));
} else {
assertFalse(config.isStageActive(i));
}
}
// test boundary conditions
// stage -1 should not exist, and isStageActive() should throw exception
boolean IndexOutOfBoundsExceptionFlag = false;
try {
assertFalse(config.isStageActive(-1));
} catch (IndexOutOfBoundsException e) {
IndexOutOfBoundsExceptionFlag = true;
}
assertTrue(IndexOutOfBoundsExceptionFlag);
// n+1 stage should not exist, isStageActive() should return false
// TODO: isStageActive(stageCount + 1) really should throw IndexOutOfBoundsException
assertFalse(config.isStageActive(stageCount + 1));
assertTrue("this test is not yet written.", false);
// int[] stages = config.getActiveStages();
//
// assertTrue(stages.length == expectedActiveStageCount);
//
// // test if isHead() detects first stage being active or inactive
// if (activeStageFlags.get(0)) {
// assertTrue(config.isHead());
// } else {
// assertFalse(config.isHead());
// }
//
// // test if isStageActive() detects stage x being active or inactive
// for (int i = 0; i < expectedStageCount; i++) {
// if (activeStageFlags.get(i)) {
// assertTrue(config.isStageActive(i));
// } else {
// assertFalse(config.isStageActive(i));
// }
// }
//
// // test boundary conditions
//
// // stage -1 should not exist, and isStageActive() should throw exception
// boolean IndexOutOfBoundsExceptionFlag = false;
// try {
// assertFalse(config.isStageActive(-1));
// } catch (IndexOutOfBoundsException e) {
// IndexOutOfBoundsExceptionFlag = true;
// }
// assertTrue(IndexOutOfBoundsExceptionFlag);
//
// // n+1 stage should not exist, isStageActive() should return false
// // TODO: isStageActive(stageCount + 1) really should throw IndexOutOfBoundsException
// assertFalse(config.isStageActive(stageCount + 1));
}
//////////////////// Test Rocket Creation Methods /////////////////////////
public static Rocket makeEmptyRocket() {

View File

@ -11,12 +11,15 @@ import javax.swing.JToggleButton;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.StateChangeListener;
public class StageSelector extends JPanel implements StateChangeListener {
private static final long serialVersionUID = -2898763402479628711L;
private static final Translator trans = Application.getTranslator();
private final Configuration configuration;
@ -40,13 +43,11 @@ public class StageSelector extends JPanel implements StateChangeListener {
if (buttons.size() == stages)
return;
while (buttons.size() > stages) {
JToggleButton button = buttons.remove(buttons.size() - 1);
this.remove(button);
}
while (buttons.size() < stages) {
JToggleButton button = new JToggleButton(new StageAction(buttons.size()));
buttons.clear();
this.removeAll();
for(AxialStage stage : configuration.getRocket().getStageList()){
int stageNum = stage.getStageNumber();
JToggleButton button = new JToggleButton(new StageAction(stageNum));
this.add(button);
buttons.add(button);
}
@ -55,8 +56,6 @@ public class StageSelector extends JPanel implements StateChangeListener {
}
@Override
public void stateChanged(EventObject e) {
updateButtons();
@ -64,10 +63,11 @@ public class StageSelector extends JPanel implements StateChangeListener {
private class StageAction extends AbstractAction implements StateChangeListener {
private final int stage;
private static final long serialVersionUID = 7433006728984943763L;
private final int stageNumber;
public StageAction(final int stage) {
this.stage = stage;
this.stageNumber = stage;
configuration.addChangeListener(this);
stateChanged(null);
}
@ -75,37 +75,20 @@ public class StageSelector extends JPanel implements StateChangeListener {
@Override
public Object getValue(String key) {
if (key.equals(NAME)) {
//// Stage
return trans.get("StageAction.Stage") + " " + (stage + 1);
// Stage
return trans.get("StageAction.Stage") + " " + (stageNumber );
}
return super.getValue(key);
}
@Override
public void actionPerformed(ActionEvent e) {
configuration.setToStage(stage);
// boolean state = (Boolean)getValue(SELECTED_KEY);
// if (state == true) {
// // Was disabled, now enabled
// configuration.setToStage(stage);
// } else {
// // Was enabled, check what to do
// if (configuration.isStageActive(stage + 1)) {
// configuration.setToStage(stage);
// } else {
// if (stage == 0)
// configuration.setAllStages();
// else
// configuration.setToStage(stage-1);
// }
// }
// stateChanged(null);
configuration.toggleStage(stageNumber);
}
@Override
public void stateChanged(EventObject e) {
this.putValue(SELECTED_KEY, configuration.isStageActive(stage));
this.putValue(SELECTED_KEY, configuration.isStageActive(stageNumber));
}
}
}

View File

@ -513,7 +513,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
cgData.clear();
dragData.clear();
rollData.clear();
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
forces = aeroData.get(c);
Coordinate cg = massData.get(c);

View File

@ -2,7 +2,6 @@ package net.sf.openrocket.gui.figure3d;
import java.awt.Point;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
@ -62,7 +61,7 @@ public abstract class RocketRenderer {
// Store a vector of pickable parts.
final Vector<RocketComponent> pickParts = new Vector<RocketComponent>();
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (ignore != null && ignore.contains(c))
continue;
@ -116,7 +115,7 @@ public abstract class RocketRenderer {
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, colorBlack, 0);
gl.glLineWidth(5.0f);
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (selection.contains(c)) {
// Draw as lines, set Z to nearest
gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE);
@ -141,7 +140,7 @@ public abstract class RocketRenderer {
gl.glCullFace(GL.GL_BACK);
// Draw all inner components
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (isDrawn(c)) {
if (!isDrawnTransparent(c)) {
renderComponent(gl, c, 1.0f);
@ -153,7 +152,7 @@ public abstract class RocketRenderer {
// Draw T&T front faces blended, without depth test
gl.glEnable(GL.GL_BLEND);
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (isDrawn(c)) {
if (isDrawnTransparent(c)) {
renderComponent(gl, c, 0.2f);
@ -166,9 +165,11 @@ public abstract class RocketRenderer {
private void renderMotors(GL2 gl, Configuration configuration) {
String motorID = configuration.getFlightConfigurationID();
Iterator<MotorMount> iterator = configuration.motorIterator();
while (iterator.hasNext()) {
MotorMount mount = iterator.next();
for( RocketComponent comp : configuration.getActiveComponents()){
if( comp instanceof MotorMount){
MotorMount mount = (MotorMount) comp;
Motor motor = mount.getMotorConfiguration().get(motorID).getMotor();
double length = motor.getLength();
@ -182,6 +183,7 @@ public abstract class RocketRenderer {
gl.glPopMatrix();
}
}
}
}

View File

@ -14,7 +14,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import javax.media.opengl.DebugGL2;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
@ -44,7 +43,6 @@ import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.util.Color;
@ -416,22 +414,24 @@ public class PhotoPanel extends JPanel implements GLEventListener {
rr.render(drawable, configuration, new HashSet<RocketComponent>());
//Figure out the lowest stage shown
final int currentStageNumber = configuration.getActiveStages()[configuration.getActiveStages().length-1];
final AxialStage currentStage = (AxialStage)configuration.getRocket().getChild(currentStageNumber);
final int bottomStageNumber = configuration.getBottomStage().getStageNumber();
//final int currentStageNumber = configuration.getActiveStages()[configuration.getActiveStages().length-1];
//final AxialStage currentStage = (AxialStage)configuration.getRocket().getChild( bottomStageNumber);
final String motorID = configuration.getFlightConfigurationID();
final Iterator<MotorMount> iterator = configuration.motorIterator();
motor: while (iterator.hasNext()) {
final MotorMount mount = iterator.next();
final Iterator<RocketComponent> iter = configuration.getActiveComponents().iterator();
while( iter.hasNext()){
RocketComponent comp = iter.next();
if( comp instanceof MotorMount){
final MotorMount mount = (MotorMount) comp;
int curStageNumber = comp.getStageNumber();
//If this mount is not in currentStage continue on to the next one.
RocketComponent parent = ((RocketComponent)mount);
while ( null != (parent = parent.getParent()) ){
if ( parent instanceof AxialStage ){
if ( parent != currentStage )
continue motor;
break;
}
if( curStageNumber != bottomStageNumber ){
continue;
}
final Motor motor = mount.getMotorConfiguration().get(motorID).getMotor();
@ -449,6 +449,7 @@ public class PhotoPanel extends JPanel implements GLEventListener {
gl.glPopMatrix();
}
}
}
gl.glDisable(GL.GL_BLEND);
gl.glFrontFace(GL.GL_CCW);

View File

@ -13,6 +13,7 @@ import java.awt.geom.Rectangle2D;
import net.sf.openrocket.aerodynamics.Warning;
import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.simulation.FlightData;
import net.sf.openrocket.startup.Application;
@ -47,7 +48,9 @@ public class RocketInfo implements FigureElement {
private double cg = 0, cp = 0;
private double length = 0, diameter = 0;
private double mass = 0;
private double aoa = Double.NaN, theta = Double.NaN, mach = Application.getPreferences().getDefaultMach();
private double aoa = Double.NaN;
private double theta = Double.NaN;
private double mach = Application.getPreferences().getDefaultMach();
private WarningSet warnings = null;
@ -151,7 +154,8 @@ public class RocketInfo implements FigureElement {
UnitGroup.UNITS_LENGTH.getDefaultUnit().toStringUnit(diameter));
String massText;
if (configuration.hasMotors())
MotorInstanceConfiguration mic = new MotorInstanceConfiguration(configuration);
if (mic.hasMotors())
//// Mass with motors
massText = trans.get("RocketInfo.massText1") +" ";
else

View File

@ -16,6 +16,7 @@ import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.Rocket;
@ -192,7 +193,9 @@ public class DesignReport {
canvas.showText("" + rocket.getStageCount());
if (configuration.hasMotors()) {
MotorInstanceConfiguration mic = new MotorInstanceConfiguration(configuration);
if (mic.hasMotors()){
if (configuration.getStageCount() > 1) {
canvas.newlineShowText(MASS_WITH_MOTORS);
} else {
@ -341,7 +344,8 @@ public class DesignReport {
for (RocketComponent c : rocket) {
if (c instanceof AxialStage) {
config.setToStage(stage);
config.clearAllStages();
config.setOnlyStage(stage);
stage++;
stageMass = massCalc.getCG(config, MassCalcType.LAUNCH_MASS).weight;
// Calculate total thrust-to-weight from only lowest stage motors

View File

@ -23,6 +23,8 @@ import net.sf.openrocket.gui.figureelements.FigureElement;
import net.sf.openrocket.gui.util.ColorConversion;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.MotorMount;
@ -339,13 +341,13 @@ public class RocketFigure extends AbstractScaleFigure {
RenderingHints.VALUE_STROKE_NORMALIZE);
// Draw motors
String motorID = configuration.getFlightConfigurationID();
Color fillColor = ((SwingPreferences)Application.getPreferences()).getMotorFillColor();
Color borderColor = ((SwingPreferences)Application.getPreferences()).getMotorBorderColor();
Iterator<MotorMount> iterator = configuration.motorIterator();
while (iterator.hasNext()) {
MotorMount mount = iterator.next();
Motor motor = mount.getMotor(motorID);
MotorInstanceConfiguration mic = new MotorInstanceConfiguration(configuration);
for( MotorInstance curInstance : configuration.getActiveMotors(mic)){
MotorMount mount = curInstance.getMount();
Motor motor = curInstance.getMotor();
double motorLength = motor.getLength();
double motorRadius = motor.getDiameter() / 2;

View File

@ -56,6 +56,7 @@ import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.masscalc.MassCalculator;
import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.Configuration;
@ -640,7 +641,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
length = maxX - minX;
}
for (RocketComponent c : configuration) {
for (RocketComponent c : configuration.getActiveComponents()) {
if (c instanceof SymmetricComponent) {
double d1 = ((SymmetricComponent) c).getForeRadius() * 2;
double d2 = ((SymmetricComponent) c).getAftRadius() * 2;
@ -692,8 +693,10 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
// Stop previous computation (if any)
stopBackgroundSimulation();
MotorInstanceConfiguration mic = new MotorInstanceConfiguration( configuration);
// Check that configuration has motors
if (!configuration.hasMotors()) {
if (!mic.hasMotors()){
extraText.setFlightData(FlightData.NaN_DATA);
extraText.setCalculatingData(false);
return;

View File

@ -8,6 +8,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executors;
@ -30,6 +31,8 @@ import net.sf.openrocket.gui.dialogs.DetailDialog;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.motor.MotorInstance;
import net.sf.openrocket.motor.MotorInstanceConfiguration;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.IgnitionConfiguration;
import net.sf.openrocket.rocketcomponent.MotorMount;
@ -306,11 +309,13 @@ public class SimulationRunDialog extends JDialog {
// Calculate estimate of motor burn time
double launchBurn = 0;
double otherBurn = 0;
Configuration config = simulation.getConfiguration();
String id = simulation.getOptions().getMotorConfigurationID();
Iterator<MotorMount> iterator = config.motorIterator();
while (iterator.hasNext()) {
MotorMount m = iterator.next();
Configuration config = simulation.getConfiguration();
MotorInstanceConfiguration mic = new MotorInstanceConfiguration(config);
Collection<MotorInstance> activeMotors = config.getActiveMotors(mic );
for( MotorInstance curInstance : activeMotors ){
MotorMount m = curInstance.getMount();
if (m.getIgnitionConfiguration().getDefault().getIgnitionEvent() == IgnitionConfiguration.IgnitionEvent.LAUNCH)
launchBurn = MathUtil.max(launchBurn, m.getMotor(id).getBurnTimeEstimate());
else