diff --git a/core/resources-src/datafiles/openrocket-database b/core/resources-src/datafiles/openrocket-database index 48eb2172d..93054dc2b 160000 --- a/core/resources-src/datafiles/openrocket-database +++ b/core/resources-src/datafiles/openrocket-database @@ -1 +1 @@ -Subproject commit 48eb2172d58c0db6042bd847a782835df056b287 +Subproject commit 93054dc2b40d5196433b1d91c636cee2e9dda424 diff --git a/core/src/main/java/info/openrocket/core/simulation/AbstractEulerStepper.java b/core/src/main/java/info/openrocket/core/simulation/AbstractEulerStepper.java index d1c07935d..1dcb406fb 100644 --- a/core/src/main/java/info/openrocket/core/simulation/AbstractEulerStepper.java +++ b/core/src/main/java/info/openrocket/core/simulation/AbstractEulerStepper.java @@ -4,7 +4,9 @@ import info.openrocket.core.logging.SimulationAbort; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import info.openrocket.core.aerodynamics.AerodynamicForces; import info.openrocket.core.l10n.Translator; +import info.openrocket.core.masscalc.RigidBody; import info.openrocket.core.models.atmosphere.AtmosphericConditions; import info.openrocket.core.rocketcomponent.InstanceMap; import info.openrocket.core.rocketcomponent.RecoveryDevice; @@ -21,16 +23,25 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper { private static final double RECOVERY_TIME_STEP = 0.5; - protected double cd; + DataStore store = new DataStore(); @Override public SimulationStatus initialize(SimulationStatus status) { - this.cd = computeCD(status); - return status; - } + store.thrustForce = 0; + + // note most of our forces don't end up getting set, so they're all NaN. + AerodynamicForces forces = new AerodynamicForces(); - private double getCD() { - return cd; + double cd = computeCD(status); + forces.setCD(cd); + forces.setCDaxial(cd); + forces.setFrictionCD(0); + forces.setPressureCD(cd); + forces.setBaseCD(0); + + store.forces = forces; + + return status; } protected abstract double computeCD(SimulationStatus status); @@ -39,63 +50,62 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper { public void step(SimulationStatus status, double maxTimeStep) throws SimulationException { // Get the atmospheric conditions - final AtmosphericConditions atmosphere = modelAtmosphericConditions(status); + store.atmosphericConditions = modelAtmosphericConditions(status); //// Local wind speed and direction - final Coordinate windSpeed = modelWindVelocity(status); - Coordinate airSpeed = status.getRocketVelocity().add(windSpeed); + store.windVelocity = modelWindVelocity(status); + Coordinate airSpeed = status.getRocketVelocity().add(store.windVelocity); // Compute drag force - final double mach = airSpeed.length() / atmosphere.getMachSpeed(); - final double CdA = getCD() * status.getConfiguration().getReferenceArea(); - final double dragForce = 0.5 * CdA * atmosphere.getDensity() * airSpeed.length2(); + final double mach = airSpeed.length() / store.atmosphericConditions.getMachSpeed(); + final double CdA = store.forces.getCD() * status.getConfiguration().getReferenceArea(); + store.dragForce = 0.5 * CdA * store.atmosphericConditions.getDensity() * airSpeed.length2(); - final double rocketMass = calculateStructureMass(status).getMass(); - final double motorMass = calculateMotorMass(status).getMass(); + RigidBody structureMassData = calculateStructureMass(status); + store.motorMass = calculateMotorMass(status); + store.rocketMass = structureMassData.add( store.motorMass ); - final double mass = rocketMass + motorMass; - if (mass < MathUtil.EPSILON) { + if (store.rocketMass.getMass() < MathUtil.EPSILON) { status.abortSimulation(SimulationAbort.Cause.ACTIVE_MASS_ZERO); } // Compute drag acceleration - Coordinate linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass); + store.linearAcceleration = airSpeed.normalize().multiply(-store.dragForce / store.rocketMass.getMass()); // Add effect of gravity - final double gravity = modelGravity(status); - linearAcceleration = linearAcceleration.sub(0, 0, gravity); - + store.gravity = modelGravity(status); + store.linearAcceleration = store.linearAcceleration.sub(0, 0, store.gravity); // Add coriolis acceleration - final Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration( + store.coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration( status.getRocketWorldPosition(), status.getRocketVelocity()); - linearAcceleration = linearAcceleration.add(coriolisAcceleration); + store.linearAcceleration = store.linearAcceleration.add(store.coriolisAcceleration); // Select tentative time step - double timeStep = RECOVERY_TIME_STEP; + store.timeStep = RECOVERY_TIME_STEP; // adjust based on acceleration - final double absAccel = linearAcceleration.length(); + final double absAccel = store.linearAcceleration.length(); if (absAccel > MathUtil.EPSILON) { - timeStep = Math.min(timeStep, 1.0/absAccel); + store.timeStep = Math.min(store.timeStep, 1.0/absAccel); } // Honor max step size passed in. If the time to next time step is greater than our minimum // we'll set our next step to just before it in order to better capture discontinuities in things like chute opening - if (maxTimeStep < timeStep) { + if (maxTimeStep < store.timeStep) { if (maxTimeStep > MIN_TIME_STEP) { - timeStep = maxTimeStep - MIN_TIME_STEP; + store.timeStep = maxTimeStep - MIN_TIME_STEP; } else { - timeStep = maxTimeStep; + store.timeStep = maxTimeStep; } } // but don't let it get *too* small - timeStep = Math.max(timeStep, MIN_TIME_STEP); - log.trace("timeStep is " + timeStep); + store.timeStep = Math.max(store.timeStep, MIN_TIME_STEP); + log.trace("timeStep is " + store.timeStep); // Perform Euler integration - EulerValues newVals = eulerIntegrate(status.getRocketPosition(), status.getRocketVelocity(), linearAcceleration, timeStep); + EulerValues newVals = eulerIntegrate(status.getRocketPosition(), status.getRocketVelocity(), store.linearAcceleration, store.timeStep); // Check to see if z or either of its first two derivatives have changed sign and recalculate // time step to point of change if so @@ -105,10 +115,10 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper { // Note that it's virtually impossible for apogee to occur on the same // step as either ground hit or descent rate inflection, and if we get a ground hit // any descent rate inflection won't matter - final double a = linearAcceleration.z; + final double a = store.linearAcceleration.z; final double v = status.getRocketVelocity().z; final double z = status.getRocketPosition().z; - double t = timeStep; + double t = store.timeStep; if (newVals.pos.z < 0) { // If I've hit the ground, the new timestep is the solution of // 1/2 at^2 + vt + z = 0 @@ -124,13 +134,13 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper { // calculations to get it "right"; this will be close enough for our purposes. // use chain rule to compute jerk // dA/dT = dA/dV * dV/dT - final double dFdV = CdA * atmosphere.getDensity() * airSpeed.length(); - final Coordinate dAdV = airSpeed.normalize().multiply(dFdV / mass); - final Coordinate jerk = linearAcceleration.multiply(dAdV); - final Coordinate newAcceleration = linearAcceleration.add(jerk.multiply(timeStep)); + final double dFdV = CdA * store.atmosphericConditions.getDensity() * airSpeed.length(); + final Coordinate dAdV = airSpeed.normalize().multiply(dFdV / store.rocketMass.getMass()); + final Coordinate jerk = store.linearAcceleration.multiply(dAdV); + final Coordinate newAcceleration = store.linearAcceleration.add(jerk.multiply(store.timeStep)); // Only do this one if acceleration is appreciably different from 0 - if (newAcceleration.z * linearAcceleration.z < -MathUtil.EPSILON) { + if (newAcceleration.z * store.linearAcceleration.z < -MathUtil.EPSILON) { // If acceleration oscillation is building up, the new timestep is the solution of // a + j*t = 0 t = Math.abs(a / jerk.z); @@ -142,14 +152,14 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper { t = Math.max(t, MIN_TIME_STEP); // recalculate Euler integration for position and velocity if necessary. - if (Math.abs(t - timeStep) > MathUtil.EPSILON) { - timeStep = t; + if (Math.abs(t - store.timeStep) > MathUtil.EPSILON) { + store.timeStep = t; - if (maxTimeStep - timeStep < MIN_TIME_STEP) { - timeStep = maxTimeStep; + if (maxTimeStep - store.timeStep < MIN_TIME_STEP) { + store.timeStep = maxTimeStep; } - newVals = eulerIntegrate(status.getRocketPosition(), status.getRocketVelocity(), linearAcceleration, timeStep); + newVals = eulerIntegrate(status.getRocketPosition(), status.getRocketVelocity(), store.linearAcceleration, store.timeStep); // If we just landed chop off rounding error if (Math.abs(newVals.pos.z) < MathUtil.EPSILON) { @@ -157,11 +167,11 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper { } } - status.setSimulationTime(status.getSimulationTime() + timeStep); + status.setSimulationTime(status.getSimulationTime() + store.timeStep); status.setRocketPosition(newVals.pos); status.setRocketVelocity(newVals.vel); - status.setRocketAcceleration(linearAcceleration); + status.setRocketAcceleration(store.linearAcceleration); // Update the world coordinate WorldCoordinate w = status.getSimulationConditions().getLaunchSite(); @@ -172,65 +182,18 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper { final FlightDataBranch dataBranch = status.getFlightDataBranch(); // Values looked up or calculated at start of time step - dataBranch.setValue(FlightDataType.TYPE_REFERENCE_LENGTH, status.getConfiguration().getReferenceLength()); - dataBranch.setValue(FlightDataType.TYPE_REFERENCE_AREA, status.getConfiguration().getReferenceArea()); - dataBranch.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length()); - dataBranch.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature()); - dataBranch.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure()); - dataBranch.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed()); - dataBranch.setValue(FlightDataType.TYPE_MACH_NUMBER, mach); - - if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) { - dataBranch.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length()); - } - dataBranch.setValue(FlightDataType.TYPE_GRAVITY, gravity); - - dataBranch.setValue(FlightDataType.TYPE_DRAG_COEFF, getCD()); - dataBranch.setValue(FlightDataType.TYPE_PRESSURE_DRAG_COEFF, getCD()); - dataBranch.setValue(FlightDataType.TYPE_FRICTION_DRAG_COEFF, 0); - dataBranch.setValue(FlightDataType.TYPE_BASE_DRAG_COEFF, 0); - dataBranch.setValue(FlightDataType.TYPE_AXIAL_DRAG_COEFF, getCD()); - dataBranch.setValue(FlightDataType.TYPE_THRUST_FORCE, 0); - dataBranch.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce); - - dataBranch.setValue(FlightDataType.TYPE_MASS, mass); - dataBranch.setValue(FlightDataType.TYPE_MOTOR_MASS, motorMass); - dataBranch.setValue(FlightDataType.TYPE_THRUST_WEIGHT_RATIO, 0); - - dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_XY, - MathUtil.hypot(linearAcceleration.x, linearAcceleration.y)); - dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z); - dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length()); - - dataBranch.setValue(FlightDataType.TYPE_TIME_STEP, timeStep); - + store.storeData(status); + // Values calculated on this step dataBranch.addPoint(); - dataBranch.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime()); - dataBranch.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z); - dataBranch.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x); - dataBranch.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y); + status.storeData(); - dataBranch.setValue(FlightDataType.TYPE_POSITION_XY, - MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y)); - dataBranch.setValue(FlightDataType.TYPE_POSITION_DIRECTION, - Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x)); - dataBranch.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad()); - dataBranch.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad()); - - dataBranch.setValue(FlightDataType.TYPE_VELOCITY_XY, - MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y)); - dataBranch.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z); - dataBranch.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length()); - - airSpeed = status.getRocketVelocity().add(windSpeed); + airSpeed = status.getRocketVelocity().add(store.windVelocity); final double Re = airSpeed.length() * status.getConfiguration().getLengthAerodynamic() / - atmosphere.getKinematicViscosity(); + store.atmosphericConditions.getKinematicViscosity(); dataBranch.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re); - dataBranch.setValue(FlightDataType.TYPE_COMPUTATION_TIME, - (System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0); log.trace("time " + dataBranch.getLast(FlightDataType.TYPE_TIME) + ", altitude " + dataBranch.getLast(FlightDataType.TYPE_ALTITUDE) + ", velocity " + dataBranch.getLast(FlightDataType.TYPE_VELOCITY_Z)); } diff --git a/core/src/main/java/info/openrocket/core/simulation/AbstractSimulationStepper.java b/core/src/main/java/info/openrocket/core/simulation/AbstractSimulationStepper.java index c6a2363b7..dcd152768 100644 --- a/core/src/main/java/info/openrocket/core/simulation/AbstractSimulationStepper.java +++ b/core/src/main/java/info/openrocket/core/simulation/AbstractSimulationStepper.java @@ -2,6 +2,8 @@ package info.openrocket.core.simulation; import java.util.Collection; +import info.openrocket.core.aerodynamics.AerodynamicForces; +import info.openrocket.core.aerodynamics.FlightConditions; import info.openrocket.core.masscalc.MassCalculator; import info.openrocket.core.masscalc.RigidBody; import info.openrocket.core.models.atmosphere.AtmosphericConditions; @@ -9,7 +11,10 @@ import info.openrocket.core.simulation.exception.SimulationException; import info.openrocket.core.simulation.listeners.SimulationListenerHelper; import info.openrocket.core.util.BugException; import info.openrocket.core.util.Coordinate; +import info.openrocket.core.util.GeodeticComputationStrategy; +import info.openrocket.core.util.MathUtil; import info.openrocket.core.util.Quaternion; +import info.openrocket.core.util.Rotation2D; public abstract class AbstractSimulationStepper implements SimulationStepper { @@ -224,4 +229,132 @@ public abstract class AbstractSimulationStepper implements SimulationStepper { throw new BugException("Simulation resulted in not-a-number (NaN) value, please report a bug, q=" + q); } } + + protected static class DataStore { + + public double timeStep = Double.NaN; + + public AccelerationData accelerationData; + + public AtmosphericConditions atmosphericConditions; + + public FlightConditions flightConditions; + + public double longitudinalAcceleration = Double.NaN; + + public RigidBody rocketMass; + + public RigidBody motorMass; + + public Coordinate coriolisAcceleration; + + public Coordinate linearAcceleration; + public Coordinate angularAcceleration; + + public Coordinate launchRodDirection = null; + + public double maxZvelocity = Double.NaN; + public double startWarningTime = Double.NaN; + + // set by calculateFlightConditions and calculateAcceleration: + public AerodynamicForces forces; + public Coordinate windVelocity = new Coordinate(Double.NaN, Double.NaN, Double.NaN); + public double gravity = Double.NaN; + public double thrustForce = Double.NaN; + public double dragForce = Double.NaN; + public double lateralPitchRate = Double.NaN; + + public double rollAcceleration = Double.NaN; + public double lateralPitchAcceleration = Double.NaN; + + public Rotation2D thetaRotation; + + void storeData(SimulationStatus status) { + + FlightDataBranch dataBranch = status.getFlightDataBranch(); + + dataBranch.setValue(FlightDataType.TYPE_THRUST_FORCE, thrustForce); + dataBranch.setValue(FlightDataType.TYPE_GRAVITY, gravity); + double weight = rocketMass.getMass() * gravity; + dataBranch.setValue(FlightDataType.TYPE_THRUST_WEIGHT_RATIO, thrustForce / weight); + dataBranch.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce); + + dataBranch.setValue(FlightDataType.TYPE_WIND_VELOCITY, windVelocity.length()); + dataBranch.setValue(FlightDataType.TYPE_TIME_STEP, timeStep); + + if (GeodeticComputationStrategy.FLAT != status.getSimulationConditions().getGeodeticComputation()) { + dataBranch.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length()); + } + + if (null != linearAcceleration) { + dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_XY, + MathUtil.hypot(linearAcceleration.x, linearAcceleration.y)); + + dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length()); + dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z); + } + + if (null != rocketMass) { + dataBranch.setValue(FlightDataType.TYPE_CG_LOCATION, rocketMass.getCM().x); + dataBranch.setValue(FlightDataType.TYPE_MASS, rocketMass.getMass()); + dataBranch.setValue(FlightDataType.TYPE_LONGITUDINAL_INERTIA, rocketMass.getLongitudinalInertia()); + dataBranch.setValue(FlightDataType.TYPE_ROTATIONAL_INERTIA, rocketMass.getRotationalInertia()); + } + + if (null != motorMass) { + dataBranch.setValue(FlightDataType.TYPE_MOTOR_MASS, motorMass.getMass()); + } + + if (null != flightConditions) { + double Re = (flightConditions.getVelocity() * + status.getConfiguration().getLengthAerodynamic() / + flightConditions.getAtmosphericConditions().getKinematicViscosity()); + dataBranch.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re); + dataBranch.setValue(FlightDataType.TYPE_MACH_NUMBER, flightConditions.getMach()); + dataBranch.setValue(FlightDataType.TYPE_REFERENCE_LENGTH, flightConditions.getRefLength()); + dataBranch.setValue(FlightDataType.TYPE_REFERENCE_AREA, flightConditions.getRefArea()); + + dataBranch.setValue(FlightDataType.TYPE_PITCH_RATE, flightConditions.getPitchRate()); + dataBranch.setValue(FlightDataType.TYPE_YAW_RATE, flightConditions.getYawRate()); + dataBranch.setValue(FlightDataType.TYPE_ROLL_RATE, flightConditions.getRollRate()); + + dataBranch.setValue(FlightDataType.TYPE_AOA, flightConditions.getAOA()); + dataBranch.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, + flightConditions.getAtmosphericConditions().getTemperature()); + dataBranch.setValue(FlightDataType.TYPE_AIR_PRESSURE, + flightConditions.getAtmosphericConditions().getPressure()); + dataBranch.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, + flightConditions.getAtmosphericConditions().getMachSpeed()); + } + + if (null != forces) { + dataBranch.setValue(FlightDataType.TYPE_DRAG_COEFF, forces.getCD()); + dataBranch.setValue(FlightDataType.TYPE_AXIAL_DRAG_COEFF, forces.getCDaxial()); + dataBranch.setValue(FlightDataType.TYPE_FRICTION_DRAG_COEFF, forces.getFrictionCD()); + dataBranch.setValue(FlightDataType.TYPE_PRESSURE_DRAG_COEFF, forces.getPressureCD()); + dataBranch.setValue(FlightDataType.TYPE_BASE_DRAG_COEFF, forces.getBaseCD()); + } + + if (status.isLaunchRodCleared() && null != forces) { + if (null != forces.getCP()) { + dataBranch.setValue(FlightDataType.TYPE_CP_LOCATION, forces.getCP().x); + } + dataBranch.setValue(FlightDataType.TYPE_NORMAL_FORCE_COEFF, forces.getCN()); + dataBranch.setValue(FlightDataType.TYPE_SIDE_FORCE_COEFF, forces.getCside()); + dataBranch.setValue(FlightDataType.TYPE_ROLL_MOMENT_COEFF, forces.getCroll()); + dataBranch.setValue(FlightDataType.TYPE_ROLL_FORCING_COEFF, forces.getCrollForce()); + dataBranch.setValue(FlightDataType.TYPE_ROLL_DAMPING_COEFF, forces.getCrollDamp()); + dataBranch.setValue(FlightDataType.TYPE_PITCH_DAMPING_MOMENT_COEFF, forces.getPitchDampingMoment()); + + if (null != rocketMass && null != flightConditions) { + dataBranch.setValue(FlightDataType.TYPE_STABILITY, + (forces.getCP().x - rocketMass.getCM().x) / flightConditions.getRefLength()); + dataBranch.setValue(FlightDataType.TYPE_PITCH_MOMENT_COEFF, + forces.getCm() - forces.getCN() * rocketMass.getCM().x / flightConditions.getRefLength()); + dataBranch.setValue(FlightDataType.TYPE_YAW_MOMENT_COEFF, + forces.getCyaw() - forces.getCside() * rocketMass.getCM().x / flightConditions.getRefLength()); + } + } + } + } } diff --git a/core/src/main/java/info/openrocket/core/simulation/BasicEventSimulationEngine.java b/core/src/main/java/info/openrocket/core/simulation/BasicEventSimulationEngine.java index a29e51a03..aeae329b3 100644 --- a/core/src/main/java/info/openrocket/core/simulation/BasicEventSimulationEngine.java +++ b/core/src/main/java/info/openrocket/core/simulation/BasicEventSimulationEngine.java @@ -84,13 +84,11 @@ public class BasicEventSimulationEngine implements SimulationEngine { branchName = trans.get("BasicEventSimulationEngine.nullBranchName"); } FlightDataBranch initialBranch = new FlightDataBranch( branchName, FlightDataType.TYPE_TIME); + currentStatus.setFlightDataBranch(initialBranch); // put a point on it so we can plot if we get an early abort event initialBranch.addPoint(); - initialBranch.setValue(FlightDataType.TYPE_TIME, 0.0); - initialBranch.setValue(FlightDataType.TYPE_ALTITUDE, 0.0); - - currentStatus.setFlightDataBranch(initialBranch); + currentStatus.storeData(); // Sanity checks on design and configuration diff --git a/core/src/main/java/info/openrocket/core/simulation/BasicLandingStepper.java b/core/src/main/java/info/openrocket/core/simulation/BasicLandingStepper.java index 1066569ed..8130968f1 100644 --- a/core/src/main/java/info/openrocket/core/simulation/BasicLandingStepper.java +++ b/core/src/main/java/info/openrocket/core/simulation/BasicLandingStepper.java @@ -8,7 +8,7 @@ public class BasicLandingStepper extends AbstractEulerStepper { @Override protected double computeCD(SimulationStatus status) { // Accumulate CD for all recovery devices - cd = 0; + double cd = 0; final InstanceMap imap = status.getConfiguration().getActiveInstances(); for (RecoveryDevice c : status.getDeployedRecoveryDevices()) { cd += imap.count(c) * c.getCD() * c.getArea() / status.getConfiguration().getReferenceArea(); diff --git a/core/src/main/java/info/openrocket/core/simulation/GroundStepper.java b/core/src/main/java/info/openrocket/core/simulation/GroundStepper.java index 782a367ab..e68bcebd2 100644 --- a/core/src/main/java/info/openrocket/core/simulation/GroundStepper.java +++ b/core/src/main/java/info/openrocket/core/simulation/GroundStepper.java @@ -21,5 +21,6 @@ public class GroundStepper extends AbstractSimulationStepper { public void step(SimulationStatus status, double timeStep) throws SimulationException { log.trace("step: position=" + status.getRocketPosition() + ", velocity=" + status.getRocketVelocity()); status.setSimulationTime(status.getSimulationTime() + timeStep); + status.storeData(); } } diff --git a/core/src/main/java/info/openrocket/core/simulation/RK4SimulationStatus.java b/core/src/main/java/info/openrocket/core/simulation/RK4SimulationStatus.java deleted file mode 100644 index dc41584d2..000000000 --- a/core/src/main/java/info/openrocket/core/simulation/RK4SimulationStatus.java +++ /dev/null @@ -1,68 +0,0 @@ -package info.openrocket.core.simulation; - -import info.openrocket.core.models.atmosphere.AtmosphericConditions; -import info.openrocket.core.rocketcomponent.FlightConfiguration; -import info.openrocket.core.util.Coordinate; - -public class RK4SimulationStatus extends SimulationStatus implements Cloneable { - private Coordinate launchRodDirection; - - private double previousAcceleration = 0; - - // Used for determining when to store aerodynamic computation warnings: - private double maxZVelocity = 0; - private double startWarningTime = -1; - - public RK4SimulationStatus(FlightConfiguration configuration, - SimulationConditions simulationConditions ) { - super(configuration, simulationConditions); - } - - public RK4SimulationStatus(SimulationStatus other) { - super(other); - if (other instanceof RK4SimulationStatus) { - this.launchRodDirection = ((RK4SimulationStatus) other).launchRodDirection; - this.previousAcceleration = ((RK4SimulationStatus) other).previousAcceleration; - this.maxZVelocity = ((RK4SimulationStatus) other).maxZVelocity; - this.startWarningTime = ((RK4SimulationStatus) other).startWarningTime; - } - } - - public void setLaunchRodDirection(Coordinate launchRodDirection) { - this.launchRodDirection = launchRodDirection; - } - - public Coordinate getLaunchRodDirection() { - return launchRodDirection; - } - - public double getPreviousAcceleration() { - return previousAcceleration; - } - - public void setPreviousAcceleration(double previousAcceleration) { - this.previousAcceleration = previousAcceleration; - } - - public double getMaxZVelocity() { - return maxZVelocity; - } - - public void setMaxZVelocity(double maxZVelocity) { - this.maxZVelocity = maxZVelocity; - } - - public double getStartWarningTime() { - return startWarningTime; - } - - public void setStartWarningTime(double startWarningTime) { - this.startWarningTime = startWarningTime; - } - - @Override - public RK4SimulationStatus clone() { - return (RK4SimulationStatus) super.clone(); - } - -} diff --git a/core/src/main/java/info/openrocket/core/simulation/RK4SimulationStepper.java b/core/src/main/java/info/openrocket/core/simulation/RK4SimulationStepper.java index 8e41a20dc..91b76d28d 100644 --- a/core/src/main/java/info/openrocket/core/simulation/RK4SimulationStepper.java +++ b/core/src/main/java/info/openrocket/core/simulation/RK4SimulationStepper.java @@ -63,18 +63,18 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { DataStore store = new DataStore(); @Override - public RK4SimulationStatus initialize(SimulationStatus original) { + public SimulationStatus initialize(SimulationStatus original) { - RK4SimulationStatus status = new RK4SimulationStatus(original); + SimulationStatus status = new SimulationStatus(original); // Copy the existing warnings status.setWarnings(original.getWarnings()); SimulationConditions sim = original.getSimulationConditions(); - - status.setLaunchRodDirection(new Coordinate( - Math.sin(sim.getLaunchRodAngle()) * Math.cos(Math.PI / 2.0 - sim.getLaunchRodDirection()), - Math.sin(sim.getLaunchRodAngle()) * Math.sin(Math.PI / 2.0 - sim.getLaunchRodDirection()), - Math.cos(sim.getLaunchRodAngle()))); + + store.launchRodDirection = new Coordinate( + Math.sin(sim.getLaunchRodAngle()) * Math.cos(Math.PI / 2.0 - sim.getLaunchRodDirection()), + Math.sin(sim.getLaunchRodAngle()) * Math.sin(Math.PI / 2.0 - sim.getLaunchRodDirection()), + Math.cos(sim.getLaunchRodAngle())); this.random = new Random(original.getSimulationConditions().getRandomSeed() ^ SEED_RANDOMIZATION); @@ -87,11 +87,11 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { @Override public void step(SimulationStatus simulationStatus, double maxTimeStep) throws SimulationException { - RK4SimulationStatus status = (RK4SimulationStatus) simulationStatus; + SimulationStatus status = simulationStatus; //////// Perform RK4 integration: //////// - RK4SimulationStatus status2; + SimulationStatus status2; RK4Parameters k1, k2, k3, k4; /* @@ -107,6 +107,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { //// First position, k1 = f(t, y) k1 = computeParameters(status, store); + store.storeData(status); /* * Select the actual time step to use. It is the minimum of the following: @@ -138,18 +139,18 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { dt[0] /= 5.0; dt[6] = status.getSimulationConditions().getLaunchRodLength() / k1.v.length() / 10; } - dt[7] = 1.5 * store.timestep; + dt[7] = 1.5 * store.timeStep; - store.timestep = Double.MAX_VALUE; + store.timeStep = Double.MAX_VALUE; int limitingValue = -1; for (int i = 0; i < dt.length; i++) { - if (dt[i] < store.timestep) { - store.timestep = dt[i]; + if (dt[i] < store.timeStep) { + store.timeStep = dt[i]; limitingValue = i; } } - log.trace("Selected time step " + store.timestep + " (limiting factor " + limitingValue + ")"); + log.trace("Selected time step " + store.timeStep + " (limiting factor " + limitingValue + ")"); // If we have a scheduled event coming up before the end of our timestep, truncate step // else if the time from the end of our timestep to the next scheduled event time is less than @@ -158,36 +159,36 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { FlightEvent nextEvent = status.getEventQueue().peek(); if (nextEvent != null) { double nextEventTime = nextEvent.getTime(); - if (status.getSimulationTime() + store.timestep > nextEventTime) { - store.timestep = nextEventTime - status.getSimulationTime(); - log.trace("scheduled event at " + nextEventTime + " truncates timestep to " + store.timestep); - } else if ((status.getSimulationTime() + store.timestep < nextEventTime) && - (status.getSimulationTime() + store.timestep + minTimeStep > nextEventTime)) { - store.timestep = nextEventTime - status.getSimulationTime(); - log.trace("Scheduled event at " + nextEventTime + " stretches timestep to " + store.timestep); + if (status.getSimulationTime() + store.timeStep > nextEventTime) { + store.timeStep = nextEventTime - status.getSimulationTime(); + log.trace("scheduled event at " + nextEventTime + " truncates timestep to " + store.timeStep); + } else if ((status.getSimulationTime() + store.timeStep < nextEventTime) && + (status.getSimulationTime() + store.timeStep + minTimeStep > nextEventTime)) { + store.timeStep = nextEventTime - status.getSimulationTime(); + log.trace("Scheduled event at " + nextEventTime + " stretches timestep to " + store.timeStep); } } // If we've wound up with a too-small timestep, increase it avoid numerical instability even at the // cost of not being *quite* on an event - if (store.timestep < minTimeStep) { - log.trace("Too small time step " + store.timestep + " (limiting factor " + limitingValue + "), using " + + if (store.timeStep < minTimeStep) { + log.trace("Too small time step " + store.timeStep + " (limiting factor " + limitingValue + "), using " + minTimeStep + " instead."); - store.timestep = minTimeStep; + store.timeStep = minTimeStep; } - checkNaN(store.timestep); + checkNaN(store.timeStep); //// Second position, k2 = f(t + h/2, y + k1*h/2) status2 = status.clone(); - status2.setSimulationTime(status.getSimulationTime() + store.timestep / 2); - status2.setRocketPosition(status.getRocketPosition().add(k1.v.multiply(store.timestep / 2))); - status2.setRocketVelocity(status.getRocketVelocity().add(k1.a.multiply(store.timestep / 2))); - status2.setRocketOrientationQuaternion(status.getRocketOrientationQuaternion().multiplyLeft(Quaternion.rotation(k1.rv.multiply(store.timestep / 2)))); - status2.setRocketRotationVelocity(status.getRocketRotationVelocity().add(k1.ra.multiply(store.timestep / 2))); + status2.setSimulationTime(status.getSimulationTime() + store.timeStep / 2); + status2.setRocketPosition(status.getRocketPosition().add(k1.v.multiply(store.timeStep / 2))); + status2.setRocketVelocity(status.getRocketVelocity().add(k1.a.multiply(store.timeStep / 2))); + status2.setRocketOrientationQuaternion(status.getRocketOrientationQuaternion().multiplyLeft(Quaternion.rotation(k1.rv.multiply(store.timeStep / 2)))); + status2.setRocketRotationVelocity(status.getRocketRotationVelocity().add(k1.ra.multiply(store.timeStep / 2))); k2 = computeParameters(status2, store); @@ -195,11 +196,11 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { //// Third position, k3 = f(t + h/2, y + k2*h/2) status2 = status.clone(); - status2.setSimulationTime(status.getSimulationTime() + store.timestep / 2); - status2.setRocketPosition(status.getRocketPosition().add(k2.v.multiply(store.timestep / 2))); - status2.setRocketVelocity(status.getRocketVelocity().add(k2.a.multiply(store.timestep / 2))); - status2.setRocketOrientationQuaternion(status2.getRocketOrientationQuaternion().multiplyLeft(Quaternion.rotation(k2.rv.multiply(store.timestep / 2)))); - status2.setRocketRotationVelocity(status.getRocketRotationVelocity().add(k2.ra.multiply(store.timestep / 2))); + status2.setSimulationTime(status.getSimulationTime() + store.timeStep / 2); + status2.setRocketPosition(status.getRocketPosition().add(k2.v.multiply(store.timeStep / 2))); + status2.setRocketVelocity(status.getRocketVelocity().add(k2.a.multiply(store.timeStep / 2))); + status2.setRocketOrientationQuaternion(status2.getRocketOrientationQuaternion().multiplyLeft(Quaternion.rotation(k2.rv.multiply(store.timeStep / 2)))); + status2.setRocketRotationVelocity(status.getRocketRotationVelocity().add(k2.ra.multiply(store.timeStep / 2))); k3 = computeParameters(status2, store); @@ -207,21 +208,21 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { //// Fourth position, k4 = f(t + h, y + k3*h) status2 = status.clone(); - status2.setSimulationTime(status.getSimulationTime() + store.timestep); - status2.setRocketPosition(status.getRocketPosition().add(k3.v.multiply(store.timestep))); - status2.setRocketVelocity(status.getRocketVelocity().add(k3.a.multiply(store.timestep))); - status2.setRocketOrientationQuaternion(status2.getRocketOrientationQuaternion().multiplyLeft(Quaternion.rotation(k3.rv.multiply(store.timestep)))); - status2.setRocketRotationVelocity(status.getRocketRotationVelocity().add(k3.ra.multiply(store.timestep))); + status2.setSimulationTime(status.getSimulationTime() + store.timeStep); + status2.setRocketPosition(status.getRocketPosition().add(k3.v.multiply(store.timeStep))); + status2.setRocketVelocity(status.getRocketVelocity().add(k3.a.multiply(store.timeStep))); + status2.setRocketOrientationQuaternion(status2.getRocketOrientationQuaternion().multiplyLeft(Quaternion.rotation(k3.rv.multiply(store.timeStep)))); + status2.setRocketRotationVelocity(status.getRocketRotationVelocity().add(k3.ra.multiply(store.timeStep))); k4 = computeParameters(status2, store); //// Sum all together, y(n+1) = y(n) + h*(k1 + 2*k2 + 2*k3 + k4)/6 Coordinate deltaV, deltaP, deltaR, deltaO; - deltaV = k2.a.add(k3.a).multiply(2).add(k1.a).add(k4.a).multiply(store.timestep / 6); - deltaP = k2.v.add(k3.v).multiply(2).add(k1.v).add(k4.v).multiply(store.timestep / 6); - deltaR = k2.ra.add(k3.ra).multiply(2).add(k1.ra).add(k4.ra).multiply(store.timestep / 6); - deltaO = k2.rv.add(k3.rv).multiply(2).add(k1.rv).add(k4.rv).multiply(store.timestep / 6); + deltaV = k2.a.add(k3.a).multiply(2).add(k1.a).add(k4.a).multiply(store.timeStep / 6); + deltaP = k2.v.add(k3.v).multiply(2).add(k1.v).add(k4.v).multiply(store.timeStep / 6); + deltaR = k2.ra.add(k3.ra).multiply(2).add(k1.ra).add(k4.ra).multiply(store.timeStep / 6); + deltaO = k2.rv.add(k3.rv).multiply(2).add(k1.rv).add(k4.rv).multiply(store.timeStep / 6); status.setRocketVelocity(status.getRocketVelocity().add(deltaV)); @@ -233,15 +234,17 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition()); status.setRocketWorldPosition(w); - if (!(0 <= store.timestep)) { + if (!(0 <= store.timeStep)) { // Also catches NaN - throw new IllegalArgumentException("Stepping backwards in time, timestep=" + store.timestep); + throw new IllegalArgumentException("Stepping backwards in time, timestep=" + store.timeStep); } - status.setSimulationTime(status.getSimulationTime() + store.timestep); + status.setSimulationTime(status.getSimulationTime() + store.timeStep); // Store data // TODO: MEDIUM: Store acceleration etc of entire RK4 step, store should be cloned or something... - storeData(status, store); + status.getFlightDataBranch().addPoint(); + status.storeData(); + store.storeData(status); // Verify that values don't run out of range if (status.getRocketVelocity().length2() > 1e18 || @@ -255,7 +258,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { - private RK4Parameters computeParameters(RK4SimulationStatus status, DataStore dataStore) + private RK4Parameters computeParameters(SimulationStatus status, DataStore dataStore) throws SimulationException { RK4Parameters params = new RK4Parameters(); @@ -294,7 +297,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { * @param status the status of the rocket. * @throws SimulationException */ - private AccelerationData calculateAcceleration(RK4SimulationStatus status, DataStore store) throws SimulationException { + private AccelerationData calculateAcceleration(SimulationStatus status, DataStore store) throws SimulationException { // Compute the forces affecting the rocket calculateForces(status, store); @@ -347,8 +350,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { // set angular acceleration to zero. if (!status.isLaunchRodCleared()) { - store.linearAcceleration = status.getLaunchRodDirection().multiply( - store.linearAcceleration.dot(status.getLaunchRodDirection())); + store.linearAcceleration = store.launchRodDirection.multiply(store.linearAcceleration.dot(store.launchRodDirection)); store.angularAcceleration = Coordinate.NUL; store.rollAcceleration = 0; store.lateralPitchAcceleration = 0; @@ -389,7 +391,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { * Calculate the aerodynamic forces into the data store. This method also handles * whether to include aerodynamic computation warnings or not. */ - private void calculateForces(RK4SimulationStatus status, DataStore store) throws SimulationException { + private void calculateForces(SimulationStatus status, DataStore store) throws SimulationException { // Call pre-listeners store.forces = SimulationListenerHelper.firePreAerodynamicCalculation(status); @@ -406,19 +408,21 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { * below 20% of the max. velocity. */ WarningSet warnings = status.getWarnings(); - status.setMaxZVelocity(MathUtil.max(status.getMaxZVelocity(), status.getRocketVelocity().z)); + store.maxZvelocity = MathUtil.max(store.maxZvelocity, status.getRocketVelocity().z); if (!status.isLaunchRodCleared()) { warnings = null; } else { - if (status.getRocketVelocity().z < 0.2 * status.getMaxZVelocity()) + if (status.getRocketVelocity().z < 0.2 * store.maxZvelocity) { warnings = null; - if (status.getStartWarningTime() < 0) - status.setStartWarningTime(status.getSimulationTime() + 0.25); + } + if (Double.isNaN(store.startWarningTime)) { + store.startWarningTime = status.getSimulationTime() + 0.25; + } } - if (status.getSimulationTime() < status.getStartWarningTime()) + + if (!(status.getSimulationTime() > store.startWarningTime)) warnings = null; - // Calculate aerodynamic forces store.forces = status.getSimulationConditions().getAerodynamicCalculator() @@ -444,7 +448,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { * Additionally the fields thetaRotation and lateralPitchRate are defined in * the data store, and can be used after calling this method. */ - private void calculateFlightConditions(RK4SimulationStatus status, DataStore store) + private void calculateFlightConditions(SimulationStatus status, DataStore store) throws SimulationException { // Call pre listeners, allow complete override @@ -466,9 +470,8 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { //// Local wind speed and direction - Coordinate windVelocity = modelWindVelocity(status); - store.windSpeed = windVelocity.length(); - Coordinate airSpeed = status.getRocketVelocity().add(windVelocity); + store.windVelocity = modelWindVelocity(status); + Coordinate airSpeed = status.getRocketVelocity().add(store.windVelocity); airSpeed = status.getRocketOrientationQuaternion().invRotate(airSpeed); @@ -521,151 +524,6 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { } } - - - - private void storeData(RK4SimulationStatus status, DataStore store) { - - FlightDataBranch dataBranch = status.getFlightDataBranch(); - - dataBranch.addPoint(); - dataBranch.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime()); - dataBranch.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z); - dataBranch.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x); - dataBranch.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y); - - dataBranch.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad()); - dataBranch.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad()); - if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) { - dataBranch.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, store.coriolisAcceleration.length()); - } - - dataBranch.setValue(FlightDataType.TYPE_POSITION_XY, - MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y)); - dataBranch.setValue(FlightDataType.TYPE_POSITION_DIRECTION, - Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x)); - - dataBranch.setValue(FlightDataType.TYPE_VELOCITY_XY, - MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y)); - - if (store.linearAcceleration != null) { - dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_XY, - MathUtil.hypot(store.linearAcceleration.x, store.linearAcceleration.y)); - - dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, store.linearAcceleration.length()); - } - - if (store.flightConditions != null) { - double Re = (store.flightConditions.getVelocity() * - status.getConfiguration().getLengthAerodynamic() / - store.flightConditions.getAtmosphericConditions().getKinematicViscosity()); - dataBranch.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re); - } - - dataBranch.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z); - if (store.linearAcceleration != null) { - dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_Z, store.linearAcceleration.z); - } - - if (store.flightConditions != null) { - dataBranch.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, status.getRocketVelocity().length()); - dataBranch.setValue(FlightDataType.TYPE_MACH_NUMBER, store.flightConditions.getMach()); - } - - if (store.rocketMass != null) { - dataBranch.setValue(FlightDataType.TYPE_CG_LOCATION, store.rocketMass.getCM().x); - } - if (status.isLaunchRodCleared()) { - // Don't include CP and stability with huge launch AOA - if (store.forces != null) { - dataBranch.setValue(FlightDataType.TYPE_CP_LOCATION, store.forces.getCP().x); - } - if (store.forces != null && store.flightConditions != null && store.rocketMass != null) { - dataBranch.setValue(FlightDataType.TYPE_STABILITY, - (store.forces.getCP().x - store.rocketMass.getCM().x) / store.flightConditions.getRefLength()); - } - } - - if (null != store.motorMass) { - dataBranch.setValue(FlightDataType.TYPE_MOTOR_MASS, store.motorMass.getMass()); - //dataBranch.setValue(FlightDataType.TYPE_MOTOR_LONGITUDINAL_INERTIA, store.motorMassData.getLongitudinalInertia()); - //dataBranch.setValue(FlightDataType.TYPE_MOTOR_ROTATIONAL_INERTIA, store.motorMassData.getRotationalInertia()); - } - if (store.rocketMass != null) { - // N.B.: These refer to total mass - dataBranch.setValue(FlightDataType.TYPE_MASS, store.rocketMass.getMass()); - dataBranch.setValue(FlightDataType.TYPE_LONGITUDINAL_INERTIA, store.rocketMass.getLongitudinalInertia()); - dataBranch.setValue(FlightDataType.TYPE_ROTATIONAL_INERTIA, store.rocketMass.getRotationalInertia()); - } - - dataBranch.setValue(FlightDataType.TYPE_THRUST_FORCE, store.thrustForce); - double weight = store.rocketMass.getMass() * store.gravity; - dataBranch.setValue(FlightDataType.TYPE_THRUST_WEIGHT_RATIO, store.thrustForce / weight); - dataBranch.setValue(FlightDataType.TYPE_DRAG_FORCE, store.dragForce); - dataBranch.setValue(FlightDataType.TYPE_GRAVITY, store.gravity); - - if (status.isLaunchRodCleared() && store.forces != null) { - if (store.rocketMass != null && store.flightConditions != null) { - dataBranch.setValue(FlightDataType.TYPE_PITCH_MOMENT_COEFF, - store.forces.getCm() - store.forces.getCN() * store.rocketMass.getCM().x / store.flightConditions.getRefLength()); - dataBranch.setValue(FlightDataType.TYPE_YAW_MOMENT_COEFF, - store.forces.getCyaw() - store.forces.getCside() * store.rocketMass.getCM().x / store.flightConditions.getRefLength()); - } - dataBranch.setValue(FlightDataType.TYPE_NORMAL_FORCE_COEFF, store.forces.getCN()); - dataBranch.setValue(FlightDataType.TYPE_SIDE_FORCE_COEFF, store.forces.getCside()); - dataBranch.setValue(FlightDataType.TYPE_ROLL_MOMENT_COEFF, store.forces.getCroll()); - dataBranch.setValue(FlightDataType.TYPE_ROLL_FORCING_COEFF, store.forces.getCrollForce()); - dataBranch.setValue(FlightDataType.TYPE_ROLL_DAMPING_COEFF, store.forces.getCrollDamp()); - dataBranch.setValue(FlightDataType.TYPE_PITCH_DAMPING_MOMENT_COEFF, - store.forces.getPitchDampingMoment()); - } - - if (store.forces != null) { - dataBranch.setValue(FlightDataType.TYPE_DRAG_COEFF, store.forces.getCD()); - dataBranch.setValue(FlightDataType.TYPE_AXIAL_DRAG_COEFF, store.forces.getCDaxial()); - dataBranch.setValue(FlightDataType.TYPE_FRICTION_DRAG_COEFF, store.forces.getFrictionCD()); - dataBranch.setValue(FlightDataType.TYPE_PRESSURE_DRAG_COEFF, store.forces.getPressureCD()); - dataBranch.setValue(FlightDataType.TYPE_BASE_DRAG_COEFF, store.forces.getBaseCD()); - } - - if (store.flightConditions != null) { - dataBranch.setValue(FlightDataType.TYPE_REFERENCE_LENGTH, store.flightConditions.getRefLength()); - dataBranch.setValue(FlightDataType.TYPE_REFERENCE_AREA, store.flightConditions.getRefArea()); - - dataBranch.setValue(FlightDataType.TYPE_PITCH_RATE, store.flightConditions.getPitchRate()); - dataBranch.setValue(FlightDataType.TYPE_YAW_RATE, store.flightConditions.getYawRate()); - dataBranch.setValue(FlightDataType.TYPE_ROLL_RATE, store.flightConditions.getRollRate()); - - dataBranch.setValue(FlightDataType.TYPE_AOA, store.flightConditions.getAOA()); - } - - Coordinate c = status.getRocketOrientationQuaternion().rotateZ(); - double theta = Math.atan2(c.z, MathUtil.hypot(c.x, c.y)); - double phi = Math.atan2(c.y, c.x); - if (phi < -(Math.PI - 0.0001)) - phi = Math.PI; - dataBranch.setValue(FlightDataType.TYPE_ORIENTATION_THETA, theta); - dataBranch.setValue(FlightDataType.TYPE_ORIENTATION_PHI, phi); - - dataBranch.setValue(FlightDataType.TYPE_WIND_VELOCITY, store.windSpeed); - - if (store.flightConditions != null) { - dataBranch.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, - store.flightConditions.getAtmosphericConditions().getTemperature()); - dataBranch.setValue(FlightDataType.TYPE_AIR_PRESSURE, - store.flightConditions.getAtmosphericConditions().getPressure()); - dataBranch.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, - store.flightConditions.getAtmosphericConditions().getMachSpeed()); - } - - - dataBranch.setValue(FlightDataType.TYPE_TIME_STEP, store.timestep); - dataBranch.setValue(FlightDataType.TYPE_COMPUTATION_TIME, - (System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0); - } - - - private static class RK4Parameters { /** Linear acceleration */ @@ -677,40 +535,4 @@ public class RK4SimulationStepper extends AbstractSimulationStepper { /** Rotational velocity */ public Coordinate rv; } - - private static class DataStore { - public double timestep = Double.NaN; - - public AccelerationData accelerationData; - - public AtmosphericConditions atmosphericConditions; - - public FlightConditions flightConditions; - - public double longitudinalAcceleration = Double.NaN; - - public RigidBody rocketMass; - - public RigidBody motorMass; - - public Coordinate coriolisAcceleration; - - public Coordinate linearAcceleration; - public Coordinate angularAcceleration; - - // set by calculateFlightConditions and calculateAcceleration: - public AerodynamicForces forces; - public double windSpeed = Double.NaN; - public double gravity = Double.NaN; - public double thrustForce = Double.NaN; - public double dragForce = Double.NaN; - public double lateralPitchRate = Double.NaN; - - public double rollAcceleration = Double.NaN; - public double lateralPitchAcceleration = Double.NaN; - - public Rotation2D thetaRotation; - - } - } diff --git a/core/src/main/java/info/openrocket/core/simulation/SimulationStatus.java b/core/src/main/java/info/openrocket/core/simulation/SimulationStatus.java index 191cb6a01..953becf09 100644 --- a/core/src/main/java/info/openrocket/core/simulation/SimulationStatus.java +++ b/core/src/main/java/info/openrocket/core/simulation/SimulationStatus.java @@ -20,6 +20,7 @@ import info.openrocket.core.simulation.exception.SimulationException; import info.openrocket.core.simulation.listeners.SimulationListenerHelper; import info.openrocket.core.util.BugException; import info.openrocket.core.util.Coordinate; +import info.openrocket.core.util.MathUtil; import info.openrocket.core.util.Monitorable; import info.openrocket.core.util.MonitorableSet; import info.openrocket.core.util.Quaternion; @@ -33,7 +34,7 @@ import org.slf4j.LoggerFactory; * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ -public class SimulationStatus implements Monitorable { +public class SimulationStatus implements Cloneable, Monitorable { private static final Logger log = LoggerFactory.getLogger(BasicEventSimulationEngine.class); @@ -516,6 +517,39 @@ public class SimulationStatus implements Monitorable { } } + /** + * Store data from current sim status + */ + public void storeData() { + flightDataBranch.setValue(FlightDataType.TYPE_TIME, getSimulationTime()); + flightDataBranch.setValue(FlightDataType.TYPE_ALTITUDE, getRocketPosition().z); + flightDataBranch.setValue(FlightDataType.TYPE_POSITION_X, getRocketPosition().x); + flightDataBranch.setValue(FlightDataType.TYPE_POSITION_Y, getRocketPosition().y); + + flightDataBranch.setValue(FlightDataType.TYPE_LATITUDE, getRocketWorldPosition().getLatitudeRad()); + flightDataBranch.setValue(FlightDataType.TYPE_LONGITUDE, getRocketWorldPosition().getLongitudeRad()); + + flightDataBranch.setValue(FlightDataType.TYPE_POSITION_XY, + MathUtil.hypot(getRocketPosition().x, getRocketPosition().y)); + flightDataBranch.setValue(FlightDataType.TYPE_POSITION_DIRECTION, + Math.atan2(getRocketPosition().y, getRocketPosition().x)); + + flightDataBranch.setValue(FlightDataType.TYPE_VELOCITY_XY, + MathUtil.hypot(getRocketVelocity().x, getRocketVelocity().y)); + flightDataBranch.setValue(FlightDataType.TYPE_VELOCITY_Z, getRocketVelocity().z); + flightDataBranch.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, getRocketVelocity().length()); + + Coordinate c = getRocketOrientationQuaternion().rotateZ(); + double theta = Math.atan2(c.z, MathUtil.hypot(c.x, c.y)); + double phi = Math.atan2(c.y, c.x); + if (phi < -(Math.PI - 0.0001)) + phi = Math.PI; + flightDataBranch.setValue(FlightDataType.TYPE_ORIENTATION_THETA, theta); + flightDataBranch.setValue(FlightDataType.TYPE_ORIENTATION_PHI, phi); + flightDataBranch.setValue(FlightDataType.TYPE_COMPUTATION_TIME, + (System.nanoTime() - getSimulationStartWallTime()) / 1000000000.0); + } + /** * Add a flight event to the event queue unless a listener aborts adding it. *