From b6aaa3857af8678c60ddd70f54d53959deeddc79 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 21 Apr 2023 11:13:11 -0600 Subject: [PATCH 1/7] Convert BasicTumbleStepper and BasicTumbleStatus to use CD instead of drag (for consistency with everywhere else) --- .../openrocket/simulation/BasicTumbleStatus.java | 16 ++++++++-------- .../simulation/BasicTumbleStepper.java | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java index 324789b61..f534c3fc9 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java @@ -21,29 +21,29 @@ public class BasicTumbleStatus extends SimulationStatus { // 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 final double drag; + private final double cd; public BasicTumbleStatus(FlightConfiguration configuration, SimulationConditions simulationConditions) { super(configuration, simulationConditions); - this.drag = computeTumbleDrag(); + this.cd = computeTumbleCD(); } public BasicTumbleStatus(SimulationStatus orig) { super(orig); if (orig instanceof BasicTumbleStatus) { - this.drag = ((BasicTumbleStatus) orig).drag; + this.cd = ((BasicTumbleStatus) orig).cd; } else { - this.drag = computeTumbleDrag(); + this.cd = computeTumbleCD(); } } - public double getTumbleDrag() { - return drag; + public double getCD() { + return cd; } - private double computeTumbleDrag() { + private double computeTumbleCD() { // Computed based on Sampo's experimentation as documented in the pdf. @@ -80,6 +80,6 @@ public class BasicTumbleStatus extends SimulationStatus { } } - return (cDFin * aFins + cDBt * aBt); + return (cDFin * aFins + cDBt * aBt)/getConfiguration().getReferenceArea(); } } diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java index cd8750e23..fca6fca37 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java @@ -33,11 +33,11 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { // Get total CD double mach = airSpeed.length() / atmosphere.getMachSpeed(); - double tumbleDrag = ((BasicTumbleStatus)status).getTumbleDrag(); + double tumbleCD = ((BasicTumbleStatus)status).getCD(); // Compute drag force double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); - double dragForce = tumbleDrag * dynP; + double dragForce = status.getConfiguration().getReferenceArea() * tumbleCD * dynP; // n.b. this is constant, and could be calculated once at the beginning of this simulation branch... double rocketMass = calculateStructureMass(status).getMass(); From 77d4bc890f6f057db6a5359f5fcc85f75b6d3a20 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 21 Apr 2023 11:37:05 -0600 Subject: [PATCH 2/7] Move tumble drag calculations into BasicTumbleStepper and eliminate BasicTumbleStatus --- .../simulation/BasicTumbleStatus.java | 85 ------------------- .../simulation/BasicTumbleStepper.java | 73 ++++++++++++++-- 2 files changed, 68 insertions(+), 90 deletions(-) delete mode 100644 core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java deleted file mode 100644 index f534c3fc9..000000000 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStatus.java +++ /dev/null @@ -1,85 +0,0 @@ -package net.sf.openrocket.simulation; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; - -import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.FlightConfiguration; -import net.sf.openrocket.rocketcomponent.InstanceContext; -import net.sf.openrocket.rocketcomponent.InstanceMap; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.SymmetricComponent; - -public class BasicTumbleStatus extends SimulationStatus { - - // 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 final double cd; - - public BasicTumbleStatus(FlightConfiguration configuration, - SimulationConditions simulationConditions) { - super(configuration, simulationConditions); - this.cd = computeTumbleCD(); - } - - public BasicTumbleStatus(SimulationStatus orig) { - super(orig); - if (orig instanceof BasicTumbleStatus) { - this.cd = ((BasicTumbleStatus) orig).cd; - } else { - this.cd = computeTumbleCD(); - } - } - - public double getCD() { - return cd; - } - - - private double computeTumbleCD() { - - // 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; - final InstanceMap imap = this.getConfiguration().getActiveInstances(); - for(Map.Entry> entry: imap.entrySet() ) { - final RocketComponent component = entry.getKey(); - - if (!component.isAerodynamic()) { - continue; - } - - // iterate across component instances - final ArrayList contextList = entry.getValue(); - for(InstanceContext context: contextList ) { - - if (component instanceof FinSet) { - final FinSet finComponent = ((FinSet) component); - final double finArea = finComponent.getPlanformArea(); - int finCount = finComponent.getFinCount(); - - // check bounds on finCount. - if (finCount >= finEff.length) { - finCount = finEff.length - 1; - } - - aFins += finArea * finEff[finCount] / finComponent.getFinCount(); - - } else if (component instanceof SymmetricComponent) { - aBt += ((SymmetricComponent) component).getComponentPlanformArea(); - } - } - } - - return (cDFin * aFins + cDBt * aBt)/getConfiguration().getReferenceArea(); - } -} diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java index fca6fca37..eae5a2f72 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java @@ -1,5 +1,16 @@ package net.sf.openrocket.simulation; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; +import net.sf.openrocket.rocketcomponent.InstanceContext; +import net.sf.openrocket.rocketcomponent.InstanceMap; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; import net.sf.openrocket.models.atmosphere.AtmosphericConditions; import net.sf.openrocket.simulation.exception.SimulationException; @@ -12,14 +23,66 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { private static final double RECOVERY_TIME_STEP = 0.5; + // 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 cd; + @Override - public SimulationStatus initialize(SimulationStatus original) { - BasicTumbleStatus status = new BasicTumbleStatus(original); - status.setWarnings(original.getWarnings()); - + public SimulationStatus initialize(SimulationStatus status) { + this.cd = computeTumbleCD(status); return status; } + private double getCD() { + return cd; + } + + private double computeTumbleCD(SimulationStatus status) { + + // 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; + final InstanceMap imap = status.getConfiguration().getActiveInstances(); + for(Map.Entry> entry: imap.entrySet() ) { + final RocketComponent component = entry.getKey(); + + if (!component.isAerodynamic()) { + continue; + } + + // iterate across component instances + final ArrayList contextList = entry.getValue(); + for(InstanceContext context: contextList ) { + + if (component instanceof FinSet) { + final FinSet finComponent = ((FinSet) component); + final double finArea = finComponent.getPlanformArea(); + int finCount = finComponent.getFinCount(); + + // check bounds on finCount. + if (finCount >= finEff.length) { + finCount = finEff.length - 1; + } + + aFins += finArea * finEff[finCount] / finComponent.getFinCount(); + + } else if (component instanceof SymmetricComponent) { + aBt += ((SymmetricComponent) component).getComponentPlanformArea(); + } + } + } + + return (cDFin * aFins + cDBt * aBt)/status.getConfiguration().getReferenceArea(); + } + + @Override public void step(SimulationStatus status, double maxTimeStep) throws SimulationException { @@ -33,7 +96,7 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { // Get total CD double mach = airSpeed.length() / atmosphere.getMachSpeed(); - double tumbleCD = ((BasicTumbleStatus)status).getCD(); + double tumbleCD = getCD(); // Compute drag force double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); From dfd7e04db502f1e50db35792fd69bb0d576c657c Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 21 Apr 2023 16:18:49 -0600 Subject: [PATCH 3/7] Pull CD calculation out of BasicLandingStepper sim step Aligh BasicLandingStepper and BasicTumbleStepper sim steps --- .../simulation/BasicLandingStepper.java | 32 ++++++++++++------- .../simulation/BasicTumbleStepper.java | 12 +++---- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java b/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java index de7db0bab..bdd92d8dd 100644 --- a/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java @@ -16,16 +16,31 @@ public class BasicLandingStepper extends AbstractSimulationStepper { private static final Logger log = LoggerFactory.getLogger(BasicLandingStepper.class); private static final double RECOVERY_TIME_STEP = 0.5; + + private double cd; @Override public SimulationStatus initialize(SimulationStatus status) { + this.cd = computeCD(status); return status; } + + private double getCD() { + return cd; + } + + private double computeCD(SimulationStatus status) { + // Accumulate CD for all recovery devices + cd = 0; + final InstanceMap imap = status.getConfiguration().getActiveInstances(); + for (RecoveryDevice c : status.getDeployedRecoveryDevices()) { + cd += imap.count(c) * c.getCD() * c.getArea() / status.getConfiguration().getReferenceArea(); + } + return cd; + } @Override public void step(SimulationStatus status, double maxTimeStep) throws SimulationException { - double totalCD = 0; - double refArea = status.getConfiguration().getReferenceArea(); // Get the atmospheric conditions AtmosphericConditions atmosphere = modelAtmosphericConditions(status); @@ -34,19 +49,12 @@ public class BasicLandingStepper extends AbstractSimulationStepper { Coordinate windSpeed = modelWindVelocity(status); Coordinate airSpeed = status.getRocketVelocity().add(windSpeed); - // Get total CD - double mach = airSpeed.length() / atmosphere.getMachSpeed(); - - final InstanceMap imap = status.getConfiguration().getActiveInstances(); - for (RecoveryDevice c : status.getDeployedRecoveryDevices()) { - totalCD += imap.count(c) * c.getCD(mach) * c.getArea() / refArea; - } - // Compute drag force + double mach = airSpeed.length() / atmosphere.getMachSpeed(); double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); - double dragForce = totalCD * dynP * refArea; + double dragForce = getCD() * dynP * status.getConfiguration().getReferenceArea(); - // Calculate mass data + // n.b. this is constant, and could be calculated once at the beginning of this simulation branch... double rocketMass = calculateStructureMass(status).getMass(); double motorMass = calculateMotorMass(status).getMass(); diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java index eae5a2f72..c22c327dc 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java @@ -34,7 +34,7 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { @Override public SimulationStatus initialize(SimulationStatus status) { - this.cd = computeTumbleCD(status); + this.cd = computeCD(status); return status; } @@ -42,7 +42,7 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { return cd; } - private double computeTumbleCD(SimulationStatus status) { + private double computeCD(SimulationStatus status) { // Computed based on Sampo's experimentation as documented in the pdf. @@ -93,14 +93,10 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { Coordinate windSpeed = modelWindVelocity(status); Coordinate airSpeed = status.getRocketVelocity().add(windSpeed); - // Get total CD - double mach = airSpeed.length() / atmosphere.getMachSpeed(); - - double tumbleCD = getCD(); - // Compute drag force + double mach = airSpeed.length() / atmosphere.getMachSpeed(); double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); - double dragForce = status.getConfiguration().getReferenceArea() * tumbleCD * dynP; + double dragForce = status.getConfiguration().getReferenceArea() * getCD() * dynP; // n.b. this is constant, and could be calculated once at the beginning of this simulation branch... double rocketMass = calculateStructureMass(status).getMass(); From 4954099b403911cb0feae0719b9d7fd8b3120eb9 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 21 Apr 2023 16:30:49 -0600 Subject: [PATCH 4/7] incorporate adaptive time step in tumble stepper --- .../simulation/BasicLandingStepper.java | 9 ++++----- .../simulation/BasicTumbleStepper.java | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java b/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java index bdd92d8dd..909871732 100644 --- a/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java @@ -1,5 +1,8 @@ package net.sf.openrocket.simulation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.sf.openrocket.models.atmosphere.AtmosphericConditions; import net.sf.openrocket.rocketcomponent.InstanceMap; import net.sf.openrocket.rocketcomponent.RecoveryDevice; @@ -9,9 +12,6 @@ import net.sf.openrocket.util.GeodeticComputationStrategy; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.WorldCoordinate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class BasicLandingStepper extends AbstractSimulationStepper { private static final Logger log = LoggerFactory.getLogger(BasicLandingStepper.class); @@ -77,8 +77,6 @@ public class BasicLandingStepper extends AbstractSimulationStepper { Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration( status.getRocketWorldPosition(), status.getRocketVelocity()); linearAcceleration = linearAcceleration.add(coriolisAcceleration); - - // Select tentative time step double timeStep = RECOVERY_TIME_STEP; @@ -88,6 +86,7 @@ public class BasicLandingStepper extends AbstractSimulationStepper { if (jerk > MathUtil.EPSILON) { timeStep = Math.min(timeStep, 1.0/jerk); } + // but don't let it get *too* small timeStep = Math.max(timeStep, MIN_TIME_STEP); log.trace("timeStep is " + timeStep); diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java index c22c327dc..a378b1529 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java @@ -4,6 +4,9 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.InstanceContext; @@ -20,6 +23,7 @@ import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.WorldCoordinate; public class BasicTumbleStepper extends AbstractSimulationStepper { + private static final Logger log = LoggerFactory.getLogger(BasicTumbleStepper.class); private static final double RECOVERY_TIME_STEP = 0.5; @@ -124,8 +128,18 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { - // Select time step + // Select tentative time step double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP); + + // adjust based on change in acceleration (ie jerk) + final double jerk = Math.abs(linearAcceleration.sub(status.getRocketAcceleration()).multiply(1.0/status.getPreviousTimeStep()).length()); + if (jerk > MathUtil.EPSILON) { + timeStep = Math.min(timeStep, 1.0/jerk); + } + + // but don't let it get *too* small + timeStep = Math.max(timeStep, MIN_TIME_STEP); + log.trace("timeStep is " + timeStep); // Perform Euler integration Coordinate newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). From 44cf2eafca91f19b8d6d857a13440ea919e138d3 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 21 Apr 2023 16:39:08 -0600 Subject: [PATCH 5/7] save all the same data in tumble stepper as landing stepper --- .../net/sf/openrocket/simulation/BasicTumbleStepper.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java index a378b1529..e3e9f7198 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java @@ -163,17 +163,18 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { newPosition = newPosition.setZ(0); } + status.setSimulationTime(status.getSimulationTime() + timeStep); + status.setPreviousTimeStep(timeStep); + status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2))); status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep))); - status.setSimulationTime(status.getSimulationTime() + timeStep); - + status.setRocketAcceleration(linearAcceleration); // Update the world coordinate WorldCoordinate w = status.getSimulationConditions().getLaunchSite(); w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition()); status.setRocketWorldPosition(w); - // Store data FlightDataBranch data = status.getFlightData(); @@ -184,6 +185,8 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z); data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x); data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y); + + airSpeed = status.getRocketVelocity().add(windSpeed); if (extra) { data.setValue(FlightDataType.TYPE_POSITION_XY, MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y)); From 7fc86325741798f113656c6aee6c88f32e3ae500 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 21 Apr 2023 17:09:29 -0600 Subject: [PATCH 6/7] Pull common code from BasicLandingStepper and BasicTumbleStepper into new abstract class AbstractEulerStepper --- .../simulation/BasicLandingStepper.java | 178 +--------------- .../simulation/BasicTumbleStepper.java | 199 +----------------- 2 files changed, 12 insertions(+), 365 deletions(-) diff --git a/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java b/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java index 909871732..cf0f56804 100644 --- a/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicLandingStepper.java @@ -1,35 +1,12 @@ package net.sf.openrocket.simulation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.sf.openrocket.models.atmosphere.AtmosphericConditions; import net.sf.openrocket.rocketcomponent.InstanceMap; import net.sf.openrocket.rocketcomponent.RecoveryDevice; -import net.sf.openrocket.simulation.exception.SimulationException; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.GeodeticComputationStrategy; -import net.sf.openrocket.util.MathUtil; -import net.sf.openrocket.util.WorldCoordinate; -public class BasicLandingStepper extends AbstractSimulationStepper { - private static final Logger log = LoggerFactory.getLogger(BasicLandingStepper.class); - - private static final double RECOVERY_TIME_STEP = 0.5; +public class BasicLandingStepper extends AbstractEulerStepper { - private double cd; - @Override - public SimulationStatus initialize(SimulationStatus status) { - this.cd = computeCD(status); - return status; - } - - private double getCD() { - return cd; - } - - private double computeCD(SimulationStatus status) { + protected double computeCD(SimulationStatus status) { // Accumulate CD for all recovery devices cd = 0; final InstanceMap imap = status.getConfiguration().getActiveInstances(); @@ -38,155 +15,4 @@ public class BasicLandingStepper extends AbstractSimulationStepper { } return cd; } - - @Override - public void step(SimulationStatus status, double maxTimeStep) throws SimulationException { - - // Get the atmospheric conditions - AtmosphericConditions atmosphere = modelAtmosphericConditions(status); - - //// Local wind speed and direction - Coordinate windSpeed = modelWindVelocity(status); - Coordinate airSpeed = status.getRocketVelocity().add(windSpeed); - - // Compute drag force - double mach = airSpeed.length() / atmosphere.getMachSpeed(); - double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); - double dragForce = getCD() * dynP * status.getConfiguration().getReferenceArea(); - - // n.b. this is constant, and could be calculated once at the beginning of this simulation branch... - double rocketMass = calculateStructureMass(status).getMass(); - double motorMass = calculateMotorMass(status).getMass(); - - double mass = rocketMass + motorMass; - - // Compute drag acceleration - Coordinate linearAcceleration; - if (airSpeed.length() > 0.001) { - linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass); - } else { - linearAcceleration = Coordinate.NUL; - } - - // Add effect of gravity - double gravity = modelGravity(status); - linearAcceleration = linearAcceleration.sub(0, 0, gravity); - - - // Add coriolis acceleration - Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration( - status.getRocketWorldPosition(), status.getRocketVelocity()); - linearAcceleration = linearAcceleration.add(coriolisAcceleration); - - // Select tentative time step - double timeStep = RECOVERY_TIME_STEP; - - // adjust based on change in acceleration (ie jerk) - final double jerk = Math.abs(linearAcceleration.sub(status.getRocketAcceleration()).multiply(1.0/status.getPreviousTimeStep()).length()); - if (jerk > MathUtil.EPSILON) { - timeStep = Math.min(timeStep, 1.0/jerk); - } - - // but don't let it get *too* small - timeStep = Math.max(timeStep, MIN_TIME_STEP); - log.trace("timeStep is " + timeStep); - - // Perform Euler integration - Coordinate newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). - add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)); - - // If I've hit the ground, recalculate time step and position - if (newPosition.z < 0) { - - final double a = linearAcceleration.z; - final double v = status.getRocketVelocity().z; - final double z0 = status.getRocketPosition().z; - - // The new timestep is the solution of - // 1/2 at^2 + vt + z0 = 0 - timeStep = (-v - Math.sqrt(v*v - 2*a*z0))/a; - log.trace("ground hit changes timeStep to " + timeStep); - - newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). - add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)); - - // avoid rounding error in new altitude - newPosition = newPosition.setZ(0); - } - - status.setSimulationTime(status.getSimulationTime() + timeStep); - status.setPreviousTimeStep(timeStep); - - status.setRocketPosition(newPosition); - status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep))); - status.setRocketAcceleration(linearAcceleration); - - // Update the world coordinate - WorldCoordinate w = status.getSimulationConditions().getLaunchSite(); - w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition()); - status.setRocketWorldPosition(w); - - // Store data - FlightDataBranch data = status.getFlightData(); - boolean extra = status.getSimulationConditions().isCalculateExtras(); - data.addPoint(); - - data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime()); - data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z); - data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x); - data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y); - - airSpeed = status.getRocketVelocity().add(windSpeed); - if (extra) { - data.setValue(FlightDataType.TYPE_POSITION_XY, - MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y)); - data.setValue(FlightDataType.TYPE_POSITION_DIRECTION, - Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x)); - - data.setValue(FlightDataType.TYPE_VELOCITY_XY, - MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y)); - data.setValue(FlightDataType.TYPE_ACCELERATION_XY, - MathUtil.hypot(linearAcceleration.x, linearAcceleration.y)); - - data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length()); - - double Re = airSpeed.length() * - status.getConfiguration().getLengthAerodynamic() / - atmosphere.getKinematicViscosity(); - data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re); - } - - - data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad()); - data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad()); - data.setValue(FlightDataType.TYPE_GRAVITY, gravity); - - if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) { - data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length()); - } - - - data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z); - data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z); - - data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length()); - data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach); - - data.setValue(FlightDataType.TYPE_MASS, mass); - data.setValue(FlightDataType.TYPE_MOTOR_MASS, motorMass); - - data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0); - data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce); - - data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length()); - data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature()); - data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure()); - data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed()); - - data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep); - data.setValue(FlightDataType.TYPE_COMPUTATION_TIME, - (System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0); - log.trace("time " + data.getLast(FlightDataType.TYPE_TIME) + ", altitude " + data.getLast(FlightDataType.TYPE_ALTITUDE) + ", velocity " + data.getLast(FlightDataType.TYPE_VELOCITY_Z)); - } - } diff --git a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java index e3e9f7198..6eeaa47f3 100644 --- a/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java +++ b/core/src/net/sf/openrocket/simulation/BasicTumbleStepper.java @@ -4,51 +4,24 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import net.sf.openrocket.rocketcomponent.FinSet; -import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.InstanceContext; import net.sf.openrocket.rocketcomponent.InstanceMap; -import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.SymmetricComponent; -import net.sf.openrocket.models.atmosphere.AtmosphericConditions; -import net.sf.openrocket.simulation.exception.SimulationException; -import net.sf.openrocket.util.Coordinate; -import net.sf.openrocket.util.GeodeticComputationStrategy; -import net.sf.openrocket.util.MathUtil; -import net.sf.openrocket.util.WorldCoordinate; - -public class BasicTumbleStepper extends AbstractSimulationStepper { - private static final Logger log = LoggerFactory.getLogger(BasicTumbleStepper.class); +public class BasicTumbleStepper extends AbstractEulerStepper { - private static final double RECOVERY_TIME_STEP = 0.5; - - // 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 cd; - - @Override - public SimulationStatus initialize(SimulationStatus status) { - this.cd = computeCD(status); - return status; - } - - private double getCD() { - return cd; - } - - private double computeCD(SimulationStatus status) { + public double computeCD(SimulationStatus status) { - // Computed based on Sampo's experimentation as documented in the pdf. + // Computed based on Sampo's experimentation as documented in techdoc.pdf. + + // Magic constants from techdoc.pdf + final double cDFin = 1.42; + final 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 + final double[] finEff = { 0.0, 0.5, 1.0, 1.41, 1.81, 1.73, 1.90, 1.85 }; // compute the fin and body tube projected areas double aFins = 0.0; @@ -85,157 +58,5 @@ public class BasicTumbleStepper extends AbstractSimulationStepper { return (cDFin * aFins + cDBt * aBt)/status.getConfiguration().getReferenceArea(); } - - - @Override - public void step(SimulationStatus status, double maxTimeStep) throws SimulationException { - - // Get the atmospheric conditions - AtmosphericConditions atmosphere = modelAtmosphericConditions(status); - - //// Local wind speed and direction - Coordinate windSpeed = modelWindVelocity(status); - Coordinate airSpeed = status.getRocketVelocity().add(windSpeed); - - // Compute drag force - double mach = airSpeed.length() / atmosphere.getMachSpeed(); - double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); - double dragForce = status.getConfiguration().getReferenceArea() * getCD() * dynP; - - // n.b. this is constant, and could be calculated once at the beginning of this simulation branch... - double rocketMass = calculateStructureMass(status).getMass(); - double motorMass = calculateMotorMass(status).getMass(); - - double mass = rocketMass + motorMass; - - // Compute drag acceleration - Coordinate linearAcceleration; - if (airSpeed.length() > 0.001) { - linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass); - } else { - linearAcceleration = Coordinate.NUL; - } - - // Add effect of gravity - double gravity = modelGravity(status); - linearAcceleration = linearAcceleration.sub(0, 0, gravity); - - - // Add coriolis acceleration - Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration( - status.getRocketWorldPosition(), status.getRocketVelocity()); - linearAcceleration = linearAcceleration.add(coriolisAcceleration); - - - - // Select tentative time step - double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP); - - // adjust based on change in acceleration (ie jerk) - final double jerk = Math.abs(linearAcceleration.sub(status.getRocketAcceleration()).multiply(1.0/status.getPreviousTimeStep()).length()); - if (jerk > MathUtil.EPSILON) { - timeStep = Math.min(timeStep, 1.0/jerk); - } - - // but don't let it get *too* small - timeStep = Math.max(timeStep, MIN_TIME_STEP); - log.trace("timeStep is " + timeStep); - - // Perform Euler integration - Coordinate newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). - add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)); - - // If I've hit the ground, recalculate time step and position - if (newPosition.z < 0) { - - final double a = linearAcceleration.z; - final double v = status.getRocketVelocity().z; - final double z0 = status.getRocketPosition().z; - - // The new timestep is the solution of - // 1/2 at^2 + vt + z0 = 0 - timeStep = (-v - Math.sqrt(v*v - 2*a*z0))/a; - - newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). - add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)); - - // avoid rounding error in new altitude - newPosition = newPosition.setZ(0); - } - - status.setSimulationTime(status.getSimulationTime() + timeStep); - status.setPreviousTimeStep(timeStep); - - status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). - add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2))); - status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep))); - status.setRocketAcceleration(linearAcceleration); - - // Update the world coordinate - WorldCoordinate w = status.getSimulationConditions().getLaunchSite(); - w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition()); - status.setRocketWorldPosition(w); - - // Store data - FlightDataBranch data = status.getFlightData(); - boolean extra = status.getSimulationConditions().isCalculateExtras(); - data.addPoint(); - - data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime()); - data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z); - data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x); - data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y); - - airSpeed = status.getRocketVelocity().add(windSpeed); - if (extra) { - data.setValue(FlightDataType.TYPE_POSITION_XY, - MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y)); - data.setValue(FlightDataType.TYPE_POSITION_DIRECTION, - Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x)); - - data.setValue(FlightDataType.TYPE_VELOCITY_XY, - MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y)); - data.setValue(FlightDataType.TYPE_ACCELERATION_XY, - MathUtil.hypot(linearAcceleration.x, linearAcceleration.y)); - - data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length()); - - double Re = airSpeed.length() * - status.getConfiguration().getLengthAerodynamic() / - atmosphere.getKinematicViscosity(); - data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re); - } - - - data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad()); - data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad()); - data.setValue(FlightDataType.TYPE_GRAVITY, gravity); - - if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) { - data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length()); - } - - - data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z); - data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z); - - data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length()); - data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach); - - data.setValue(FlightDataType.TYPE_MASS, mass); - data.setValue(FlightDataType.TYPE_MOTOR_MASS, motorMass); - - data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0); - data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce); - - data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length()); - data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature()); - data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure()); - data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed()); - - data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep); - data.setValue(FlightDataType.TYPE_COMPUTATION_TIME, - (System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0); - } } From 2d8799a862e34c5fd56c2c83330276f80d5fe968 Mon Sep 17 00:00:00 2001 From: JoePfeiffer Date: Fri, 21 Apr 2023 17:14:10 -0600 Subject: [PATCH 7/7] Needed to commit the new class --- .../simulation/AbstractEulerStepper.java | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 core/src/net/sf/openrocket/simulation/AbstractEulerStepper.java diff --git a/core/src/net/sf/openrocket/simulation/AbstractEulerStepper.java b/core/src/net/sf/openrocket/simulation/AbstractEulerStepper.java new file mode 100644 index 000000000..a004b1069 --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/AbstractEulerStepper.java @@ -0,0 +1,184 @@ +package net.sf.openrocket.simulation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.sf.openrocket.models.atmosphere.AtmosphericConditions; +import net.sf.openrocket.rocketcomponent.InstanceMap; +import net.sf.openrocket.rocketcomponent.RecoveryDevice; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.GeodeticComputationStrategy; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.util.WorldCoordinate; + +public abstract class AbstractEulerStepper extends AbstractSimulationStepper { + private static final Logger log = LoggerFactory.getLogger(AbstractEulerStepper.class); + + private static final double RECOVERY_TIME_STEP = 0.5; + + protected double cd; + + @Override + public SimulationStatus initialize(SimulationStatus status) { + this.cd = computeCD(status); + return status; + } + + private double getCD() { + return cd; + } + + protected abstract double computeCD(SimulationStatus status); + + @Override + public void step(SimulationStatus status, double maxTimeStep) throws SimulationException { + + // Get the atmospheric conditions + AtmosphericConditions atmosphere = modelAtmosphericConditions(status); + + //// Local wind speed and direction + Coordinate windSpeed = modelWindVelocity(status); + Coordinate airSpeed = status.getRocketVelocity().add(windSpeed); + + // Compute drag force + double mach = airSpeed.length() / atmosphere.getMachSpeed(); + double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2()); + double dragForce = getCD() * dynP * status.getConfiguration().getReferenceArea(); + + // n.b. this is constant, and could be calculated once at the beginning of this simulation branch... + double rocketMass = calculateStructureMass(status).getMass(); + double motorMass = calculateMotorMass(status).getMass(); + + double mass = rocketMass + motorMass; + + // Compute drag acceleration + Coordinate linearAcceleration; + if (airSpeed.length() > 0.001) { + linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass); + } else { + linearAcceleration = Coordinate.NUL; + } + + // Add effect of gravity + double gravity = modelGravity(status); + linearAcceleration = linearAcceleration.sub(0, 0, gravity); + + + // Add coriolis acceleration + Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration( + status.getRocketWorldPosition(), status.getRocketVelocity()); + linearAcceleration = linearAcceleration.add(coriolisAcceleration); + + // Select tentative time step + double timeStep = RECOVERY_TIME_STEP; + + // adjust based on change in acceleration (ie jerk) + final double jerk = Math.abs(linearAcceleration.sub(status.getRocketAcceleration()).multiply(1.0/status.getPreviousTimeStep()).length()); + if (jerk > MathUtil.EPSILON) { + timeStep = Math.min(timeStep, 1.0/jerk); + } + + // but don't let it get *too* small + timeStep = Math.max(timeStep, MIN_TIME_STEP); + log.trace("timeStep is " + timeStep); + + // Perform Euler integration + Coordinate newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). + add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)); + + // If I've hit the ground, recalculate time step and position + if (newPosition.z < 0) { + + final double a = linearAcceleration.z; + final double v = status.getRocketVelocity().z; + final double z0 = status.getRocketPosition().z; + + // The new timestep is the solution of + // 1/2 at^2 + vt + z0 = 0 + timeStep = (-v - Math.sqrt(v*v - 2*a*z0))/a; + log.trace("ground hit changes timeStep to " + timeStep); + + newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)). + add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)); + + // avoid rounding error in new altitude + newPosition = newPosition.setZ(0); + } + + status.setSimulationTime(status.getSimulationTime() + timeStep); + status.setPreviousTimeStep(timeStep); + + status.setRocketPosition(newPosition); + status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep))); + status.setRocketAcceleration(linearAcceleration); + + // Update the world coordinate + WorldCoordinate w = status.getSimulationConditions().getLaunchSite(); + w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition()); + status.setRocketWorldPosition(w); + + // Store data + FlightDataBranch data = status.getFlightData(); + boolean extra = status.getSimulationConditions().isCalculateExtras(); + data.addPoint(); + + data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime()); + data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z); + data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x); + data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y); + + airSpeed = status.getRocketVelocity().add(windSpeed); + if (extra) { + data.setValue(FlightDataType.TYPE_POSITION_XY, + MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y)); + data.setValue(FlightDataType.TYPE_POSITION_DIRECTION, + Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x)); + + data.setValue(FlightDataType.TYPE_VELOCITY_XY, + MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y)); + data.setValue(FlightDataType.TYPE_ACCELERATION_XY, + MathUtil.hypot(linearAcceleration.x, linearAcceleration.y)); + + data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length()); + + double Re = airSpeed.length() * + status.getConfiguration().getLengthAerodynamic() / + atmosphere.getKinematicViscosity(); + data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re); + } + + + data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad()); + data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad()); + data.setValue(FlightDataType.TYPE_GRAVITY, gravity); + + if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) { + data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length()); + } + + + data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z); + data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z); + + data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length()); + data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach); + + data.setValue(FlightDataType.TYPE_MASS, mass); + data.setValue(FlightDataType.TYPE_MOTOR_MASS, motorMass); + + data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0); + data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce); + + data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length()); + data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature()); + data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure()); + data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed()); + + data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep); + data.setValue(FlightDataType.TYPE_COMPUTATION_TIME, + (System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0); + log.trace("time " + data.getLast(FlightDataType.TYPE_TIME) + ", altitude " + data.getLast(FlightDataType.TYPE_ALTITUDE) + ", velocity " + data.getLast(FlightDataType.TYPE_VELOCITY_Z)); + } + +}