diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 117978079..0b0465672 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1756,6 +1756,9 @@ Warning.TUMBLE_UNDER_THRUST = Stage began to tumble under thrust. Warning.EVENT_AFTER_LANDING = Flight Event occurred after landing: Warning.ZERO_LENGTH_BODY = Zero length bodies may not result in accurate simulations. Warning.ZERO_RADIUS_BODY = Zero length bodies may not result in accurate simulations. +Warning.TUBE_STABILITY = Tube fin stability calculations may not be accurate. +Warning.TUBE_SEPARATION = Space between tube fins may not result in accurate simulations. +Warning.TUBE_OVERLAP = Overlapping tube fins may not result in accurate simulations. ! Scale dialog ScaleDialog.lbl.scaleRocket = Entire rocket diff --git a/core/src/net/sf/openrocket/aerodynamics/Warning.java b/core/src/net/sf/openrocket/aerodynamics/Warning.java index 92dd6e027..0653b1a3e 100644 --- a/core/src/net/sf/openrocket/aerodynamics/Warning.java +++ b/core/src/net/sf/openrocket/aerodynamics/Warning.java @@ -392,4 +392,7 @@ public abstract class Warning { public static final Warning ZERO_LENGTH_BODY = new Other(trans.get("Warning.ZERO_LENGTH_BODY")); public static final Warning ZERO_RADIUS_BODY = new Other(trans.get("Warning.ZERO_RADIUS_BODY")); + public static final Warning TUBE_STABILITY = new Other(trans.get("Warning.TUBE_STABILITY")); + public static final Warning TUBE_SEPARATION = new Other(trans.get("Warning.TUBE_SEPARATION")); + public static final Warning TUBE_OVERLAP = new Other(trans.get("Warning.TUBE_OVERLAP")); } diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/LaunchLugCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/LaunchLugCalc.java index 179a4d9a0..2400d5cce 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/LaunchLugCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/LaunchLugCalc.java @@ -8,7 +8,7 @@ import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Transformation; -public class LaunchLugCalc extends RocketComponentCalc { +public class LaunchLugCalc extends TubeCalc { private final double CDmul; private final double refArea; diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeCalc.java new file mode 100644 index 000000000..8595e1efb --- /dev/null +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeCalc.java @@ -0,0 +1,27 @@ +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.LaunchLug; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.Transformation; + +public abstract class TubeCalc extends RocketComponentCalc { + + private final double CDmul; + protected final double refArea; + + public TubeCalc(RocketComponent component) { + super(component); + + LaunchLug lug = (LaunchLug)component; + double ld = lug.getLength() / (2*lug.getOuterRadius()); + + CDmul = Math.max(1.3 - ld, 1); + refArea = Math.PI * MathUtil.pow2(lug.getOuterRadius()) - + Math.PI * MathUtil.pow2(lug.getInnerRadius()) * Math.max(1 - ld, 0); + } + +} diff --git a/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java b/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java index 261cff8e0..23fbf16a4 100644 --- a/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java +++ b/core/src/net/sf/openrocket/aerodynamics/barrowman/TubeFinSetCalc.java @@ -10,6 +10,7 @@ import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.Warning.Other; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.TubeFinSet; @@ -33,9 +34,9 @@ import org.slf4j.LoggerFactory; * @author kruland * */ -public class TubeFinSetCalc extends RocketComponentCalc { +public class TubeFinSetCalc extends TubeCalc { - private final static Logger logger = LoggerFactory.getLogger(FinSetCalc.class); + private final static Logger log = LoggerFactory.getLogger(FinSetCalc.class); private static final double STALL_ANGLE = (20 * Math.PI / 180); @@ -56,8 +57,13 @@ public class TubeFinSetCalc extends RocketComponentCalc { protected double[] chordLead = new double[DIVISIONS]; protected double[] chordTrail = new double[DIVISIONS]; protected double[] chordLength = new double[DIVISIONS]; - + + protected final WarningSet geometryWarnings = new WarningSet(); + private final double[] poly = new double[6]; + + private final double wettedArea; + private final double frontalArea; private final double thickness; private final double bodyRadius; @@ -73,9 +79,52 @@ public class TubeFinSetCalc extends RocketComponentCalc { throw new IllegalArgumentException("Illegal component type " + component); } - TubeFinSet fin = (TubeFinSet) component; + final TubeFinSet tubes = (TubeFinSet) component; + final TubeFinSet fin = tubes; // keep this around while we're still leveraging FinSet + + geometryWarnings.add(Warning.TUBE_STABILITY); + if (tubes.getTubeSeparation() > MathUtil.EPSILON) { + geometryWarnings.add(Warning.TUBE_SEPARATION); + } else if (tubes.getTubeSeparation() < -MathUtil.EPSILON) { + geometryWarnings.add(Warning.TUBE_OVERLAP); + } + + // precompute geometry + bodyRadius = tubes.getBodyRadius(); + + // wetted area for friction drag calculation + // Area of inner surface of tubes. + final double innerArea = tubes.getFinCount() * tubes.getLength() * 2.0 * Math.PI * tubes.getInnerRadius(); + + // Area of the outer surface of tubes. Since roughly half + // of the area is "masked" by the interstices between the tubes and the + // body tube, only consider half the area + final double outerArea = tubes.getFinCount() * tubes.getLength() * Math.PI * tubes.getOuterRadius(); + + // Area of the portion of the body tube masked by the tube fins + final BodyTube parent = (BodyTube) tubes.getParent(); + final double maskedArea = tubes.getLength() * 2.0 * Math.PI * parent.getOuterRadius(); + + wettedArea = innerArea + outerArea - maskedArea; + log.debug("wetted area of tube fins " + wettedArea); + + // effective frontal area for pressure drag calculation + // model interstices between tube fins and body tube as closed blunt area + + // area of disk passing through tube fin centers + final double tubeDiskArea = Math.PI * MathUtil.pow2(bodyRadius + tubes.getOuterRadius()); + + // half of combined area of tube fin interiors. + final double tubeInnerArea = tubes.getFinCount() * Math.PI * MathUtil.pow2(tubes.getInnerRadius()) / 2.0; + + // body tube area + final double bodyTubeArea = Math.PI * MathUtil.pow2(bodyRadius); + + // refArea is effective frontal area of a tube, from LaunchLugCalc + frontalArea = tubes.getFinCount() * refArea + tubeDiskArea - tubeInnerArea - bodyTubeArea; + log.debug("effective frontal area " + frontalArea); + thickness = fin.getThickness(); - bodyRadius = fin.getBodyRadius(); finCount = 3 * fin.getFinCount(); baseRotation = fin.getBaseRotation(); cantAngle = 0; @@ -94,7 +143,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { */ @Override public void calculateNonaxialForces(FlightConditions conditions, Transformation transform, - AerodynamicForces forces, WarningSet warnings) { + AerodynamicForces forces, WarningSet warnings) { if (span < 0.001) { forces.setCm(0); @@ -113,14 +162,13 @@ public class TubeFinSetCalc extends RocketComponentCalc { if( (0 < bodyRadius) && (thickness > bodyRadius / 2)){ warnings.add(Warning.THICK_FIN); } - warnings.add(new Other("Tube fin support is experimental")); //////// Calculate CNa. ///////// // One fin without interference (both sub- and supersonic): double cna1 = calculateFinCNa1(conditions); - // logger.debug("Component cna1 = {}", cna1); + // log.debug("Component cna1 = {}", cna1); // Multiple fins with fin-fin interference double cna; @@ -141,7 +189,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { cna = cna1 * finCount / 2.0; } - // logger.debug("Component cna = {}", cna); + // log.debug("Component cna = {}", cna); // Take into account fin-fin interference effects switch (interferenceFinCount) { @@ -182,7 +230,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { tau = 0; cna *= 1 + tau; // Classical Barrowman // cna *= pow2(1 + tau); // Barrowman thesis (too optimistic??) - // logger.debug("Component cna = {}", cna); + // log.debug("Component cna = {}", cna); // TODO: LOW: check for fin tip mach cone interference // (Barrowman thesis pdf-page 40) @@ -191,9 +239,9 @@ public class TubeFinSetCalc extends RocketComponentCalc { // Calculate CP position double x = macLead + calculateCPPos(conditions) * macLength; - // logger.debug("Component macLead = {}", macLead); - // logger.debug("Component macLength = {}", macLength); - // logger.debug("Component x = {}", x); + // log.debug("Component macLead = {}", macLead); + // log.debug("Component macLength = {}", macLength); + // log.debug("Component x = {}", x); // Calculate roll forces, reduce forcing above stall angle @@ -352,7 +400,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { double y = i * dy; macLength += length * length; - logger.debug("macLength = {}, length = {}, i = {}", macLength, length, i); + log.debug("macLength = {}, length = {}, i = {}", macLength, length, i); macSpan += y * length; macLead += chordLead[i] * length; area += length; @@ -368,7 +416,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { } macLength *= dy; - logger.debug("macLength = {}", macLength); + log.debug("macLength = {}", macLength); macSpan *= dy; macLead *= dy; area *= dy; @@ -537,7 +585,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { */ private double calculateCPPos(FlightConditions cond) { double m = cond.getMach(); - // logger.debug("m = {} ", m); + // log.debug("m = {} ", m); if (m <= 0.5) { // At subsonic speeds CP at quarter chord return 0.25; @@ -556,7 +604,7 @@ public class TubeFinSetCalc extends RocketComponentCalc { val += poly[i] * x; x *= m; } - // logger.debug("val = {}", val); + // log.debug("val = {}", val); return val; } @@ -586,82 +634,26 @@ public class TubeFinSetCalc extends RocketComponentCalc { poly[1] = (-31.6049 * (-0.705375 + ar) * (-0.198476 + ar)) / denom; poly[0] = (9.16049 * (-0.588838 + ar) * (-0.20624 + ar)) / denom; } - - - // @SuppressWarnings("null") - // public static void main(String arg[]) { - // Rocket rocket = TestRocket.makeRocket(); - // FinSet finset = null; - // - // Iterator iter = rocket.deepIterator(); - // while (iter.hasNext()) { - // RocketComponent c = iter.next(); - // if (c instanceof FinSet) { - // finset = (FinSet)c; - // break; - // } - // } - // - // ((TrapezoidFinSet)finset).setHeight(0.10); - // ((TrapezoidFinSet)finset).setRootChord(0.10); - // ((TrapezoidFinSet)finset).setTipChord(0.10); - // ((TrapezoidFinSet)finset).setSweep(0.0); - // - // - // FinSetCalc calc = new FinSetCalc(finset); - // - // calc.calculateFinGeometry(); - // FlightConditions cond = new FlightConditions(new Configuration(rocket)); - // for (double m=0; m < 3; m+=0.05) { - // cond.setMach(m); - // cond.setAOA(0.0*Math.PI/180); - // double cna = calc.calculateFinCNa1(cond); - // System.out.printf("%5.2f "+cna+"\n", m); - // } - // - // } + + @Override + public double calculateFrictionCD(FlightConditions conditions, double componentCf, WarningSet warnings) { + warnings.addAll(geometryWarnings); + + final double frictionCD = componentCf * wettedArea / conditions.getRefArea(); + log.debug("frictionCD " + frictionCD); + return frictionCD; + } @Override public double calculatePressureCD(FlightConditions conditions, - double stagnationCD, double baseCD, WarningSet warnings) { + double stagnationCD, double baseCD, WarningSet warnings) { - double mach = conditions.getMach(); - double cd = 0; - - // Pressure fore-drag - if (crossSection == FinSet.CrossSection.AIRFOIL || - crossSection == FinSet.CrossSection.ROUNDED) { - - // Round leading edge - if (mach < 0.9) { - cd = Math.pow(1 - pow2(mach), -0.417) - 1; - } else if (mach < 1) { - cd = 1 - 1.785 * (mach - 0.9); - } else { - cd = 1.214 - 0.502 / pow2(mach) + 0.1095 / pow2(pow2(mach)); - } - - } else if (crossSection == FinSet.CrossSection.SQUARE) { - cd = stagnationCD; - } else { - throw new UnsupportedOperationException("Unsupported fin profile: " + crossSection); - } - - // Slanted leading edge - cd *= pow2(cosGammaLead); - - // Trailing edge drag - if (crossSection == FinSet.CrossSection.SQUARE) { - cd += baseCD; - } else if (crossSection == FinSet.CrossSection.ROUNDED) { - cd += baseCD / 2; - } - // Airfoil assumed to have zero base drag - - // Scale to correct reference area - cd *= finCount * span * thickness / conditions.getRefArea(); - - return cd; + warnings.addAll(geometryWarnings); + + final double cd = (stagnationCD + baseCD) * frontalArea / conditions.getRefArea(); + log.debug("pressure CD " + cd); + + return cd; } private static int calculateInterferenceFinCount(TubeFinSet component) { @@ -672,11 +664,4 @@ public class TubeFinSetCalc extends RocketComponentCalc { return 3 * component.getFinCount(); } - - @Override - public double calculateFrictionCD(FlightConditions conditions, double componentCf, WarningSet warnings) { - // launch lug doesn't add enough area to worry about - return 0; - } - }