import net.sf.openrocket.aerodynamics.WarningSet;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
+import net.sf.openrocket.simulation.exception.SimulationCalculationException;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.simulation.listeners.SimulationListenerHelper;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.GeodeticComputationStrategy;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.Quaternion;
import net.sf.openrocket.util.Rotation2D;
-
+import net.sf.openrocket.util.WorldCoordinate;
public class RK4SimulationStepper extends AbstractSimulationStepper {
private static final LogHelper log = Application.getLogger();
+ /** Random value with which to XOR the random seed value */
+ private static final int SEED_RANDOMIZATION = 0x23E3A01F;
+
/**
* A recommended reasonably accurate time step.
private static final double MIN_TIME_STEP = 0.001;
-
- private final Random random = new Random();
+ private Random random;
@Override
public RK4SimulationStatus initialize(SimulationStatus original) {
- log.info("Performing RK4SimulationStepper initialization");
-
RK4SimulationStatus status = new RK4SimulationStatus();
status.copyFrom(original);
SimulationConditions sim = original.getSimulationConditions();
+
status.setLaunchRodDirection(new Coordinate(
Math.sin(sim.getLaunchRodAngle()) * Math.cos(sim.getLaunchRodDirection()),
Math.sin(sim.getLaunchRodAngle()) * Math.sin(sim.getLaunchRodDirection()),
Math.cos(sim.getLaunchRodAngle())
));
+ this.random = new Random(original.getSimulationConditions().getRandomSeed() ^ SEED_RANDOMIZATION);
+
return status;
}
RK4SimulationStatus status = (RK4SimulationStatus) simulationStatus;
DataStore store = new DataStore();
-
//////// Perform RK4 integration: ////////
RK4SimulationStatus status2;
status.setRocketRotationVelocity(status.getRocketRotationVelocity().add(deltaR));
status.setRocketOrientationQuaternion(status.getRocketOrientationQuaternion().multiplyLeft(Quaternion.rotation(deltaO)).normalizeIfNecessary());
+ WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
+ w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition());
+ status.setRocketWorldPosition(w);
+
status.setSimulationTime(status.getSimulationTime() + store.timestep);
status.setPreviousTimeStep(store.timestep);
+
+ // Verify that values don't run out of range
+ if (status.getRocketVelocity().length2() > 1e18 ||
+ status.getRocketPosition().length2() > 1e18 ||
+ status.getRocketRotationVelocity().length2() > 1e18) {
+ throw new SimulationCalculationException("Simulation values exceeded limits");
+ }
}
store.linearAcceleration = store.thetaRotation.rotateZ(store.linearAcceleration);
- // Convert into world coordinates and add effect of gravity
+ // Convert into rocket world coordinates
store.linearAcceleration = status.getRocketOrientationQuaternion().rotate(store.linearAcceleration);
+ // add effect of gravity
store.gravity = modelGravity(status);
store.linearAcceleration = store.linearAcceleration.sub(0, 0, store.gravity);
-
+ // add effect of Coriolis acceleration
+ store.coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation()
+ .getCoriolisAcceleration(status.getRocketWorldPosition(), status.getRocketVelocity());
+ store.linearAcceleration = store.linearAcceleration.add(store.coriolisAcceleration);
+
// If still on the launch rod, project acceleration onto launch rod direction and
// set angular acceleration to zero.
if (!status.isLaunchRodCleared()) {
// Compute acceleration in rocket coordinates
store.angularAcceleration = new Coordinate(momX / store.massData.getLongitudinalInertia(),
- momY / store.massData.getLongitudinalInertia(), momZ / store.massData.getRotationalInertia());
+ momY / store.massData.getLongitudinalInertia(),
+ momZ / store.massData.getRotationalInertia());
store.rollAcceleration = store.angularAcceleration.z;
// TODO: LOW: This should be hypot, but does it matter?
}
-
/**
* Calculate the aerodynamic forces into the data store. This method also handles
* whether to include aerodynamic computation warnings or not.
data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
+ if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.NONE) {
+ data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
+ data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
+ data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, store.coriolisAcceleration.length());
+ }
+
if (extra) {
data.setValue(FlightDataType.TYPE_POSITION_XY,
MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y));
public MassData massData;
+ public Coordinate coriolisAcceleration;
+
public Coordinate linearAcceleration;
public Coordinate angularAcceleration;