1 package net.sf.openrocket.simulation;
3 import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
4 import net.sf.openrocket.rocketcomponent.RecoveryDevice;
5 import net.sf.openrocket.simulation.exception.SimulationException;
6 import net.sf.openrocket.util.Coordinate;
7 import net.sf.openrocket.util.GeodeticComputationStrategy;
8 import net.sf.openrocket.util.MathUtil;
9 import net.sf.openrocket.util.WorldCoordinate;
11 public class BasicLandingStepper extends AbstractSimulationStepper {
13 private static final double RECOVERY_TIME_STEP = 0.5;
16 public SimulationStatus initialize(SimulationStatus status) throws SimulationException {
21 public void step(SimulationStatus status, double maxTimeStep) throws SimulationException {
23 double refArea = status.getConfiguration().getReferenceArea();
25 // Get the atmospheric conditions
26 AtmosphericConditions atmosphere = modelAtmosphericConditions(status);
28 //// Local wind speed and direction
29 Coordinate windSpeed = modelWindVelocity(status);
30 Coordinate airSpeed = status.getRocketVelocity().add(windSpeed);
33 double mach = airSpeed.length() / atmosphere.getMachSpeed();
34 for (RecoveryDevice c : status.getDeployedRecoveryDevices()) {
35 totalCD += c.getCD(mach) * c.getArea() / refArea;
39 double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
40 double dragForce = totalCD * dynP * refArea;
41 MassData massData = calculateMassData(status);
42 double mass = massData.getCG().weight;
45 // Compute drag acceleration
46 Coordinate linearAcceleration;
47 if (airSpeed.length() > 0.001) {
48 linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass);
50 linearAcceleration = Coordinate.NUL;
53 // Add effect of gravity
54 double gravity = modelGravity(status);
55 linearAcceleration = linearAcceleration.sub(0, 0, gravity);
58 // Add coriolis acceleration
59 Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration(
60 status.getRocketWorldPosition(), status.getRocketVelocity());
61 linearAcceleration = linearAcceleration.add(coriolisAcceleration);
66 double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP);
68 // Perform Euler integration
69 status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
70 add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)));
71 status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
72 status.setSimulationTime(status.getSimulationTime() + timeStep);
75 // Update the world coordinate
76 WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
77 w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition());
78 status.setRocketWorldPosition(w);
82 FlightDataBranch data = status.getFlightData();
83 boolean extra = status.getSimulationConditions().isCalculateExtras();
86 data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime());
87 data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z);
88 data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
89 data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
91 data.setValue(FlightDataType.TYPE_POSITION_XY,
92 MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y));
93 data.setValue(FlightDataType.TYPE_POSITION_DIRECTION,
94 Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x));
96 data.setValue(FlightDataType.TYPE_VELOCITY_XY,
97 MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y));
98 data.setValue(FlightDataType.TYPE_ACCELERATION_XY,
99 MathUtil.hypot(linearAcceleration.x, linearAcceleration.y));
101 data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length());
103 double Re = airSpeed.length() *
104 status.getConfiguration().getLength() /
105 atmosphere.getKinematicViscosity();
106 data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re);
110 data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
111 data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
112 if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) {
113 data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length());
117 data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z);
118 data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z);
120 data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length());
121 data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach);
123 data.setValue(FlightDataType.TYPE_MASS, mass);
125 data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0);
126 data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce);
128 data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length());
129 data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature());
130 data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure());
131 data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed());
133 data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep);
134 data.setValue(FlightDataType.TYPE_COMPUTATION_TIME,
135 (System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0);