Merge branch 'unstable' into issue-1791
This commit is contained in:
commit
4691bb2ae3
@ -140,12 +140,11 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
WarningSet warnings)
|
||||
{
|
||||
// forces for this component, and all forces below it, in the rocket-tree
|
||||
// => regardless `if(comp isinstance ComponentAssembly)`, or not.
|
||||
AerodynamicForces aggregateForces = new AerodynamicForces().zero();
|
||||
aggregateForces.setComponent(comp);
|
||||
|
||||
// forces for this component, _only_
|
||||
if(comp.isAerodynamic()) {
|
||||
if(comp.isAerodynamic() || comp instanceof ComponentAssembly) {
|
||||
RocketComponentCalc calcObj = calcMap.get(comp);
|
||||
if (null == calcObj) {
|
||||
throw new NullPointerException("Could not find a CalculationObject for aerodynamic Component!: " + comp.getComponentName());
|
||||
@ -163,6 +162,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
if (child instanceof AxialStage && !configuration.isStageActive(child.getStageNumber())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// forces particular to each component
|
||||
AerodynamicForces childForces = calculateForceAnalysis(configuration, conds, child, instances, eachForces, assemblyForces, warnings);
|
||||
|
||||
@ -173,7 +173,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
|
||||
assemblyForces.put(comp, aggregateForces);
|
||||
|
||||
return eachForces.get(comp);
|
||||
return assemblyForces.get(comp);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -944,7 +944,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator {
|
||||
calcMap = new HashMap<>();
|
||||
|
||||
for (RocketComponent comp: configuration.getAllComponents()) {
|
||||
if (!comp.isAerodynamic()) {
|
||||
if (!comp.isAerodynamic() && !(comp instanceof ComponentAssembly)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
package net.sf.openrocket.aerodynamics.barrowman;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.util.Transformation;
|
||||
|
||||
/*
|
||||
* Aerodynamic properties of a component assembly. Since an "assembly"
|
||||
* has no impact except as a summation of its subparts, just returns 0
|
||||
*
|
||||
*/
|
||||
public class ComponentAssemblyCalc extends RocketComponentCalc {
|
||||
public ComponentAssemblyCalc(RocketComponent c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void calculateNonaxialForces(FlightConditions conditions, Transformation transform,
|
||||
AerodynamicForces forces, WarningSet warnings) {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public double calculateFrictionCD(FlightConditions conditions, double componentCf, WarningSet warnings) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double calculatePressureCD(FlightConditions conditions,
|
||||
double stagnationCD, double baseCD, WarningSet warnings) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -308,7 +308,7 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
*/
|
||||
public Status getStatus() {
|
||||
mutex.verify();
|
||||
if (status == Status.UPTODATE || status == Status.LOADED) {
|
||||
if (isStatusUpToDate(status)) {
|
||||
if (rocket.getFunctionalModID() != simulatedRocketID || !options.equals(simulatedConditions)) {
|
||||
status = Status.OUTDATED;
|
||||
}
|
||||
@ -330,6 +330,14 @@ public class Simulation implements ChangeSource, Cloneable {
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true is the status indicates that the simulation data is up-to-date.
|
||||
* @param status status of the simulation to check for if its data is up-to-date
|
||||
*/
|
||||
public static boolean isStatusUpToDate(Status status) {
|
||||
return status == Status.UPTODATE || status == Status.LOADED || status == Status.EXTERNAL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -29,6 +29,8 @@ public class RockSimCommonConstants {
|
||||
public static final String DENSITY_TYPE = "DensityType";
|
||||
public static final String RADIAL_LOC = "RadialLoc";
|
||||
public static final String RADIAL_ANGLE = "RadialAngle";
|
||||
public static final String AUTO_CALC_RADIAL_DISTANCE = "AutoCalcRadialDistance";
|
||||
public static final String AUTO_CALC_RADIAL_ANGLE = "AutoCalcRadialAngle";
|
||||
public static final String LOCATION_MODE = "LocationMode";
|
||||
public static final String FINISH_CODE = "FinishCode";
|
||||
public static final String SERIAL_NUMBER = "SerialNo";
|
||||
|
@ -11,6 +11,7 @@ import net.sf.openrocket.rocketcomponent.InnerTube;
|
||||
import net.sf.openrocket.rocketcomponent.LaunchLug;
|
||||
import net.sf.openrocket.rocketcomponent.MassObject;
|
||||
import net.sf.openrocket.rocketcomponent.Parachute;
|
||||
import net.sf.openrocket.rocketcomponent.PodSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.Streamer;
|
||||
import net.sf.openrocket.rocketcomponent.Transition;
|
||||
@ -50,6 +51,7 @@ public class BodyTubeDTO extends BasePartDTO implements AttachableParts {
|
||||
@XmlElementRefs({
|
||||
@XmlElementRef(name = RockSimCommonConstants.BODY_TUBE, type = BodyTubeDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.BODY_TUBE, type = InnerBodyTubeDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.TRANSITION, type = TransitionDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.RING, type = CenteringRingDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.LAUNCH_LUG, type = LaunchLugDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.FIN_SET, type = FinSetDTO.class),
|
||||
@ -57,7 +59,8 @@ public class BodyTubeDTO extends BasePartDTO implements AttachableParts {
|
||||
@XmlElementRef(name = RockSimCommonConstants.TUBE_FIN_SET, type = TubeFinSetDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.STREAMER, type = StreamerDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.PARACHUTE, type = ParachuteDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.MASS_OBJECT, type = MassObjectDTO.class)})
|
||||
@XmlElementRef(name = RockSimCommonConstants.MASS_OBJECT, type = MassObjectDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.EXTERNAL_POD, type = PodSetDTO.class)})
|
||||
List<BasePartDTO> attachedParts = new ArrayList<BasePartDTO>();
|
||||
|
||||
/**
|
||||
@ -125,6 +128,8 @@ public class BodyTubeDTO extends BasePartDTO implements AttachableParts {
|
||||
addAttachedPart(new FinSetDTO((FinSet) rocketComponents));
|
||||
} else if (rocketComponents instanceof TubeFinSet) {
|
||||
addAttachedPart(new TubeFinSetDTO((TubeFinSet) rocketComponents));
|
||||
} else if (rocketComponents instanceof PodSet) {
|
||||
addAttachedPart(new PodSetDTO((PodSet) rocketComponents));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
110
core/src/net/sf/openrocket/file/rocksim/export/PodSetDTO.java
Normal file
110
core/src/net/sf/openrocket/file/rocksim/export/PodSetDTO.java
Normal file
@ -0,0 +1,110 @@
|
||||
package net.sf.openrocket.file.rocksim.export;
|
||||
|
||||
import net.sf.openrocket.file.rocksim.RockSimCommonConstants;
|
||||
import net.sf.openrocket.rocketcomponent.BodyTube;
|
||||
import net.sf.openrocket.rocketcomponent.NoseCone;
|
||||
import net.sf.openrocket.rocketcomponent.PodSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.Transition;
|
||||
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
|
||||
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlElementRef;
|
||||
import javax.xml.bind.annotation.XmlElementRefs;
|
||||
import javax.xml.bind.annotation.XmlElementWrapper;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Models the XML element for a RockSim pod.
|
||||
*/
|
||||
@XmlRootElement(name = RockSimCommonConstants.EXTERNAL_POD)
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class PodSetDTO extends BasePartDTO implements AttachableParts {
|
||||
@XmlElement(name = RockSimCommonConstants.AUTO_CALC_RADIAL_DISTANCE)
|
||||
private int autoCalcRadialDistance = 0;
|
||||
@XmlElement(name = RockSimCommonConstants.AUTO_CALC_RADIAL_ANGLE)
|
||||
private int autoCalcRadialAngle = 0;
|
||||
@XmlElementWrapper(name = RockSimCommonConstants.ATTACHED_PARTS)
|
||||
@XmlElementRefs({
|
||||
@XmlElementRef(name = RockSimCommonConstants.BODY_TUBE, type = BodyTubeDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.NOSE_CONE, type = NoseConeDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.TRANSITION, type = TransitionDTO.class),
|
||||
@XmlElementRef(name = RockSimCommonConstants.EXTERNAL_POD, type = PodSetDTO.class)})
|
||||
List<BasePartDTO> attachedParts = new ArrayList<BasePartDTO>();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public PodSetDTO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor. Fully populates this instance with values taken from the OR PodSet.
|
||||
*
|
||||
* @param theORPodSet
|
||||
*/
|
||||
public PodSetDTO(PodSet theORPodSet) {
|
||||
super(theORPodSet);
|
||||
// OR should always override the radial angle and distance
|
||||
setAutoCalcRadialDistance(false);
|
||||
setAutoCalcRadialAngle(false);
|
||||
setRadialAngle(theORPodSet.getAngleOffset());
|
||||
setRadialLoc(theORPodSet.getRadiusMethod().getRadius(
|
||||
theORPodSet.getParent(), theORPodSet,
|
||||
theORPodSet.getRadiusOffset()) * RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
|
||||
setXb(theORPodSet.getAxialOffset(AxialMethod.TOP) * RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
|
||||
|
||||
for (RocketComponent child : theORPodSet.getChildren()) {
|
||||
if (child instanceof PodSet) {
|
||||
addAttachedPart(new PodSetDTO((PodSet) child));
|
||||
} else if (child instanceof BodyTube) {
|
||||
addAttachedPart(new BodyTubeDTO((BodyTube) child));
|
||||
} else if (child instanceof NoseCone) {
|
||||
addAttachedPart(new NoseConeDTO((NoseCone) child));
|
||||
} else if (child instanceof Transition) {
|
||||
addAttachedPart(new TransitionDTO((Transition) child));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getAutoCalcRadialDistance() {
|
||||
return autoCalcRadialDistance;
|
||||
}
|
||||
|
||||
public void setAutoCalcRadialDistance(boolean motorMount) {
|
||||
if (motorMount) {
|
||||
this.autoCalcRadialDistance = 1;
|
||||
} else {
|
||||
this.autoCalcRadialDistance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getAutoCalcRadialAngle() {
|
||||
return autoCalcRadialAngle;
|
||||
}
|
||||
|
||||
public void setAutoCalcRadialAngle(boolean motorMount) {
|
||||
if (motorMount) {
|
||||
this.autoCalcRadialAngle = 1;
|
||||
} else {
|
||||
this.autoCalcRadialAngle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAttachedPart(BasePartDTO part) {
|
||||
if (!attachedParts.contains(part)) {
|
||||
attachedParts.add(part);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttachedPart(BasePartDTO part) {
|
||||
attachedParts.remove(part);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import net.sf.openrocket.file.DocumentLoadingContext;
|
||||
import net.sf.openrocket.file.rocksim.RockSimCommonConstants;
|
||||
import net.sf.openrocket.file.simplesax.AbstractElementHandler;
|
||||
import net.sf.openrocket.file.simplesax.ElementHandler;
|
||||
import net.sf.openrocket.rocketcomponent.PodSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -68,23 +69,32 @@ class AttachedPartsHandler extends AbstractElementHandler {
|
||||
return new RingHandler(context, component, warnings);
|
||||
}
|
||||
if (RockSimCommonConstants.BODY_TUBE.equals(element)) {
|
||||
return new InnerBodyTubeHandler(context, component, warnings);
|
||||
// Pods can have BodyTubes as attached parts, but not inner tubes. All other components can't have BodyTubes as
|
||||
// attached parts.
|
||||
if (component instanceof PodSet) {
|
||||
return new BodyTubeHandler(context, component, warnings);
|
||||
} else {
|
||||
return new InnerBodyTubeHandler(context, component, warnings);
|
||||
}
|
||||
}
|
||||
if (RockSimCommonConstants.TRANSITION.equals(element)) {
|
||||
return new TransitionHandler(context, component, warnings);
|
||||
}
|
||||
if (RockSimCommonConstants.NOSE_CONE.equals(element)) {
|
||||
return new NoseConeHandler(context, component, warnings);
|
||||
}
|
||||
if (RockSimCommonConstants.SUBASSEMBLY.equals(element)) {
|
||||
return new SubAssemblyHandler(context, component);
|
||||
}
|
||||
if (RockSimCommonConstants.TUBE_FIN_SET.equals(element)) {
|
||||
return new TubeFinSetHandler(context, component, warnings);
|
||||
}
|
||||
if (RockSimCommonConstants.EXTERNAL_POD.equals(element)) {
|
||||
return new PodHandler(context, component, warnings);
|
||||
}
|
||||
if (RockSimCommonConstants.RING_TAIL.equals(element)) {
|
||||
warnings.add("Ring tails are not currently supported. Ignoring.");
|
||||
}
|
||||
if (RockSimCommonConstants.EXTERNAL_POD.equals(element)) {
|
||||
warnings.add("Pods are not currently supported. Ignoring.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ class BodyTubeHandler extends BaseHandler<BodyTube> {
|
||||
* The OpenRocket BodyTube.
|
||||
*/
|
||||
private final BodyTube bodyTube;
|
||||
private int isInsideTube = 0;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -80,6 +81,9 @@ class BodyTubeHandler extends BaseHandler<BodyTube> {
|
||||
if (RockSimCommonConstants.MATERIAL.equals(element)) {
|
||||
setMaterialName(content);
|
||||
}
|
||||
if (RockSimCommonConstants.IS_INSIDE_TUBE.equals(element)) {
|
||||
isInsideTube = Integer.parseInt(content);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number.");
|
||||
}
|
||||
@ -104,4 +108,11 @@ class BodyTubeHandler extends BaseHandler<BodyTube> {
|
||||
public Material.Type getMaterialType() {
|
||||
return Material.Type.BULK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0 if this is a body tube, 1 if it is an inside tube.
|
||||
*/
|
||||
public int isInsideTube() {
|
||||
return isInsideTube;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
package net.sf.openrocket.file.rocksim.importt;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.file.DocumentLoadingContext;
|
||||
import net.sf.openrocket.file.rocksim.RockSimCommonConstants;
|
||||
import net.sf.openrocket.file.simplesax.ElementHandler;
|
||||
import net.sf.openrocket.file.simplesax.PlainTextHandler;
|
||||
import net.sf.openrocket.material.Material;
|
||||
import net.sf.openrocket.rocketcomponent.PodSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class PodHandler extends PositionDependentHandler<PodSet> {
|
||||
/**
|
||||
* The OpenRocket BodyTube.
|
||||
*/
|
||||
private final PodSet podSet;
|
||||
|
||||
public PodHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings) {
|
||||
super(context);
|
||||
if (c == null) {
|
||||
throw new IllegalArgumentException("The parent component of a pod set may not be null.");
|
||||
}
|
||||
podSet = new PodSet();
|
||||
podSet.setInstanceCount(1); // RockSim only supports one pod instance
|
||||
podSet.setRadiusMethod(RadiusMethod.FREE); // RockSim radial offset is relative to the center of the parent
|
||||
if (isCompatible(c, PodSet.class, warnings)) {
|
||||
c.addChild(podSet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) throws SAXException {
|
||||
if (RockSimCommonConstants.BODY_TUBE.equals(element)) { // RockSim pods allow body tubes, not inner tubes
|
||||
return new BodyTubeHandler(context, podSet, warnings);
|
||||
}
|
||||
if (RockSimCommonConstants.ATTACHED_PARTS.equals(element)) {
|
||||
return new AttachedPartsHandler(context, podSet);
|
||||
}
|
||||
return PlainTextHandler.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeElement(String element, HashMap<String, String> attributes, String content, WarningSet warnings) throws SAXException {
|
||||
super.closeElement(element, attributes, content, warnings);
|
||||
if (RockSimCommonConstants.RADIAL_ANGLE.equals(element)) {
|
||||
podSet.setAngleOffset(Double.parseDouble(content));
|
||||
}
|
||||
if (RockSimCommonConstants.RADIAL_LOC.equals(element)) {
|
||||
podSet.setRadiusOffset(Double.parseDouble(content) / RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PodSet getComponent() {
|
||||
return podSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material.Type getMaterialType() {
|
||||
return Material.Type.BULK;
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@ import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.file.DocumentLoadingContext;
|
||||
import net.sf.openrocket.file.rocksim.RockSimCommonConstants;
|
||||
import net.sf.openrocket.file.rocksim.RockSimLocationMode;
|
||||
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
|
||||
import net.sf.openrocket.rocketcomponent.ParallelStage;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
|
||||
|
||||
@ -70,6 +72,10 @@ public abstract class PositionDependentHandler<C extends RocketComponent> extend
|
||||
* Set the axialMethod of a component.
|
||||
*/
|
||||
protected void setLocation() {
|
||||
if ((getComponent() instanceof ComponentAssembly || getComponent() instanceof ParallelStage) &&
|
||||
getComponent().getParent() == null) {
|
||||
return;
|
||||
}
|
||||
getComponent().setAxialMethod(axialMethod);
|
||||
if (axialMethod.equals(AxialMethod.BOTTOM)) {
|
||||
getComponent().setAxialOffset(-1d * positionValue);
|
||||
|
@ -121,19 +121,23 @@ public class PodSet extends ComponentAssembly implements RingInstanceable {
|
||||
|
||||
@Override
|
||||
public double getAxialOffset() {
|
||||
return getAxialOffset(this.axialMethod);
|
||||
}
|
||||
@Override
|
||||
public double getAxialOffset(AxialMethod method) {
|
||||
double returnValue;
|
||||
|
||||
|
||||
if (this.isAfter()){
|
||||
// remember the implicit (this instanceof Stage)
|
||||
throw new BugException("found a pod positioned via: AFTER, but is not on the centerline?!: " + this.getName() + " is " + this.getAxialMethod().name() );
|
||||
} else {
|
||||
returnValue = super.getAxialOffset(this.axialMethod);
|
||||
returnValue = super.getAxialOffset(method);
|
||||
}
|
||||
|
||||
|
||||
if (MathUtil.EPSILON > Math.abs(returnValue)) {
|
||||
returnValue = 0.0;
|
||||
}
|
||||
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
@ -221,25 +221,16 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
// and thrust < THRUST_TUMBLE_CONDITION threshold
|
||||
|
||||
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 = currentStatus.getConfiguration().isStageActive(0);
|
||||
final boolean isApogee = currentStatus.isApogeeReached();
|
||||
if (tooMuchThrust) {
|
||||
currentStatus.getWarnings().add(Warning.TUMBLE_UNDER_THRUST);
|
||||
} else if (isApogee || !isSustainer) {
|
||||
addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, currentStatus.getSimulationTime()));
|
||||
currentStatus.setTumbling(true);
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isSustainer = currentStatus.getConfiguration().isStageActive(0);
|
||||
final boolean isApogee = currentStatus.isApogeeReached();
|
||||
if (wantToTumble && (isApogee || !isSustainer)) {
|
||||
addEvent(new FlightEvent(FlightEvent.Type.TUMBLE, currentStatus.getSimulationTime()));
|
||||
}
|
||||
}
|
||||
|
||||
// If I'm on the ground and have no events in the queue, I'm done
|
||||
@ -535,6 +526,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
|
||||
currentStatus.getFlightData().addEvent(event);
|
||||
}
|
||||
log.debug("deployed recovery devices: " + currentStatus.getDeployedRecoveryDevices().size() );
|
||||
break;
|
||||
|
||||
case GROUND_HIT:
|
||||
@ -556,10 +548,19 @@ public class BasicEventSimulationEngine implements SimulationEngine {
|
||||
break;
|
||||
|
||||
case TUMBLE:
|
||||
if (!currentStatus.isLanded()) {
|
||||
currentStepper = tumbleStepper;
|
||||
currentStatus = currentStepper.initialize(currentStatus);
|
||||
}
|
||||
// Inhibit if we've deployed a parachute or we're on the ground
|
||||
if ((currentStatus.getDeployedRecoveryDevices().size() > 0) || currentStatus.isLanded())
|
||||
break;
|
||||
|
||||
currentStepper = tumbleStepper;
|
||||
currentStatus = currentStepper.initialize(currentStatus);
|
||||
|
||||
final boolean tooMuchThrust = currentStatus.getFlightData().getLast(FlightDataType.TYPE_THRUST_FORCE) > THRUST_TUMBLE_CONDITION;
|
||||
if (tooMuchThrust) {
|
||||
currentStatus.getWarnings().add(Warning.TUMBLE_UNDER_THRUST);
|
||||
}
|
||||
|
||||
currentStatus.setTumbling(true);
|
||||
currentStatus.getFlightData().addEvent(event);
|
||||
break;
|
||||
}
|
||||
|
@ -146,15 +146,33 @@ public class FlightEvent implements Comparable<FlightEvent> {
|
||||
|
||||
/**
|
||||
* Compares this event to another event depending on the event time. Secondary
|
||||
* sorting is performed on stages; lower (numerically higher) stage first. Tertiary
|
||||
* sorting is performed based on the event type ordinal.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(FlightEvent o) {
|
||||
|
||||
// first, sort on time
|
||||
if (this.time < o.time)
|
||||
return -1;
|
||||
if (this.time > o.time)
|
||||
return 1;
|
||||
|
||||
// second, sort on stage presence. Events with no source go first
|
||||
if ((this.getSource() == null) && (o.getSource() != null))
|
||||
return -1;
|
||||
if ((this.getSource() != null) && (o.getSource() == null))
|
||||
return 1;
|
||||
|
||||
// third, sort on stage order. Bigger stage number goes first
|
||||
if ((this.getSource() != null) && (o.getSource() != null)) {
|
||||
if (this.getSource().getStageNumber() > o.getSource().getStageNumber())
|
||||
return -1;
|
||||
if (this.getSource().getStageNumber() < o.getSource().getStageNumber())
|
||||
return 1;
|
||||
}
|
||||
|
||||
// finally, sort on event type
|
||||
return this.type.ordinal() - o.type.ordinal();
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ public class TestRockets {
|
||||
.setDiameter(0.029)
|
||||
.setLength(0.124)
|
||||
.setTimePoints(new double[] { 0, 1, 2 })
|
||||
.setThrustPoints(new double[] { 0, 1, 0 })
|
||||
.setThrustPoints(new double[] { 0, 20, 0 })
|
||||
.setCGPoints(new Coordinate[] {
|
||||
new Coordinate(.062, 0, 0, 0.123),new Coordinate(.062, 0, 0, .0935),new Coordinate(.062, 0, 0, 0.064)})
|
||||
.setDigest("digest G77 test")
|
||||
|
@ -31,11 +31,14 @@ import net.sf.openrocket.rocketcomponent.InnerTube;
|
||||
import net.sf.openrocket.rocketcomponent.MassComponent;
|
||||
import net.sf.openrocket.rocketcomponent.NoseCone;
|
||||
import net.sf.openrocket.rocketcomponent.Parachute;
|
||||
import net.sf.openrocket.rocketcomponent.PodSet;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.ShockCord;
|
||||
import net.sf.openrocket.rocketcomponent.Streamer;
|
||||
import net.sf.openrocket.rocketcomponent.TubeCoupler;
|
||||
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
|
||||
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
|
||||
import org.junit.Assert;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
@ -94,6 +97,72 @@ public class RockSimDocumentDTOTest extends RockSimTestBase {
|
||||
output.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests exporting a rocket with pods, and whether importing that same file results in the same pod configuration.
|
||||
*/
|
||||
@Test
|
||||
public void testPodsExport() throws Exception {
|
||||
OpenRocketDocument originalDocument = makePodsRocket();
|
||||
Rocket originalRocket = originalDocument.getRocket();
|
||||
|
||||
// Convert to RockSim XML
|
||||
String result = new RockSimSaver().marshalToRockSim(originalDocument);
|
||||
|
||||
// Write to .rkt file
|
||||
Path output = Files.createTempFile("podsRocket", ".rkt");
|
||||
Files.write(output, result.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Read the file
|
||||
RockSimLoader loader = new RockSimLoader();
|
||||
InputStream stream = new FileInputStream(output.toFile());
|
||||
Assert.assertNotNull("Could not open podsRocket.rkt", stream);
|
||||
OpenRocketDocument importedDocument = OpenRocketDocumentFactory.createEmptyRocket();
|
||||
DocumentLoadingContext context = new DocumentLoadingContext();
|
||||
context.setOpenRocketDocument(importedDocument);
|
||||
context.setMotorFinder(new DatabaseMotorFinder());
|
||||
loader.loadFromStream(context, new BufferedInputStream(stream));
|
||||
Rocket importedRocket = importedDocument.getRocket();
|
||||
|
||||
// Test children counts
|
||||
List<RocketComponent> originalChildren = originalRocket.getAllChildren();
|
||||
List<RocketComponent> importedChildren = importedRocket.getAllChildren();
|
||||
assertEquals(" Number of total children doesn't match",
|
||||
originalChildren.size(), importedChildren.size());
|
||||
assertEquals(" Number of rocket children doesn't match", 1, importedRocket.getChildCount());
|
||||
AxialStage stage = (AxialStage) importedRocket.getChild(0);
|
||||
assertEquals(" Number of stage children doesn't match", 2, stage.getChildCount());
|
||||
BodyTube tube = (BodyTube) stage.getChild(1);
|
||||
assertEquals(" Number of body tube children doesn't match", 3, tube.getChildCount());
|
||||
PodSet pod1 = (PodSet) tube.getChild(0);
|
||||
assertEquals(" Number of pod 1 children doesn't match", 1, pod1.getChildCount());
|
||||
PodSet pod2 = (PodSet) tube.getChild(1);
|
||||
assertEquals(" Number of pod 2 children doesn't match", 2, pod2.getChildCount());
|
||||
PodSet pod3 = (PodSet) tube.getChild(2);
|
||||
assertEquals(" Number of pod 3 children doesn't match", 0, pod3.getChildCount());
|
||||
|
||||
// Test component names
|
||||
for (int i = 1; i < originalChildren.size(); i++) {
|
||||
assertEquals(" Child " + i + " does not match",
|
||||
originalChildren.get(i).getName(), importedChildren.get(i).getName());
|
||||
}
|
||||
|
||||
// Test pod parameters
|
||||
assertEquals(-0.14, pod1.getAxialOffset(), 0.0001);
|
||||
assertEquals(0.065, pod1.getRadiusOffset(), 0.0001);
|
||||
assertEquals(Math.PI, pod1.getAngleOffset(), 0.0001);
|
||||
assertEquals(1, pod1.getInstanceCount());
|
||||
assertEquals(0.02, pod2.getAxialOffset(), 0.0001);
|
||||
assertEquals(0.025, pod2.getRadiusOffset(), 0.0001);
|
||||
assertEquals(- Math.PI / 2, pod2.getAngleOffset(), 0.0001);
|
||||
assertEquals(1, pod2.getInstanceCount());
|
||||
assertEquals(0.23, pod3.getAxialOffset(), 0.0001);
|
||||
assertEquals(0.06, pod3.getRadiusOffset(), 0.0001);
|
||||
assertEquals(Math.PI / 3, pod3.getAngleOffset(), 0.0001);
|
||||
assertEquals(1, pod3.getInstanceCount());
|
||||
|
||||
stream.close();
|
||||
Files.delete(output);
|
||||
}
|
||||
/**
|
||||
* Tests exporting a design where a tube coupler has children, which is not supported by RockSim, so the children
|
||||
* need to be moved outside the tube coupler.
|
||||
@ -142,19 +211,91 @@ public class RockSimDocumentDTOTest extends RockSimTestBase {
|
||||
Files.delete(output);
|
||||
}
|
||||
|
||||
private OpenRocketDocument makeTubeCouplerRocket() {
|
||||
private OpenRocketDocument makePodsRocket() {
|
||||
OpenRocketDocument document = OpenRocketDocumentFactory.createNewRocket();
|
||||
Rocket rocket = document.getRocket();
|
||||
AxialStage stage = rocket.getStage(0);
|
||||
|
||||
// Stage children
|
||||
NoseCone noseCone = new NoseCone();
|
||||
noseCone.setName("Nose Cone");
|
||||
stage.addChild(noseCone);
|
||||
BodyTube tube = new BodyTube();
|
||||
tube.setName("Body Tube");
|
||||
stage.addChild(tube);
|
||||
|
||||
// Body tube children
|
||||
PodSet pod1 = new PodSet();
|
||||
pod1.setName("Pod 1");
|
||||
tube.addChild(pod1);
|
||||
PodSet pod2 = new PodSet();
|
||||
pod2.setName("Pod 2");
|
||||
tube.addChild(pod2);
|
||||
PodSet pod3 = new PodSet();
|
||||
pod2.setName("Pod 3");
|
||||
tube.addChild(pod3);
|
||||
|
||||
// Pod 1 children
|
||||
NoseCone noseCone1 = new NoseCone();
|
||||
noseCone1.setName("Nose Cone 1");
|
||||
pod1.addChild(noseCone1);
|
||||
|
||||
// Pod 2 children
|
||||
NoseCone noseCone2 = new NoseCone();
|
||||
noseCone2.setName("Nose Cone 2");
|
||||
pod2.addChild(noseCone2);
|
||||
BodyTube tube2 = new BodyTube();
|
||||
tube2.setName("Body Tube 2");
|
||||
pod2.addChild(tube2);
|
||||
|
||||
// Set pod parameters
|
||||
pod1.setInstanceCount(1);
|
||||
pod2.setInstanceCount(2);
|
||||
pod3.setInstanceCount(3);
|
||||
|
||||
pod1.setAxialMethod(AxialMethod.ABSOLUTE);
|
||||
pod1.setAxialOffset(0.01);
|
||||
pod2.setAxialMethod(AxialMethod.TOP);
|
||||
pod2.setAxialOffset(0.02);
|
||||
pod3.setAxialMethod(AxialMethod.BOTTOM);
|
||||
pod3.setAxialOffset(0.03);
|
||||
|
||||
pod1.setRadiusMethod(RadiusMethod.RELATIVE);
|
||||
pod1.setRadiusOffset(0.015);
|
||||
pod2.setRadiusMethod(RadiusMethod.FREE);
|
||||
pod2.setRadiusOffset(0.025);
|
||||
pod3.setRadiusMethod(RadiusMethod.RELATIVE);
|
||||
pod3.setRadiusOffset(0.035);
|
||||
|
||||
pod1.setAngleOffset(Math.PI);
|
||||
pod2.setAngleOffset(- Math.PI / 2);
|
||||
pod3.setAngleOffset(Math.PI / 3);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private OpenRocketDocument makeTubeCouplerRocket() {
|
||||
OpenRocketDocument document = OpenRocketDocumentFactory.createNewRocket();
|
||||
Rocket rocket = document.getRocket();
|
||||
AxialStage stage = rocket.getStage(0);
|
||||
|
||||
// Stage children
|
||||
NoseCone noseCone = new NoseCone();
|
||||
noseCone.setName("Nose Cone");
|
||||
stage.addChild(noseCone);
|
||||
BodyTube tube = new BodyTube();
|
||||
tube.setName("Body Tube");
|
||||
stage.addChild(tube);
|
||||
|
||||
// Body tube children
|
||||
TubeCoupler coupler = new TubeCoupler();
|
||||
coupler.setName("Tube coupler 1");
|
||||
tube.addChild(coupler);
|
||||
TubeCoupler coupler3 = new TubeCoupler();
|
||||
coupler3.setName("Tube Coupler 3");
|
||||
tube.addChild(coupler3);
|
||||
|
||||
// Tube coupler 1 children
|
||||
InnerTube innerTube = new InnerTube();
|
||||
innerTube.setName("Inner Tube");
|
||||
coupler.addChild(innerTube);
|
||||
@ -182,9 +323,8 @@ public class RockSimDocumentDTOTest extends RockSimTestBase {
|
||||
MassComponent massComponent = new MassComponent();
|
||||
massComponent.setName("Mass Component");
|
||||
coupler.addChild(massComponent);
|
||||
TubeCoupler coupler3 = new TubeCoupler();
|
||||
coupler3.setName("Tube Coupler 3");
|
||||
tube.addChild(coupler3);
|
||||
|
||||
// Tube coupler 3 children
|
||||
Parachute parachute2 = new Parachute();
|
||||
parachute2.setName("Parachute 2");
|
||||
coupler3.addChild(parachute2);
|
||||
|
1277
core/test/net/sf/openrocket/file/rocksim/importt/PodTest.rkt
Normal file
1277
core/test/net/sf/openrocket/file/rocksim/importt/PodTest.rkt
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,10 +8,17 @@ import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.sf.openrocket.rocketcomponent.Bulkhead;
|
||||
import net.sf.openrocket.rocketcomponent.CenteringRing;
|
||||
import net.sf.openrocket.rocketcomponent.FreeformFinSet;
|
||||
import net.sf.openrocket.rocketcomponent.InnerTube;
|
||||
import net.sf.openrocket.rocketcomponent.MassComponent;
|
||||
import net.sf.openrocket.rocketcomponent.NoseCone;
|
||||
import net.sf.openrocket.rocketcomponent.Parachute;
|
||||
import net.sf.openrocket.rocketcomponent.PodSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.rocketcomponent.Transition;
|
||||
import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import org.junit.Assert;
|
||||
@ -212,7 +219,41 @@ public class RockSimLoaderTest extends BaseTestCase {
|
||||
Assert.assertEquals(0.185d, stage1.getOverrideMass(), 0.001);
|
||||
Assert.assertTrue(stage1.isCGOverridden());
|
||||
Assert.assertEquals(0.3d, stage1.getOverrideCG().x, 0.001);
|
||||
Assert.assertEquals(3, loader.getWarnings().size());
|
||||
Assert.assertEquals(2, loader.getWarnings().size());
|
||||
|
||||
NoseCone nc = (NoseCone) stage1.getChild(0);
|
||||
Assert.assertEquals(2, nc.getChildCount());
|
||||
Assert.assertEquals("Clay", nc.getChild(0).getName());
|
||||
RocketComponent it = nc.getChild(1);
|
||||
Assert.assertEquals(InnerTube.class, it.getClass());
|
||||
Assert.assertEquals("Attachment Rod", it.getName());
|
||||
|
||||
Assert.assertEquals(3, it.getChildCount());
|
||||
RocketComponent c = it.getChild(0);
|
||||
Assert.assertEquals(CenteringRing.class, c.getClass());
|
||||
Assert.assertEquals("Plate", c.getName());
|
||||
c = it.getChild(1);
|
||||
Assert.assertEquals(CenteringRing.class, c.getClass());
|
||||
Assert.assertEquals("Sleeve ", c.getName());
|
||||
c = it.getChild(2);
|
||||
Assert.assertEquals(Parachute.class, c.getClass());
|
||||
Assert.assertEquals("Nose Cone Parachute", c.getName());
|
||||
|
||||
BodyTube bt1 = (BodyTube) stage1.getChild(1);
|
||||
Assert.assertEquals(5, bt1.getChildCount());
|
||||
Assert.assertEquals("Centering ring", bt1.getChild(0).getName());
|
||||
Assert.assertEquals("Centering ring", bt1.getChild(1).getName());
|
||||
c = bt1.getChild(2);
|
||||
Assert.assertEquals(InnerTube.class, c.getClass());
|
||||
Assert.assertEquals("Body tube", c.getName());
|
||||
Assert.assertEquals("Launch lug", bt1.getChild(3).getName());
|
||||
Assert.assertEquals("Pod", bt1.getChild(4).getName());
|
||||
|
||||
PodSet pod = (PodSet) bt1.getChild(4);
|
||||
Assert.assertEquals(1, pod.getChildCount());
|
||||
c = pod.getChild(0);
|
||||
Assert.assertEquals(BodyTube.class, c.getClass());
|
||||
Assert.assertEquals("Body tube", pod.getChild(0).getName());
|
||||
|
||||
Assert.assertEquals(1, stage2.getChildCount());
|
||||
Assert.assertEquals("2nd Stage Tube", stage2.getChild(0).getName());
|
||||
@ -221,8 +262,8 @@ public class RockSimLoaderTest extends BaseTestCase {
|
||||
Assert.assertTrue(stage2.isCGOverridden());
|
||||
Assert.assertEquals(0.4d, stage2.getOverrideCG().x, 0.001);
|
||||
|
||||
BodyTube bt = (BodyTube) stage2.getChild(0);
|
||||
LaunchLug ll = (LaunchLug) bt.getChild(6);
|
||||
BodyTube bt2 = (BodyTube) stage2.getChild(0);
|
||||
LaunchLug ll = (LaunchLug) bt2.getChild(6);
|
||||
Assert.assertEquals(1.22d, ll.getAngleOffset(), 0.001);
|
||||
|
||||
Assert.assertEquals(2, stage3.getChildCount());
|
||||
@ -342,6 +383,97 @@ public class RockSimLoaderTest extends BaseTestCase {
|
||||
Assert.assertEquals(0, bodyTube3.getChildCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPodRocket() throws IOException, RocketLoadException{
|
||||
RockSimLoader loader = new RockSimLoader();
|
||||
OpenRocketDocument doc = loadRockSimRocket(loader, "PodTest.rkt");
|
||||
|
||||
Assert.assertNotNull(doc);
|
||||
Rocket rocket = doc.getRocket();
|
||||
Assert.assertNotNull(rocket);
|
||||
Assert.assertEquals("Pod Test", doc.getRocket().getName());
|
||||
Assert.assertEquals(3, loader.getWarnings().size());
|
||||
|
||||
InputStream stream = this.getClass().getResourceAsStream("PodTest.rkt");
|
||||
Assert.assertNotNull("Could not open PodTest.rkt", stream);
|
||||
|
||||
doc = OpenRocketDocumentFactory.createEmptyRocket();
|
||||
DocumentLoadingContext context = new DocumentLoadingContext();
|
||||
context.setOpenRocketDocument(doc);
|
||||
context.setMotorFinder(new DatabaseMotorFinder());
|
||||
loader.loadFromStream(context, new BufferedInputStream(stream));
|
||||
|
||||
Assert.assertNotNull(doc);
|
||||
rocket = doc.getRocket();
|
||||
Assert.assertNotNull(rocket);
|
||||
Assert.assertEquals(1, rocket.getStageCount());
|
||||
AxialStage stage1 = (AxialStage) rocket.getChild(0);
|
||||
|
||||
Assert.assertEquals(3, stage1.getChildCount());
|
||||
RocketComponent noseCone1 = stage1.getChild(0);
|
||||
RocketComponent bodyTube1 = stage1.getChild(1);
|
||||
RocketComponent transition1 = stage1.getChild(2);
|
||||
Assert.assertEquals(NoseCone.class, noseCone1.getClass());
|
||||
Assert.assertEquals(BodyTube.class, bodyTube1.getClass());
|
||||
Assert.assertEquals(Transition.class, transition1.getClass());
|
||||
Assert.assertEquals("Nose cone 1", noseCone1.getName());
|
||||
Assert.assertEquals("Body tube 1", bodyTube1.getName());
|
||||
Assert.assertEquals("Transition 1", transition1.getName());
|
||||
|
||||
Assert.assertEquals(1, noseCone1.getChildCount());
|
||||
RocketComponent component = noseCone1.getChild(0);
|
||||
Assert.assertEquals(MassComponent.class, component.getClass());
|
||||
Assert.assertEquals("Mass object 1", component.getName());
|
||||
|
||||
Assert.assertEquals(2, bodyTube1.getChildCount());
|
||||
RocketComponent pod2 = bodyTube1.getChild(0);
|
||||
Assert.assertEquals(PodSet.class, pod2.getClass());
|
||||
Assert.assertEquals("Pod 2", pod2.getName());
|
||||
component = bodyTube1.getChild(1);
|
||||
Assert.assertEquals(Bulkhead.class, component.getClass());
|
||||
Assert.assertEquals("Bulkhead 1", component.getName());
|
||||
|
||||
Assert.assertEquals(3, pod2.getChildCount());
|
||||
RocketComponent noseCone2 = pod2.getChild(0);
|
||||
Assert.assertEquals(NoseCone.class, noseCone2.getClass());
|
||||
Assert.assertEquals("Nose cone 2", noseCone2.getName());
|
||||
RocketComponent bodyTube2 = pod2.getChild(1);
|
||||
Assert.assertEquals(BodyTube.class, bodyTube2.getClass());
|
||||
Assert.assertEquals("Body tube 2", bodyTube2.getName());
|
||||
component = pod2.getChild(2);
|
||||
Assert.assertEquals(Transition.class, component.getClass());
|
||||
Assert.assertEquals("Transition 2", component.getName());
|
||||
|
||||
Assert.assertEquals(1, noseCone2.getChildCount());
|
||||
component = noseCone2.getChild(0);
|
||||
Assert.assertEquals(MassComponent.class, component.getClass());
|
||||
Assert.assertEquals("Mass object 2", component.getName());
|
||||
|
||||
Assert.assertEquals(3, bodyTube2.getChildCount());
|
||||
component = bodyTube2.getChild(0);
|
||||
Assert.assertEquals(TrapezoidFinSet.class, component.getClass());
|
||||
Assert.assertEquals("Fin set 2", component.getName());
|
||||
RocketComponent pod3 = bodyTube2.getChild(1);
|
||||
Assert.assertEquals(PodSet.class, pod3.getClass());
|
||||
Assert.assertEquals("Pod 3", pod3.getName());
|
||||
component = bodyTube2.getChild(2);
|
||||
Assert.assertEquals(LaunchLug.class, component.getClass());
|
||||
Assert.assertEquals("Launch lug 1", component.getName());
|
||||
|
||||
Assert.assertEquals(1, pod3.getChildCount());
|
||||
component = pod3.getChild(0);
|
||||
Assert.assertEquals(BodyTube.class, component.getClass());
|
||||
Assert.assertEquals("Body tube 3", component.getName());
|
||||
Assert.assertEquals(0.04, pod3.getAxialOffset(), MathUtil.EPSILON);
|
||||
Assert.assertEquals(Math.PI / 2, pod3.getAngleOffset(), 0.0001);
|
||||
Assert.assertEquals(0.05, pod3.getRadiusOffset(), MathUtil.EPSILON);
|
||||
|
||||
Assert.assertEquals(1, transition1.getChildCount());
|
||||
component = transition1.getChild(0);
|
||||
Assert.assertEquals(MassComponent.class, component.getClass());
|
||||
Assert.assertEquals("Mass object 3", component.getName());
|
||||
}
|
||||
|
||||
public static OpenRocketDocument loadRockSimRocket(RockSimLoader theLoader, String fileName) throws IOException, RocketLoadException {
|
||||
try (InputStream stream = RockSimLoaderTest.class.getResourceAsStream(fileName)) {
|
||||
Assert.assertNotNull("Could not open " + fileName, stream);
|
||||
|
@ -6,8 +6,6 @@ import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.simulation.exception.MotorIgnitionException;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
import net.sf.openrocket.simulation.listeners.SimulationListener;
|
||||
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
|
||||
import net.sf.openrocket.util.TestRockets;
|
||||
import org.junit.Assert;
|
||||
@ -35,11 +33,9 @@ public class DisableStageTest extends BaseTestCase {
|
||||
simDisabled.getOptions().setISAAtmosphere(true);
|
||||
simDisabled.getOptions().setTimeStep(0.05);
|
||||
|
||||
SimulationListener simulationListener = new AbstractSimulationListener();
|
||||
|
||||
// Since there are no stages, the simulation should throw an exception.
|
||||
try {
|
||||
simDisabled.simulate(simulationListener);
|
||||
simDisabled.simulate();
|
||||
} catch (SimulationException e) {
|
||||
if (!(e instanceof MotorIgnitionException)) {
|
||||
Assert.fail("Simulation should have thrown a MotorIgnitionException");
|
||||
@ -56,7 +52,7 @@ public class DisableStageTest extends BaseTestCase {
|
||||
|
||||
simDisabled.getActiveConfiguration().setAllStages(); // Re-enable all stages.
|
||||
|
||||
compareSims(simOriginal, simDisabled, simulationListener, delta);
|
||||
compareSims(simOriginal, simDisabled, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,9 +79,7 @@ public class DisableStageTest extends BaseTestCase {
|
||||
simDisabled.getOptions().setISAAtmosphere(true);
|
||||
simDisabled.getOptions().setTimeStep(0.05);
|
||||
|
||||
SimulationListener simulationListener = new AbstractSimulationListener();
|
||||
|
||||
compareSims(simRemoved, simDisabled, simulationListener, delta);
|
||||
compareSims(simRemoved, simDisabled, delta);
|
||||
|
||||
//// Test re-enableing the stage.
|
||||
Rocket rocketOriginal = TestRockets.makeBeta();
|
||||
@ -96,7 +90,7 @@ public class DisableStageTest extends BaseTestCase {
|
||||
|
||||
simDisabled.getActiveConfiguration().setAllStages();
|
||||
|
||||
compareSims(simOriginal, simDisabled, simulationListener, delta);
|
||||
compareSims(simOriginal, simDisabled, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,9 +167,7 @@ public class DisableStageTest extends BaseTestCase {
|
||||
simDisabled.getOptions().setISAAtmosphere(true);
|
||||
simDisabled.getOptions().setTimeStep(0.05);
|
||||
|
||||
SimulationListener simulationListener = new AbstractSimulationListener();
|
||||
|
||||
compareSims(simRemoved, simDisabled, simulationListener, delta);
|
||||
compareSims(simRemoved, simDisabled, delta);
|
||||
|
||||
//// Test re-enableing the stage.
|
||||
Rocket rocketOriginal = TestRockets.makeFalcon9Heavy();
|
||||
@ -186,7 +178,7 @@ public class DisableStageTest extends BaseTestCase {
|
||||
|
||||
simDisabled.getActiveConfiguration().setAllStages();
|
||||
|
||||
compareSims(simOriginal, simDisabled, simulationListener, delta);
|
||||
compareSims(simOriginal, simDisabled, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,11 +206,9 @@ public class DisableStageTest extends BaseTestCase {
|
||||
simDisabled.getOptions().setISAAtmosphere(true);
|
||||
simDisabled.getOptions().setTimeStep(0.05);
|
||||
|
||||
SimulationListener simulationListener = new AbstractSimulationListener();
|
||||
|
||||
// There should be no motors left at this point, so a no motors exception should be thrown
|
||||
try {
|
||||
simRemoved.simulate(simulationListener);
|
||||
simRemoved.simulate();
|
||||
} catch (SimulationException e) {
|
||||
if (!(e instanceof MotorIgnitionException)) {
|
||||
Assert.fail("Simulation failed: " + e);
|
||||
@ -226,7 +216,7 @@ public class DisableStageTest extends BaseTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
simDisabled.simulate(simulationListener);
|
||||
simDisabled.simulate();
|
||||
} catch (SimulationException e) {
|
||||
if (!(e instanceof MotorIgnitionException)) {
|
||||
Assert.fail("Simulation failed: " + e);
|
||||
@ -242,7 +232,7 @@ public class DisableStageTest extends BaseTestCase {
|
||||
|
||||
simDisabled.getActiveConfiguration().setAllStages();
|
||||
|
||||
compareSims(simOriginal, simDisabled, simulationListener, delta);
|
||||
compareSims(simOriginal, simDisabled, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,13 +249,11 @@ public class DisableStageTest extends BaseTestCase {
|
||||
* - groundHitVelocity
|
||||
* @param simExpected the expected simulation results
|
||||
* @param simActual the actual simulation results
|
||||
* @param simulationListener the simulation listener to use for the comparison
|
||||
* @param delta the error margin for the comparison (e.g. 0.05 = 5 % error margin)
|
||||
*/
|
||||
private void compareSims(Simulation simExpected, Simulation simActual,
|
||||
SimulationListener simulationListener, double delta) {
|
||||
private void compareSims(Simulation simExpected, Simulation simActual, double delta) {
|
||||
try {
|
||||
simExpected.simulate(simulationListener);
|
||||
simExpected.simulate();
|
||||
double maxAltitudeOriginal = simExpected.getSimulatedData().getMaxAltitude();
|
||||
double maxVelocityOriginal = simExpected.getSimulatedData().getMaxVelocity();
|
||||
double maxMachNumberOriginal = simExpected.getSimulatedData().getMaxMachNumber();
|
||||
@ -274,7 +262,7 @@ public class DisableStageTest extends BaseTestCase {
|
||||
double launchRodVelocityOriginal = simExpected.getSimulatedData().getLaunchRodVelocity();
|
||||
double deploymentVelocityOriginal = simExpected.getSimulatedData().getDeploymentVelocity();
|
||||
|
||||
simActual.simulate(simulationListener);
|
||||
simActual.simulate();
|
||||
double maxAltitudeDisabled = simActual.getSimulatedData().getMaxAltitude();
|
||||
double maxVelocityDisabled = simActual.getSimulatedData().getMaxVelocity();
|
||||
double maxMachNumberDisabled = simActual.getSimulatedData().getMaxMachNumber();
|
||||
|
107
core/test/net/sf/openrocket/simulation/FlightEventsTest.java
Normal file
107
core/test/net/sf/openrocket/simulation/FlightEventsTest.java
Normal file
@ -0,0 +1,107 @@
|
||||
package net.sf.openrocket.simulation;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfigurationId;
|
||||
import net.sf.openrocket.rocketcomponent.Rocket;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
|
||||
import net.sf.openrocket.util.TestRockets;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
* Tests to verify that simulations contain all the expected flight events.
|
||||
*/
|
||||
public class FlightEventsTest extends BaseTestCase {
|
||||
/**
|
||||
* Tests for a single stage design.
|
||||
*/
|
||||
@Test
|
||||
public void testSingleStage() throws SimulationException {
|
||||
Rocket rocket = TestRockets.makeEstesAlphaIII();
|
||||
Simulation sim = new Simulation(rocket);
|
||||
sim.getOptions().setISAAtmosphere(true);
|
||||
sim.getOptions().setTimeStep(0.05);
|
||||
sim.setFlightConfigurationId(TestRockets.TEST_FCID_0);
|
||||
|
||||
sim.simulate();
|
||||
|
||||
// Test branch count
|
||||
int branchCount = sim.getSimulatedData().getBranchCount();
|
||||
assertEquals(" Single stage simulation invalid branch count", 1, branchCount);
|
||||
|
||||
FlightEvent.Type[] expectedEventTypes = {FlightEvent.Type.LAUNCH, FlightEvent.Type.IGNITION, FlightEvent.Type.LIFTOFF,
|
||||
FlightEvent.Type.LAUNCHROD, FlightEvent.Type.BURNOUT, FlightEvent.Type.EJECTION_CHARGE, FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT,
|
||||
FlightEvent.Type.APOGEE, FlightEvent.Type.GROUND_HIT, FlightEvent.Type.SIMULATION_END};
|
||||
|
||||
// Test event count
|
||||
FlightDataBranch branch = sim.getSimulatedData().getBranch(0);
|
||||
List<FlightEvent> eventList = branch.getEvents();
|
||||
List<FlightEvent.Type> eventTypes = eventList.stream().map(FlightEvent::getType).collect(Collectors.toList());
|
||||
assertEquals(" Single stage simulation invalid number of events", expectedEventTypes.length, eventTypes.size());
|
||||
|
||||
// Test that all expected events are present, and in the right order
|
||||
for (int i = 0; i < expectedEventTypes.length; i++) {
|
||||
assertSame(" Flight type " + expectedEventTypes[i] + " not found in single stage simulation",
|
||||
eventTypes.get(i), expectedEventTypes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for a multi-stage design.
|
||||
*/
|
||||
@Test
|
||||
public void testMultiStage() throws SimulationException {
|
||||
Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
Simulation sim = new Simulation(rocket);
|
||||
sim.getOptions().setISAAtmosphere(true);
|
||||
sim.getOptions().setTimeStep(0.05);
|
||||
rocket.getSelectedConfiguration().setAllStages();
|
||||
FlightConfigurationId fcid = rocket.getSelectedConfiguration().getFlightConfigurationID();
|
||||
sim.setFlightConfigurationId(fcid);
|
||||
|
||||
sim.simulate();
|
||||
|
||||
// Test branch count
|
||||
int branchCount = sim.getSimulatedData().getBranchCount();
|
||||
assertEquals(" Multi-stage simulation invalid branch count", 3, branchCount);
|
||||
|
||||
for (int b = 0; b < 3; b++) {
|
||||
FlightEvent.Type[] expectedEventTypes;
|
||||
switch (b) {
|
||||
case 0:
|
||||
expectedEventTypes = new FlightEvent.Type[]{FlightEvent.Type.LAUNCH, FlightEvent.Type.IGNITION, FlightEvent.Type.IGNITION,
|
||||
FlightEvent.Type.LIFTOFF, FlightEvent.Type.LAUNCHROD, FlightEvent.Type.APOGEE,
|
||||
FlightEvent.Type.BURNOUT, FlightEvent.Type.EJECTION_CHARGE, FlightEvent.Type.STAGE_SEPARATION,
|
||||
FlightEvent.Type.BURNOUT, FlightEvent.Type.EJECTION_CHARGE, FlightEvent.Type.STAGE_SEPARATION,
|
||||
FlightEvent.Type.TUMBLE, FlightEvent.Type.GROUND_HIT, FlightEvent.Type.SIMULATION_END};
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
expectedEventTypes = new FlightEvent.Type[]{FlightEvent.Type.TUMBLE, FlightEvent.Type.GROUND_HIT,
|
||||
FlightEvent.Type.SIMULATION_END};
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Invalid branch number " + b);
|
||||
}
|
||||
|
||||
// Test event count
|
||||
FlightDataBranch branch = sim.getSimulatedData().getBranch(b);
|
||||
List<FlightEvent> eventList = branch.getEvents();
|
||||
List<FlightEvent.Type> eventTypes = eventList.stream().map(FlightEvent::getType).collect(Collectors.toList());
|
||||
assertEquals(" Multi-stage simulation, branch " + b + " invalid number of events", expectedEventTypes.length, eventTypes.size());
|
||||
|
||||
// Test that all expected events are present, and in the right order
|
||||
for (int i = 0; i < expectedEventTypes.length; i++) {
|
||||
assertSame(" Flight type " + expectedEventTypes[i] + ", branch " + b + " not found in multi-stage simulation",
|
||||
eventTypes.get(i), expectedEventTypes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
@ -13,6 +13,7 @@ import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -210,9 +211,16 @@ public class IntegerModel implements StateChangeListener {
|
||||
* Sets the value of the variable.
|
||||
*/
|
||||
public void setValue(int v) {
|
||||
int clampedValue = MathUtil.clamp(v, minValue, maxValue);
|
||||
if (clampedValue != v) {
|
||||
log.debug("Clamped value " + v + " to " + clampedValue + " for " + this);
|
||||
v = clampedValue;
|
||||
}
|
||||
|
||||
log.debug("Setting value " + v + " for " + this);
|
||||
try {
|
||||
setMethod.invoke(source, v);
|
||||
fireStateChanged();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BugException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
|
@ -86,7 +86,7 @@ public class ComponentAssemblyConfig extends RocketComponentConfig {
|
||||
// set angle
|
||||
JLabel angleLabel = new JLabel(trans.get("StageConfig.parallel.angle"));
|
||||
motherPanel.add( angleLabel, "align left");
|
||||
DoubleModel angleModel = new DoubleModel( boosters, "AngleOffset", 1.0, UnitGroup.UNITS_ANGLE, 0.0, Math.PI*2);
|
||||
DoubleModel angleModel = new DoubleModel( boosters, "AngleOffset", 1.0, UnitGroup.UNITS_ANGLE, -Math.PI, Math.PI);
|
||||
|
||||
JSpinner angleSpinner = new JSpinner(angleModel.getSpinnerModel());
|
||||
angleSpinner.setEditor(new SpinnerEditor(angleSpinner));
|
||||
|
@ -199,6 +199,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
Object c = stabData.get(row).name;
|
||||
|
||||
return c.toString();
|
||||
}
|
||||
|
||||
@ -237,7 +238,7 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int row) {
|
||||
return NOUNIT.toString(stabData.get(row).cpx);
|
||||
return unit.toString(stabData.get(row).cpx);
|
||||
}
|
||||
},
|
||||
new Column("<html>C<sub>N<sub>" + ALPHA + "</sub></sub>") {
|
||||
@ -541,11 +542,6 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
rollData.clear();
|
||||
|
||||
for(final RocketComponent comp: configuration.getAllComponents()) {
|
||||
// // this is actually redundant, because the analysis will not contain inactive stages.
|
||||
// if (!configuration.isComponentActive(comp)) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
CMAnalysisEntry cmEntry = cmMap.get(comp.hashCode());
|
||||
if (null == cmEntry) {
|
||||
log.warn("Could not find massData entry for component: " + comp.getName());
|
||||
@ -556,7 +552,11 @@ public class ComponentAnalysisDialog extends JDialog implements StateChangeListe
|
||||
continue;
|
||||
}
|
||||
|
||||
LongitudinalStabilityRow row = new LongitudinalStabilityRow(cmEntry.name, cmEntry.source);
|
||||
String name = cmEntry.name;
|
||||
if (cmEntry.source instanceof Rocket) {
|
||||
name = trans.get("componentanalysisdlg.TOTAL");
|
||||
}
|
||||
LongitudinalStabilityRow row = new LongitudinalStabilityRow(name, cmEntry.source);
|
||||
stabData.add(row);
|
||||
|
||||
row.source = cmEntry.source;
|
||||
|
@ -504,8 +504,7 @@ public class SimulationPanel extends JPanel {
|
||||
|
||||
private void openDialog(final Simulation sim) {
|
||||
boolean plotMode = false;
|
||||
if (sim.hasSimulationData() && (sim.getStatus() == Status.UPTODATE || sim.getStatus() == Status.LOADED
|
||||
|| sim.getStatus() == Status.EXTERNAL)) {
|
||||
if (sim.hasSimulationData() && Simulation.isStatusUpToDate(sim.getStatus())) {
|
||||
plotMode = true;
|
||||
}
|
||||
openDialog(plotMode, sim);
|
||||
|
@ -60,7 +60,7 @@ import org.jfree.ui.RectangleInsets;
|
||||
import org.jfree.ui.TextAnchor;
|
||||
|
||||
/*
|
||||
* It should be possible to simplify this code quite a bit by using a single Renderer instance for
|
||||
* TODO: It should be possible to simplify this code quite a bit by using a single Renderer instance for
|
||||
* both datasets and the legend. But for now, the renderers are queried for the line color information
|
||||
* and this is held in the Legend.
|
||||
*/
|
||||
|
@ -857,8 +857,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
// Re-run the present simulation(s)
|
||||
List<Simulation> sims = new LinkedList<>();
|
||||
for (Simulation sim : document.getSimulations()) {
|
||||
if (sim.getStatus() == Simulation.Status.UPTODATE || sim.getStatus() == Simulation.Status.LOADED
|
||||
|| !document.getRocket().getFlightConfiguration(sim.getFlightConfigurationId()).hasMotors())
|
||||
if (Simulation.isStatusUpToDate(sim.getStatus()) ||
|
||||
!document.getRocket().getFlightConfiguration(sim.getFlightConfigurationId()).hasMotors())
|
||||
continue;
|
||||
|
||||
// Find a Simulation based on the current flight configuration
|
||||
|
@ -292,8 +292,7 @@ public class SimulationEditDialog extends JDialog {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
// If the simulation is out of date, run the simulation.
|
||||
if (simulationList[0].getStatus() != Simulation.Status.UPTODATE &&
|
||||
simulationList[0].getStatus() != Simulation.Status.LOADED) {
|
||||
if (!Simulation.isStatusUpToDate(simulationList[0].getStatus())) {
|
||||
new SimulationRunDialog(SimulationEditDialog.this.parentWindow, document, simulationList[0]).setVisible(true);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user