diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index eb84e43e8..2d5d7d070 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -194,13 +194,17 @@ public class BasicEventSimulationEngine implements SimulationEngine { } // Check for Tumbling - // FIXME - need to test things like no longer stable. + // Conditions for transision are: + // apogee reached + // and is not already tumbling + // and not stable (cg > cp) + // and aoa > 30 if (status.isApogeeReached() && !status.isTumbling() ) { - int last_data_index = status.getFlightData().getLength() -1; - double cp = status.getFlightData().get(FlightDataType.TYPE_CP_LOCATION).get(last_data_index); - double cg = status.getFlightData().get(FlightDataType.TYPE_CG_LOCATION).get(last_data_index); - if( cg > cp ) { + double cp = status.getFlightData().getLast(FlightDataType.TYPE_CP_LOCATION); + double cg = status.getFlightData().getLast(FlightDataType.TYPE_CG_LOCATION); + double aoa = status.getFlightData().getLast(FlightDataType.TYPE_AOA); + if( cg > cp && aoa > 30 ) { addEvent( new FlightEvent(FlightEvent.Type.TUMBLE,status.getSimulationTime())); } status.setTumbling(true); diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java index 5e391a176..db2d97469 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java @@ -1,42 +1,77 @@ package net.sf.openrocket.simulation; +import java.util.Iterator; + import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.motor.MotorInstanceConfiguration; import net.sf.openrocket.rocketcomponent.Configuration; +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; public class BasicTumbleStatus extends SimulationStatus { - private double tumbleCd; + // Magic constants from techdoc.pdf + private final static double cDFin = 1.42; + private final static double cDBt = 0.56; + // Fin efficiency. Index is number of fins. The 0th entry is arbitrary and used to + // offset the indexes so finEff[1] is the coefficient for one fin from the table in techdoc.pdf + private final static double[] finEff = { 0.0, 0.5, 1.0, 1.41, 1.81, 1.73, 1.90, 1.85 }; + + private double drag; public BasicTumbleStatus(Configuration configuration, MotorInstanceConfiguration motorConfiguration, SimulationConditions simulationConditions) { super(configuration, motorConfiguration, simulationConditions); - computeTumbleCd(); + computeTumbleDrag(); } public BasicTumbleStatus(SimulationStatus orig) { super(orig); if ( orig instanceof BasicTumbleStatus ) { - this.tumbleCd = ((BasicTumbleStatus) orig).tumbleCd; + this.drag = ((BasicTumbleStatus) orig).drag; } } - public double getTumbleCd( ) { - return tumbleCd; + public double getTumbleDrag( ) { + return drag; } - public void computeTumbleCd() { - // FIXME - probably want to compute the overall CD more accurately. Perhaps average - // CD over three AoA: 0, 90, 180. In any case, using barrowman to compute this Cd is - // completely wrong. - WarningSet warnings = new WarningSet(); - FlightConditions cond = new FlightConditions(this.getConfiguration()); - cond.setAOA(Math.PI); - AerodynamicForces forces = this.getSimulationConditions().getAerodynamicCalculator().getAerodynamicForces(this.getConfiguration(), cond, warnings); - tumbleCd = forces.getCD(); + public void computeTumbleDrag() { + + // Computed based on Sampo's experimentation as documented in the pdf. + + // compute the fin and body tube projected areas + double aFins = 0.0; + double aBt = 0.0; + Rocket r = this.getConfiguration().getRocket(); + Iterator componentIterator = r.iterator(); + while ( componentIterator.hasNext() ) { + RocketComponent component = componentIterator.next(); + if ( ! component.isAerodynamic() ) { + continue; + } + if ( component instanceof FinSet ) { + + double finComponent = ((FinSet)component).getFinArea(); + int finCount = ((FinSet)component).getFinCount(); + // check bounds on finCount. + if ( finCount >= finEff.length ) { + finCount = finEff.length-1; + } + + aFins += finComponent * finEff[finCount]; + + } else if (component instanceof SymmetricComponent ){ + aBt += ((SymmetricComponent) component) .getComponentPlanformArea(); + } + } + + drag = ( cDFin * aFins + cDBt * aBt ); } } diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java index 0d5ac5c4a..d0ec8da67 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java @@ -18,7 +18,6 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { @Override public void step(SimulationStatus status, double maxTimeStep) throws SimulationException { - double refArea = status.getConfiguration().getReferenceArea(); // Get the atmospheric conditions AtmosphericConditions atmosphere = modelAtmosphericConditions(status); @@ -30,11 +29,11 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { // Get total CD double mach = airSpeed.length() / atmosphere.getMachSpeed(); - double totalCD = ((BasicTumbleStatus)status).getTumbleCd(); + double tumbleDrag = ((BasicTumbleStatus)status).getTumbleDrag(); // Compute drag force double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); - double dragForce = totalCD * dynP * refArea; + double dragForce = tumbleDrag * dynP; MassData massData = calculateMassData(status); double mass = massData.getCG().weight;