[#2048] Support inline, flush assemblies in prev/next sym comp

This commit is contained in:
SiboVG 2023-02-21 03:15:47 +01:00
parent 09918a3d22
commit 074fee3663
6 changed files with 1069 additions and 456 deletions

View File

@ -1296,6 +1296,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
mutex.verify();
return 0;
}
public double getRadiusOffset(RadiusMethod method) {
double radius = getRadiusMethod().getRadius(parent, this, getRadiusOffset());
return method.getAsOffset(parent, this, radius);
}
public RadiusMethod getRadiusMethod() {
return RadiusMethod.COAXIAL;
@ -2059,20 +2064,37 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
}
/**
* Return all the component assemblies that are a child of this component
* @return list of ComponentAssembly components that are a child of this component
* Return all the component assemblies that are a direct/indirect child of this component
* @return list of ComponentAssembly components that are a direct/indirect child of this component
*/
public final List<RocketComponent> getChildAssemblies() {
public final List<ComponentAssembly> getAllChildAssemblies() {
checkState();
Iterator<RocketComponent> children = iterator(false);
List<RocketComponent> result = new ArrayList<>();
List<ComponentAssembly> result = new ArrayList<>();
while (children.hasNext()) {
RocketComponent child = children.next();
if (child instanceof ComponentAssembly) {
result.add(child);
result.add((ComponentAssembly) child);
}
}
return result;
}
/**
* Return all the component assemblies that are a direct child of this component
* @return list of ComponentAssembly components that are a direct child of this component
*/
public final List<ComponentAssembly> getDirectChildAssemblies() {
checkState();
List<ComponentAssembly> result = new ArrayList<>();
for (RocketComponent child : this.getChildren()) {
if (child instanceof ComponentAssembly) {
result.add((ComponentAssembly) child);
}
}
return result;

View File

@ -7,10 +7,10 @@ import java.util.Collection;
import java.util.List;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.util.BoundingBox;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
/**
* Class for an axially symmetric rocket component generated by rotating
@ -622,7 +622,8 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
while (0 <= searchSiblingIndex) {
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
if ((searchSibling instanceof SymmetricComponent) && inline(searchSibling)) {
return (SymmetricComponent) searchSibling;
return getPreviousSymmetricComponentFromComponentAssembly((SymmetricComponent) searchSibling,
(SymmetricComponent) searchSibling, 0);
}
--searchSiblingIndex;
}
@ -636,14 +637,81 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
}
}
// one last thing -- I could be the child of a PodSet, and in line with
// one last thing -- I could be the child of a ComponentAssembly, and in line with
// the SymmetricComponent that is my grandParent
if ((grandParent instanceof SymmetricComponent) && inline(grandParent)) {
return (SymmetricComponent) grandParent;
// If the grandparent is actually before me, then this is the previous component
if ((parent.getAxialOffset(AxialMethod.TOP) + getAxialOffset(AxialMethod.TOP)) > 0) {
return (SymmetricComponent) grandParent;
}
// If not, then search for the component before the grandparent
else {
// NOTE: will be incorrect if the ComponentAssembly is even further to the front than the
// previous component of the grandparent. But that would be really bad rocket design...
return ((SymmetricComponent) grandParent).getPreviousSymmetricComponent();
}
}
return null;
}
/**
* Checks if parent has component assemblies that have potential previous components.
* A child symmetric component of a component assembly is a potential previous component if:
* - it is inline with the parent
* - it is flush with the end of the parent
* - it is larger in aft radius than the parent
* @param parent parent component to check for child component assemblies
* @param previous the current previous component candidate
* @param flushOffset an extra offset to be added to check for flushness. This is used when recursively running this
* method to check children of children of the original parent are flush with the end of the
* original parent.
* @return the previous component if it is found
*/
private SymmetricComponent getPreviousSymmetricComponentFromComponentAssembly(SymmetricComponent parent,
SymmetricComponent previous, double flushOffset) {
if (previous == null) {
return parent;
}
if (parent == null) {
return previous;
}
double maxRadius = previous.isAftRadiusAutomatic() ? 0 : previous.getAftRadius();
SymmetricComponent previousComponent = previous;
for (ComponentAssembly assembly : parent.getDirectChildAssemblies()) {
if (assembly.getChildCount() == 0) {
continue;
}
/*
* Check if the component assembly's last child is a symmetric component that is:
* - inline with the parent
* - flush with the end of the parent
* - larger in aft radius than the parent
* in that case, this component assembly is the new previousComponent.
*/
RocketComponent lastChild = assembly.getChild(assembly.getChildCount() - 1);
if (!( (lastChild instanceof SymmetricComponent) && parent.inline(lastChild) )) {
continue;
}
SymmetricComponent lastSymmetricChild = (SymmetricComponent) lastChild;
double flushDeviation = flushOffset + assembly.getAxialOffset(AxialMethod.BOTTOM); // How much the last child is flush with the parent
// If the last symmetric child from the assembly if flush with the end of the parent and larger than the
// current previous component, then this is the new previous component
if (MathUtil.equals(flushDeviation, 0) && !lastSymmetricChild.isAftRadiusAutomatic() &&
lastSymmetricChild.getAftRadius() > maxRadius) {
previousComponent = lastSymmetricChild;
maxRadius = previousComponent.getAftRadius();
}
// It could be that there is a child component assembly that is flush with the end of the parent or larger
// Recursively check assembly's children
previousComponent = getPreviousSymmetricComponentFromComponentAssembly(lastSymmetricChild, previousComponent, flushDeviation);
maxRadius = previousComponent != null ? previousComponent.getAftRadius() : maxRadius;
}
return previousComponent;
}
/**
* Return the next symmetric component, or null if none exists.
@ -670,7 +738,8 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
while (searchSiblingIndex < searchParent.getChildCount()) {
final RocketComponent searchSibling = searchParent.getChild(searchSiblingIndex);
if ((searchSibling instanceof SymmetricComponent) && inline(searchSibling)) {
return (SymmetricComponent) searchSibling;
return getNextSymmetricComponentFromComponentAssembly((SymmetricComponent) searchSibling,
(SymmetricComponent) searchSibling, 0);
}
++searchSiblingIndex;
}
@ -681,13 +750,22 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
searchSiblingIndex = 0;
}
// One last thing -- I could have a child that is a PodSet that is in line
// One last thing -- I could have a child that is a ComponentAssembly that is in line
// with me
for (RocketComponent child : getChildren()) {
if (child instanceof PodSet) {
if (child instanceof ComponentAssembly) {
for (RocketComponent grandchild : child.getChildren()) {
if ((grandchild instanceof SymmetricComponent) && inline(grandchild)) {
return (SymmetricComponent) grandchild;
// If the grandparent is actually after me, then this is the next component
if ((parent.getAxialOffset(AxialMethod.BOTTOM) + getAxialOffset(AxialMethod.BOTTOM)) < 0) {
return (SymmetricComponent) grandchild;
}
// If not, then search for the component after the grandparent
else {
// NOTE: will be incorrect if the ComponentAssembly is even further to the back than the
// next component of the grandparent. But that would be really bad rocket design...
return ((SymmetricComponent) grandchild).getNextSymmetricComponent();
}
}
}
}
@ -696,6 +774,64 @@ public abstract class SymmetricComponent extends BodyComponent implements BoxBou
return null;
}
/**
* Checks if parent has component assemblies that have potential next components.
* A child symmetric component of a component assembly is a potential next component if:
* - it is inline with the parent
* - it is flush with the front of the parent
* - it is larger in fore radius than the parent
* @param parent parent component to check for child component assemblies
* @param next the next previous component candidate
* @param flushOffset an extra offset to be added to check for flushness. This is used when recursively running this
* method to check children of children of the original parent are flush with the front of the
* original parent.
* @return the next component if it is found
*/
private SymmetricComponent getNextSymmetricComponentFromComponentAssembly(SymmetricComponent parent,
SymmetricComponent next, double flushOffset) {
if (next == null) {
return parent;
}
if (parent == null) {
return next;
}
double maxRadius = next.isForeRadiusAutomatic() ? 0 : next.getForeRadius();
SymmetricComponent nextComponent = next;
for (ComponentAssembly assembly : parent.getDirectChildAssemblies()) {
if (assembly.getChildCount() == 0) {
continue;
}
/*
* Check if the component assembly's last child is a symmetric component that is:
* - inline with the parent
* - flush with the front of the parent
* - larger in fore radius than the parent
* in that case, this component assembly is the new nextComponent.
*/
RocketComponent firstChild = assembly.getChild(0);
if (!( (firstChild instanceof SymmetricComponent) && parent.inline(firstChild) )) {
continue;
}
SymmetricComponent firstSymmetricChild = (SymmetricComponent) firstChild;
double flushDeviation = flushOffset + assembly.getAxialOffset(AxialMethod.TOP); // How much the last child is flush with the parent
// If the first symmetric child from the assembly if flush with the front of the parent and larger than the
// current next component, then this is the new next component
if (MathUtil.equals(flushDeviation, 0) && !firstSymmetricChild.isForeRadiusAutomatic() &&
firstSymmetricChild.getForeRadius() > maxRadius) {
nextComponent = firstSymmetricChild;
maxRadius = nextComponent.getForeRadius();
}
// It could be that there is a child component assembly that is flush with the front of the parent or larger
// Recursively check assembly's children
nextComponent = getNextSymmetricComponentFromComponentAssembly(firstSymmetricChild, nextComponent, flushDeviation);
maxRadius = nextComponent != null ? nextComponent.getForeRadius() : maxRadius;
}
return nextComponent;
}
/***
* Determine whether a candidate symmetric component is in line with us
*

View File

@ -469,7 +469,6 @@ public class BarrowmanCalculatorTest {
final FlightConfiguration testConfig = testRocket.getSelectedConfiguration();
final FlightConditions testConditions = new FlightConditions(testConfig);
TestRockets.dumpRocket(testRocket, "/home/joseph/rockets/openrocket/git/openrocket/work/testrocket.ork");
final BarrowmanCalculator testCalc = new BarrowmanCalculator();
double testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
final AerodynamicForces testForces = testCalc.getAerodynamicForces(testConfig, testConditions, warnings);
@ -487,8 +486,15 @@ public class BarrowmanCalculatorTest {
// move the pod forward.
warnings.clear();
pod.setAxialOffset(pod.getAxialOffset() - 0.2);
testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
pod.setAxialOffset(pod.getAxialOffset() - 0.3);
testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
assertFalse("should be warning from airframe overlap", warnings.isEmpty());
// move the pod back.
warnings.clear();
pod.setAxialOffset(pod.getAxialOffset() + 0.1);
TestRockets.dumpRocket(testRocket, "/Users/SiboVanGool/Downloads/sfs/test.ork");
testCP = testCalc.getCP(testConfig, testConditions, warnings).x;
assertFalse("should be warning from airframe overlap", warnings.isEmpty());
}

View File

@ -0,0 +1,511 @@
package net.sf.openrocket.rocketcomponent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import net.sf.openrocket.material.Material;
import net.sf.openrocket.rocketcomponent.position.AxialMethod;
import net.sf.openrocket.rocketcomponent.position.RadiusMethod;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import net.sf.openrocket.util.TestRockets;
import org.junit.Test;
public class SymmetricComponentTest extends BaseTestCase {
@Test
public void testPreviousSymmetricComponent() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
AxialStage payloadStage = rocket.getStage(0);
NoseCone payloadFairingNoseCone = (NoseCone) payloadStage.getChild(0);
BodyTube payloadBody = (BodyTube) payloadStage.getChild(1);
Transition payloadFairingTail = (Transition) payloadStage.getChild(2);
BodyTube upperStageBody = (BodyTube) payloadStage.getChild(3);
BodyTube interstageBody = (BodyTube) payloadStage.getChild(4);
assertNull(payloadFairingNoseCone.getPreviousSymmetricComponent());
assertEquals(payloadFairingNoseCone, payloadBody.getPreviousSymmetricComponent());
assertEquals(payloadBody, payloadFairingTail.getPreviousSymmetricComponent());
assertEquals(payloadFairingTail, upperStageBody.getPreviousSymmetricComponent());
assertEquals(upperStageBody, interstageBody.getPreviousSymmetricComponent());
AxialStage coreStage = rocket.getStage(1);
BodyTube coreBody = (BodyTube) coreStage.getChild(0);
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
NoseCone boosterCone = (NoseCone) boosterStage.getChild(0);
BodyTube boosterBody = (BodyTube) boosterStage.getChild(1);
assertNull(boosterCone.getPreviousSymmetricComponent());
assertEquals(boosterCone, boosterBody.getPreviousSymmetricComponent());
}
@Test
public void testPreviousSymmetricComponentInlineComponentAssembly() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
// Stage 0
AxialStage payloadStage = rocket.getStage(0);
NoseCone payloadFairingNoseCone = (NoseCone) payloadStage.getChild(0);
BodyTube payloadBody = (BodyTube) payloadStage.getChild(1);
Transition payloadFairingTail = (Transition) payloadStage.getChild(2);
BodyTube upperStageBody = (BodyTube) payloadStage.getChild(3);
BodyTube interstageBody = (BodyTube) payloadStage.getChild(4);
// Stage 1
AxialStage coreStage = rocket.getStage(1);
BodyTube coreBody = (BodyTube) coreStage.getChild(0);
// Booster stage
ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
boosterStage.setInstanceCount(1);
boosterStage.setRadius(RadiusMethod.RELATIVE, 0);
NoseCone boosterCone = (NoseCone) boosterStage.getChild(0);
BodyTube boosterBody = (BodyTube) boosterStage.getChild(1);
// Add inline pod set
PodSet podSet = new PodSet();
podSet.setName("Inline Pod Set");
podSet.setInstanceCount(1);
podSet.setRadius(RadiusMethod.FREE, 0);
coreBody.addChild(podSet);
podSet.setAxialOffset(AxialMethod.BOTTOM, 0);
NoseCone podSetCone = new NoseCone();
podSetCone.setLength(0.1);
podSetCone.setBaseRadius(0.05);
podSet.addChild(podSetCone);
BodyTube podSetBody = new BodyTube(0.2, 0.05, 0.001);
podSetBody.setName("Pod Set Body");
podSet.addChild(podSetBody);
TrapezoidFinSet finSet = new TrapezoidFinSet();
podSetBody.addChild(finSet);
// Add last stage
AxialStage lastStage = new AxialStage();
BodyTube lastStageBody = new BodyTube(0.2, 0.05, 0.001);
lastStageBody.setName("Last Stage Body");
lastStage.addChild(lastStageBody);
rocket.addChild(lastStage);
assertNull(payloadFairingNoseCone.getPreviousSymmetricComponent());
assertEquals(payloadFairingNoseCone, payloadBody.getPreviousSymmetricComponent());
assertEquals(payloadBody, payloadFairingTail.getPreviousSymmetricComponent());
assertEquals(payloadFairingTail, upperStageBody.getPreviousSymmetricComponent());
assertEquals(upperStageBody, interstageBody.getPreviousSymmetricComponent());
assertNull(boosterCone.getPreviousSymmetricComponent());
assertEquals(boosterCone, boosterBody.getPreviousSymmetricComponent());
// case 1: pod set is larger, and at the back of the core stage
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 2: pod set is smaller, and at the back of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 3: pod set is equal, and at the back of the core stage
podSetBody.setOuterRadius(0.0385);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 4: pod set is larger, and at the front of the core stage
podSetBody.setOuterRadius(0.05);
podSet.setAxialOffset(AxialMethod.TOP, 0);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 5: pod set is smaller, and at the front of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 6: pod set is equal, and at the front of the core stage
podSetBody.setOuterRadius(0.0385);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 7: pod set is same length as core stage, and larger, and at the front of the core stage
podSetBody.setOuterRadius(0.05);
podSetBody.setLength(0.7);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 8: pod set is same length as core stage, and smaller, and at the front of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 9: pod set is in larger, and in the middle of the core stage
podSetBody.setLength(0.2);
podSetBody.setOuterRadius(0.05);
podSet.setAxialOffset(AxialMethod.MIDDLE, 0);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// case 10: pod set is in larger, and behind the back of the core stage
podSet.setAxialOffset(AxialMethod.BOTTOM, 1);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
// Add a booster inside the pod set
ParallelStage insideBooster = new ParallelStage();
insideBooster.setName("Inside Booster");
insideBooster.setInstanceCount(1);
insideBooster.setRadius(RadiusMethod.FREE, 0);
podSetBody.addChild(insideBooster);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 0);
BodyTube insideBoosterBody = new BodyTube(0.2, 0.06, 0.001);
insideBoosterBody.setName("Inside Booster Body");
insideBooster.addChild(insideBoosterBody);
// Case 1: inside booster is larger than pod set and flush to its end (both are at the back of the core stage)
podSet.setAxialOffset(AxialMethod.BOTTOM, 0);
insideBoosterBody.setOuterRadius(0.06);
assertEquals(insideBoosterBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 2: inside booster is smaller than pod set and flush to its end
insideBoosterBody.setOuterRadius(0.04);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 3: inside booster is equal the pod set and flush to its end
insideBoosterBody.setOuterRadius(0.05);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 4: inside booster is larger than pod set and before the back (pod set at the back of the core stage)
insideBooster.setAxialOffset(AxialMethod.BOTTOM, -1);
insideBoosterBody.setOuterRadius(0.06);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 5: inside booster is larger than pod set and after the back (pod set at the back of the core stage)
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 1);
assertEquals(podSetBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetBody, insideBoosterBody.getPreviousSymmetricComponent());
// Case 6: inside booster is larger than pod set, pod set is before the back of the core stage, inside booster is an equal amount after the back of the pod set
podSet.setAxialOffset(AxialMethod.BOTTOM, -1.5);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 1.5);
assertEquals(insideBoosterBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetBody, insideBoosterBody.getPreviousSymmetricComponent());
// Case 7: inside booster is larger than pod set, pod set is after the back of the core stage, inside booster is an equal amount before the back of the pod set
podSet.setAxialOffset(AxialMethod.BOTTOM, 1.5);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, -1.5);
assertEquals(insideBoosterBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 8: inside booster is larger than pod set, pod set is before the back of the core stage, inside booster is flush with the back of the pod set
podSet.setAxialOffset(AxialMethod.BOTTOM, -1.5);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 0);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
// Case 9: inside booster is larger than pod set, pod set is after the back of the core stage, inside booster is flush with the back of the pod set
podSet.setAxialOffset(AxialMethod.BOTTOM, 1.5);
insideBooster.setAxialOffset(AxialMethod.BOTTOM, 0);
assertEquals(coreBody, lastStageBody.getPreviousSymmetricComponent());
assertEquals(coreBody, podSetCone.getPreviousSymmetricComponent());
assertEquals(podSetCone, podSetBody.getPreviousSymmetricComponent());
assertEquals(interstageBody, coreBody.getPreviousSymmetricComponent());
assertEquals(podSetCone, insideBoosterBody.getPreviousSymmetricComponent());
}
@Test
public void testNextSymmetricComponent() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
AxialStage payloadStage = rocket.getStage(0);
NoseCone payloadFairingNoseCone = (NoseCone) payloadStage.getChild(0);
BodyTube payloadBody = (BodyTube) payloadStage.getChild(1);
Transition payloadFairingTail = (Transition) payloadStage.getChild(2);
BodyTube upperStageBody = (BodyTube) payloadStage.getChild(3);
BodyTube interstageBody = (BodyTube) payloadStage.getChild(4);
assertEquals(payloadBody, payloadFairingNoseCone.getNextSymmetricComponent());
assertEquals(payloadFairingTail, payloadBody.getNextSymmetricComponent());
assertEquals(upperStageBody, payloadFairingTail.getNextSymmetricComponent());
assertEquals(interstageBody, upperStageBody.getNextSymmetricComponent());
AxialStage coreStage = rocket.getStage(1);
BodyTube coreBody = (BodyTube) coreStage.getChild(0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertNull(coreBody.getNextSymmetricComponent());
ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
NoseCone boosterCone = (NoseCone) boosterStage.getChild(0);
BodyTube boosterBody = (BodyTube) boosterStage.getChild(1);
assertEquals(boosterBody, boosterCone.getNextSymmetricComponent());
assertNull(boosterBody.getNextSymmetricComponent());
}
@Test
public void testNextSymmetricComponentInlineComponentAssembly() {
Rocket rocket = TestRockets.makeFalcon9Heavy();
// Stage 0
AxialStage payloadStage = rocket.getStage(0);
NoseCone payloadFairingNoseCone = (NoseCone) payloadStage.getChild(0);
BodyTube payloadBody = (BodyTube) payloadStage.getChild(1);
Transition payloadFairingTail = (Transition) payloadStage.getChild(2);
BodyTube upperStageBody = (BodyTube) payloadStage.getChild(3);
BodyTube interstageBody = (BodyTube) payloadStage.getChild(4);
// Stage 1
AxialStage coreStage = rocket.getStage(1);
BodyTube coreBody = (BodyTube) coreStage.getChild(0);
// Booster stage
ParallelStage boosterStage = (ParallelStage) rocket.getStage(2);
boosterStage.setInstanceCount(1);
boosterStage.setRadius(RadiusMethod.RELATIVE, 0);
NoseCone boosterCone = (NoseCone) boosterStage.getChild(0);
BodyTube boosterBody = (BodyTube) boosterStage.getChild(1);
// Add inline pod set
PodSet podSet = new PodSet();
podSet.setName("Inline Pod Set");
podSet.setInstanceCount(1);
podSet.setRadius(RadiusMethod.FREE, 0);
coreBody.addChild(podSet);
podSet.setAxialOffset(AxialMethod.TOP, 0);
BodyTube podSetBody = new BodyTube(0.2, 0.05, 0.001);
podSetBody.setName("Pod Set Body");
podSet.addChild(podSetBody);
TrapezoidFinSet finSet = new TrapezoidFinSet();
podSetBody.addChild(finSet);
NoseCone podSetCone = new NoseCone();
podSetCone.setLength(0.1);
podSetCone.setBaseRadius(0.05);
podSetCone.setFlipped(true);
podSet.addChild(podSetCone);
// Add last stage
AxialStage lastStage = new AxialStage();
BodyTube lastStageBody = new BodyTube(0.2, 0.05, 0.001);
lastStageBody.setName("Last Stage Body");
lastStage.addChild(lastStageBody);
rocket.addChild(lastStage);
assertEquals(payloadBody, payloadFairingNoseCone.getNextSymmetricComponent());
assertEquals(payloadFairingTail, payloadBody.getNextSymmetricComponent());
assertEquals(upperStageBody, payloadFairingTail.getNextSymmetricComponent());
assertEquals(interstageBody, upperStageBody.getNextSymmetricComponent());
assertNull(lastStageBody.getNextSymmetricComponent());
assertEquals(boosterBody, boosterCone.getNextSymmetricComponent());
// case 1: pod set is larger, and at the front of the core stage
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 2: pod set is smaller, and at the front of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 3: pod set is equal, and at the front of the core stage
podSetBody.setOuterRadius(0.0385);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 4: pod set is larger, and at the back of the core stage
podSetBody.setOuterRadius(0.05);
podSet.setAxialOffset(AxialMethod.BOTTOM, 0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 5: pod set is smaller, and at the back of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 6: pod set is equal, and at the back of the core stage
podSetBody.setOuterRadius(0.0385);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 7: pod set is same length as core stage, and larger, and at the back of the core stage
podSetBody.setOuterRadius(0.05);
podSetBody.setLength(0.7);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 8: pod set is same length as core stage, and smaller, and at the back of the core stage
podSetBody.setOuterRadius(0.02);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 9: pod set is in larger, and in the middle of the core stage
podSetBody.setLength(0.2);
podSetBody.setOuterRadius(0.05);
podSet.setAxialOffset(AxialMethod.MIDDLE, 0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// case 10: pod set is in larger, and behind the front of the core stage
podSet.setAxialOffset(AxialMethod.TOP, 1);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
// Add a booster inside the pod set
ParallelStage insideBooster = new ParallelStage();
insideBooster.setName("Inside Booster");
insideBooster.setInstanceCount(1);
insideBooster.setRadius(RadiusMethod.FREE, 0);
podSetBody.addChild(insideBooster);
insideBooster.setAxialOffset(AxialMethod.TOP, 0);
BodyTube insideBoosterBody = new BodyTube(0.2, 0.06, 0.001);
insideBoosterBody.setName("Inside Booster Body");
insideBooster.addChild(insideBoosterBody);
// Case 1: inside booster is larger than pod set and flush to its front (both are at the front of the core stage)
podSet.setAxialOffset(AxialMethod.TOP, 0);
insideBoosterBody.setOuterRadius(0.06);
assertEquals(insideBoosterBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 2: inside booster is smaller than pod set and flush to its front
insideBoosterBody.setOuterRadius(0.04);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 3: inside booster is equal the pod set and flush to its front
insideBoosterBody.setOuterRadius(0.05);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 4: inside booster is larger than pod set and before the front (pod set at the front of the core stage)
insideBooster.setAxialOffset(AxialMethod.TOP, -1);
insideBoosterBody.setOuterRadius(0.06);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 5: inside booster is larger than pod set and after the front (pod set at the front of the core stage)
insideBooster.setAxialOffset(AxialMethod.TOP, 1);
assertEquals(podSetBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 6: inside booster is larger than pod set, pod set is before the front of the core stage, inside booster is an equal amount after the front of the pod set
podSet.setAxialOffset(AxialMethod.TOP, -1.5);
insideBooster.setAxialOffset(AxialMethod.TOP, 1.5);
assertEquals(insideBoosterBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 7: inside booster is larger than pod set, pod set is after the front of the core stage, inside booster is an equal amount before the front of the pod set
podSet.setAxialOffset(AxialMethod.TOP, 1.5);
insideBooster.setAxialOffset(AxialMethod.TOP, -1.5);
assertEquals(insideBoosterBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 8: inside booster is larger than pod set, pod set is before the front of the core stage, inside booster is flush with the front of the pod set
podSet.setAxialOffset(AxialMethod.TOP, -1.5);
insideBooster.setAxialOffset(AxialMethod.TOP, 0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
// Case 9: inside booster is larger than pod set, pod set is after the front of the core stage, inside booster is flush with the front of the pod set
podSet.setAxialOffset(AxialMethod.TOP, 1.5);
insideBooster.setAxialOffset(AxialMethod.TOP, 0);
assertEquals(coreBody, interstageBody.getNextSymmetricComponent());
assertEquals(podSetCone, podSetBody.getNextSymmetricComponent());
assertNull(podSetCone.getNextSymmetricComponent());
assertEquals(lastStageBody, coreBody.getNextSymmetricComponent());
assertNull(insideBoosterBody.getNextSymmetricComponent());
}
}

View File

@ -8,443 +8,380 @@ import net.sf.openrocket.util.BaseTestCase.BaseTestCase;
import org.junit.Test;
public class SymmetricComponentVolumeTest extends BaseTestCase {
@Test
public void simpleConeFilled() {
NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
double volume = Math.PI / 3.0;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.75, cg.x, epsilonPercent * 0.75);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleConeWithShoulderFilled() {
NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderThickness(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
double volume = Math.PI / 3.0;
volume += Math.PI;
double mass = density * volume;
//System.out.println(volume + "\t" + mass);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(1.312, cg.x, epsilonPercent * 1.071);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleConeHollow() {
NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setAftRadius(1.0);
nc.setThickness(0.5);
nc.setType(Transition.Shape.CONICAL);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
double volume = Math.PI / 3.0; // outer volume
// manually projected Thickness of 0.5 on to radius to determine
// the innerConeDimen. Since the outer cone is "square" (height = radius),
// we only need to compute this one dimension in order to compute the
// volume of the inner cone.
double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
volume -= innerVolume;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.7454, cg.x, epsilonPercent * 0.7454);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleConeWithShoulderHollow() {
NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0);
nc.setThickness(0.5);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderThickness(0.5);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
double volume = Math.PI / 3.0; // outer volume
// manually projected Thickness of 0.5 on to radius to determine
// the innerConeDimen. Since the outer cone is "square" (height = radius),
// we only need to compute this one dimension in order to compute the
// volume of the inner cone.
double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
volume -= innerVolume;
volume += Math.PI - Math.PI * 0.5 * 0.5;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(1.2719, cg.x, epsilonPercent * 1.2719);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleTransitionFilled() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(4.0);
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(1.0);
nc.setAftRadius(2.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(2.4285, cg.x, epsilonPercent * 2.4285);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleTransitionWithShouldersFilled() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(4.0);
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(1.0);
nc.setAftRadius(2.0);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderRadius(2.0);
nc.setAftShoulderThickness(2.0);
nc.setForeShoulderLength(1.0);
nc.setForeShoulderRadius(1.0);
nc.setForeShoulderThickness(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
// plus aft shoulder:
volume += Math.PI * 1.0 * 2.0 * 2.0;
// plus fore shoulder:
volume += Math.PI * 1.0 * 1.0 * 1.0;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(2.8023, cg.x, epsilonPercent * 2.8023);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleTransitionHollow1() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.5);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness >= fore radius, the
// hollowed out portion of the transition
// forms a cone.
// the dimensions of this cone were determined
// using a 2d cad tool.
double innerConeRadius = 0.441;
double innerConeLength = 0.882;
double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
double volume = filledVolume - innerVolume;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.5884, cg.x, epsilonPercent * 0.5884);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleTransitionWithShouldersHollow1() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.5);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderThickness(0.5);
nc.setForeShoulderLength(1.0);
nc.setForeShoulderRadius(0.5);
nc.setForeShoulderThickness(0.5); // note this means fore shoulder is filled.
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness >= fore radius, the
// hollowed out portion of the transition
// forms a cone.
// the dimensions of this cone were determined
// using a 2d cad tool.
double innerConeRadius = 0.441;
double innerConeLength = 0.882;
double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
double volume = filledVolume - innerVolume;
// Now add aft shoulder
volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.5 * 0.5;
// Now add fore shoulder
volume += Math.PI * 1.0 * 0.5 * 0.5;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.8581, cg.x, epsilonPercent * 0.8581);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleTransitionHollow2() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.25);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness < fore radius, the
// hollowed out portion of the transition
// forms a transition.
// the dimensions of this transition were determined
// using a 2d cad tool.
double innerTransitionAftRadius = 0.7205;
double innerTransitionForeRadius = 0.2205;
double innerVolume = Math.PI / 3.0
* (innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius);
double volume = filledVolume - innerVolume;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.56827, cg.x, epsilonPercent * 0.56827);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void simpleTransitionWithShouldersHollow2() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.25);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderThickness(0.25);
nc.setForeShoulderLength(1.0);
nc.setForeShoulderRadius(0.5);
nc.setForeShoulderThickness(0.25);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
//System.out.println(nc.getComponentVolume() + "\t" + nc.getMass());
//System.out.println(cg);
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness < fore radius, the
// hollowed out portion of the transition
// forms a transition.
// the dimensions of this transition were determined
// using a 2d cad tool.
double innerTransitionAftRadius = 0.7205;
double innerTransitionForeRadius = 0.2205;
double innerVolume = Math.PI / 3.0
* (innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius);
double volume = filledVolume - innerVolume;
// now add aft shoulder
volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.75 * 0.75;
// now add fore shoulder
volume += Math.PI * 1.0 * 0.5 * 0.5 - Math.PI * 1.0 * 0.25 * 0.25;
double mass = density * volume;
//System.out.println(volume);
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.7829, cg.x, epsilonPercent * 0.7829);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleConeFilled() {
NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
double volume = Math.PI / 3.0;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.75, cg.x, epsilonPercent * 0.75);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleConeWithShoulderFilled() {
NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderThickness(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
double volume = Math.PI / 3.0;
volume += Math.PI;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(1.312, cg.x, epsilonPercent * 1.071);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleConeHollow() {
NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setAftRadius(1.0);
nc.setThickness(0.5);
nc.setType(Transition.Shape.CONICAL);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
double volume = Math.PI / 3.0; // outer volume
// manually projected Thickness of 0.5 on to radius to determine
// the innerConeDimen. Since the outer cone is "square" (height = radius),
// we only need to compute this one dimension in order to compute the
// volume of the inner cone.
double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
volume -= innerVolume;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.7454, cg.x, epsilonPercent * 0.7454);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleConeWithShoulderHollow() {
NoseCone nc = new NoseCone();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setAftRadius(1.0);
nc.setThickness(0.5);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderThickness(0.5);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
double volume = Math.PI / 3.0; // outer volume
// manually projected Thickness of 0.5 on to radius to determine
// the innerConeDimen. Since the outer cone is "square" (height = radius),
// we only need to compute this one dimension in order to compute the
// volume of the inner cone.
double innerConeDimen = 1.0 - Math.sqrt(2.0) / 2.0;
double innerVolume = Math.PI / 3.0 * innerConeDimen * innerConeDimen * innerConeDimen;
volume -= innerVolume;
volume += Math.PI - Math.PI * 0.5 * 0.5;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(1.2719, cg.x, epsilonPercent * 1.2719);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleTransitionFilled() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(4.0);
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(1.0);
nc.setAftRadius(2.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(2.4285, cg.x, epsilonPercent * 2.4285);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleTransitionWithShouldersFilled() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(4.0);
nc.setFilled(true);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(1.0);
nc.setAftRadius(2.0);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderRadius(2.0);
nc.setAftShoulderThickness(2.0);
nc.setForeShoulderLength(1.0);
nc.setForeShoulderRadius(1.0);
nc.setForeShoulderThickness(1.0);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
double volume = Math.PI / 3.0 * (2.0 * 2.0 + 2.0 * 1.0 + 1.0 * 1.0) * 4.0;
// plus aft shoulder:
volume += Math.PI * 1.0 * 2.0 * 2.0;
// plus fore shoulder:
volume += Math.PI * 1.0 * 1.0 * 1.0;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(2.8023, cg.x, epsilonPercent * 2.8023);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleTransitionHollow1() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.5);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness >= fore radius, the
// hollowed out portion of the transition
// forms a cone.
// the dimensions of this cone were determined
// using a 2d cad tool.
double innerConeRadius = 0.441;
double innerConeLength = 0.882;
double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
double volume = filledVolume - innerVolume;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.5884, cg.x, epsilonPercent * 0.5884);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleTransitionWithShouldersHollow1() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.5);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderThickness(0.5);
nc.setForeShoulderLength(1.0);
nc.setForeShoulderRadius(0.5);
nc.setForeShoulderThickness(0.5); // note this means fore shoulder is filled.
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness >= fore radius, the
// hollowed out portion of the transition
// forms a cone.
// the dimensions of this cone were determined
// using a 2d cad tool.
double innerConeRadius = 0.441;
double innerConeLength = 0.882;
double innerVolume = Math.PI / 3.0 * innerConeLength * innerConeRadius * innerConeRadius;
double volume = filledVolume - innerVolume;
// Now add aft shoulder
volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.5 * 0.5;
// Now add fore shoulder
volume += Math.PI * 1.0 * 0.5 * 0.5;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.8581, cg.x, epsilonPercent * 0.8581);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleTransitionHollow2() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.25);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness < fore radius, the
// hollowed out portion of the transition
// forms a transition.
// the dimensions of this transition were determined
// using a 2d cad tool.
double innerTransitionAftRadius = 0.7205;
double innerTransitionForeRadius = 0.2205;
double innerVolume = Math.PI / 3.0
* (innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius);
double volume = filledVolume - innerVolume;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.56827, cg.x, epsilonPercent * 0.56827);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
@Test
public void testVolumeSimpleTransitionWithShouldersHollow2() {
Transition nc = new Transition();
final double epsilonPercent = 0.001;
final double density = 2.0;
nc.setLength(1.0);
nc.setType(Transition.Shape.CONICAL);
nc.setForeRadius(0.5);
nc.setAftRadius(1.0);
nc.setThickness(0.25);
nc.setAftShoulderLength(1.0);
nc.setAftShoulderRadius(1.0);
nc.setAftShoulderThickness(0.25);
nc.setForeShoulderLength(1.0);
nc.setForeShoulderRadius(0.5);
nc.setForeShoulderThickness(0.25);
nc.setMaterial(Material.newMaterial(Material.Type.BULK, "test", density, true));
Coordinate cg = nc.getCG();
// Volume of filled transition =
double filledVolume = Math.PI / 3.0 * (1.0 * 1.0 + 1.0 * 0.5 + 0.5 * 0.5) * 1.0;
// magic 2D cad drawing...
//
// Since the thickness < fore radius, the
// hollowed out portion of the transition
// forms a transition.
// the dimensions of this transition were determined
// using a 2d cad tool.
double innerTransitionAftRadius = 0.7205;
double innerTransitionForeRadius = 0.2205;
double innerVolume = Math.PI / 3.0
* (innerTransitionAftRadius * innerTransitionAftRadius + innerTransitionAftRadius * innerTransitionForeRadius + innerTransitionForeRadius * innerTransitionForeRadius);
double volume = filledVolume - innerVolume;
// now add aft shoulder
volume += Math.PI * 1.0 * 1.0 * 1.0 - Math.PI * 1.0 * 0.75 * 0.75;
// now add fore shoulder
volume += Math.PI * 1.0 * 0.5 * 0.5 - Math.PI * 1.0 * 0.25 * 0.25;
double mass = density * volume;
assertEquals(volume, nc.getComponentVolume(), epsilonPercent * volume);
assertEquals(mass, nc.getMass(), epsilonPercent * mass);
assertEquals(0.7829, cg.x, epsilonPercent * 0.7829);
assertEquals(mass, cg.weight, epsilonPercent * mass);
}
}

View File

@ -14,6 +14,7 @@ import net.sf.openrocket.gui.widgets.SelectColorToggleButton;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.Rocket;
@ -40,7 +41,7 @@ public class StageSelector extends JPanel implements StateChangeListener {
private void updateButtons( final FlightConfiguration configuration ) {
buttons.clear();
this.removeAll();
List<RocketComponent> assemblies = configuration.getRocket().getChildAssemblies();
List<ComponentAssembly> assemblies = configuration.getRocket().getAllChildAssemblies();
for (RocketComponent stage : assemblies) {
if (!(stage instanceof AxialStage)) continue;