Merge branch 'openrocket:unstable' into issue-2485
This commit is contained in:
commit
c4f93b1a6e
@ -70,22 +70,24 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper {
|
||||
}
|
||||
|
||||
// Compute drag acceleration
|
||||
store.linearAcceleration = airSpeed.normalize().multiply(-store.dragForce / store.rocketMass.getMass());
|
||||
Coordinate linearAcceleration = airSpeed.normalize().multiply(-store.dragForce / store.rocketMass.getMass());
|
||||
|
||||
// Add effect of gravity
|
||||
store.gravity = modelGravity(status);
|
||||
store.linearAcceleration = store.linearAcceleration.sub(0, 0, store.gravity);
|
||||
linearAcceleration = linearAcceleration.sub(0, 0, store.gravity);
|
||||
|
||||
// Add coriolis acceleration
|
||||
store.coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration(
|
||||
status.getRocketWorldPosition(), status.getRocketVelocity());
|
||||
store.linearAcceleration = store.linearAcceleration.add(store.coriolisAcceleration);
|
||||
linearAcceleration = linearAcceleration.add(store.coriolisAcceleration);
|
||||
|
||||
store.accelerationData = new AccelerationData(null, null, linearAcceleration, Coordinate.NUL, status.getRocketOrientationQuaternion());
|
||||
|
||||
// Select tentative time step
|
||||
store.timeStep = RECOVERY_TIME_STEP;
|
||||
|
||||
// adjust based on acceleration
|
||||
final double absAccel = store.linearAcceleration.length();
|
||||
final double absAccel = linearAcceleration.length();
|
||||
if (absAccel > MathUtil.EPSILON) {
|
||||
store.timeStep = Math.min(store.timeStep, 1.0/absAccel);
|
||||
}
|
||||
@ -105,7 +107,7 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper {
|
||||
log.trace("timeStep is " + store.timeStep);
|
||||
|
||||
// Perform Euler integration
|
||||
EulerValues newVals = eulerIntegrate(status.getRocketPosition(), status.getRocketVelocity(), store.linearAcceleration, store.timeStep);
|
||||
EulerValues newVals = eulerIntegrate(status.getRocketPosition(), status.getRocketVelocity(), 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
|
||||
@ -115,7 +117,7 @@ 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 = store.linearAcceleration.z;
|
||||
final double a = linearAcceleration.z;
|
||||
final double v = status.getRocketVelocity().z;
|
||||
final double z = status.getRocketPosition().z;
|
||||
double t = store.timeStep;
|
||||
@ -136,11 +138,11 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper {
|
||||
// dA/dT = dA/dV * dV/dT
|
||||
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));
|
||||
final Coordinate jerk = linearAcceleration.multiply(dAdV);
|
||||
final Coordinate newAcceleration = linearAcceleration.add(jerk.multiply(store.timeStep));
|
||||
|
||||
// Only do this one if acceleration is appreciably different from 0
|
||||
if (newAcceleration.z * store.linearAcceleration.z < -MathUtil.EPSILON) {
|
||||
if (newAcceleration.z * 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);
|
||||
@ -159,7 +161,7 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper {
|
||||
store.timeStep = maxTimeStep;
|
||||
}
|
||||
|
||||
newVals = eulerIntegrate(status.getRocketPosition(), status.getRocketVelocity(), store.linearAcceleration, store.timeStep);
|
||||
newVals = eulerIntegrate(status.getRocketPosition(), status.getRocketVelocity(), linearAcceleration, store.timeStep);
|
||||
|
||||
// If we just landed chop off rounding error
|
||||
if (Math.abs(newVals.pos.z) < MathUtil.EPSILON) {
|
||||
@ -171,7 +173,6 @@ public abstract class AbstractEulerStepper extends AbstractSimulationStepper {
|
||||
|
||||
status.setRocketPosition(newVals.pos);
|
||||
status.setRocketVelocity(newVals.vel);
|
||||
status.setRocketAcceleration(store.linearAcceleration);
|
||||
|
||||
// Update the world coordinate
|
||||
WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
|
||||
|
@ -163,14 +163,12 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
|
||||
*
|
||||
* @param status the current simulation status.
|
||||
* @param timestep the time step of the current iteration.
|
||||
* @param acceleration the current (approximate) acceleration
|
||||
* @param atmosphericConditions the current atmospheric conditions
|
||||
* @param store the simulation calculation DataStore (contains acceleration, atmosphere)
|
||||
* @param stepMotors whether to step the motors forward or work on a clone object
|
||||
* @return the average thrust during the time step.
|
||||
*/
|
||||
protected double calculateThrust(SimulationStatus status,
|
||||
double acceleration, AtmosphericConditions atmosphericConditions,
|
||||
boolean stepMotors) throws SimulationException {
|
||||
protected double calculateThrust(SimulationStatus status, DataStore store,
|
||||
boolean stepMotors) throws SimulationException {
|
||||
double thrust;
|
||||
|
||||
// Pre-listeners
|
||||
@ -240,17 +238,12 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
|
||||
|
||||
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;
|
||||
@ -264,9 +257,6 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
|
||||
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) {
|
||||
@ -286,12 +276,12 @@ public abstract class AbstractSimulationStepper implements SimulationStepper {
|
||||
dataBranch.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length());
|
||||
}
|
||||
|
||||
if (null != linearAcceleration) {
|
||||
if (null != accelerationData) {
|
||||
dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_XY,
|
||||
MathUtil.hypot(linearAcceleration.x, linearAcceleration.y));
|
||||
MathUtil.hypot(accelerationData.getLinearAccelerationWC().x, accelerationData.getLinearAccelerationWC().y));
|
||||
|
||||
dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length());
|
||||
dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z);
|
||||
dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, accelerationData.getLinearAccelerationWC().length());
|
||||
dataBranch.setValue(FlightDataType.TYPE_ACCELERATION_Z, accelerationData.getLinearAccelerationWC().z);
|
||||
}
|
||||
|
||||
if (null != rocketMass) {
|
||||
|
@ -57,7 +57,7 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
|
||||
// private static final double MAX_ROLL_STEP_ANGLE = 8.32 * Math.PI/180;
|
||||
|
||||
private static final double MAX_ROLL_RATE_CHANGE = 2 * Math.PI / 180;
|
||||
private static final double MAX_PITCH_CHANGE = 4 * Math.PI / 180;
|
||||
private static final double MAX_PITCH_YAW_CHANGE = 4 * Math.PI / 180;
|
||||
|
||||
private Random random;
|
||||
DataStore store = new DataStore();
|
||||
@ -133,8 +133,10 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
|
||||
dt[1] = maxTimeStep;
|
||||
dt[2] = status.getSimulationConditions().getMaximumAngleStep() / store.lateralPitchRate;
|
||||
dt[3] = Math.abs(MAX_ROLL_STEP_ANGLE / store.flightConditions.getRollRate());
|
||||
dt[4] = Math.abs(MAX_ROLL_RATE_CHANGE / store.rollAcceleration);
|
||||
dt[5] = Math.abs(MAX_PITCH_CHANGE / store.lateralPitchAcceleration);
|
||||
dt[4] = Math.abs(MAX_ROLL_RATE_CHANGE / store.accelerationData.getRotationalAccelerationRC().z);
|
||||
dt[5] = Math.abs(MAX_PITCH_YAW_CHANGE /
|
||||
MathUtil.max(Math.abs(store.accelerationData.getRotationalAccelerationRC().x),
|
||||
Math.abs(store.accelerationData.getRotationalAccelerationRC().y)));
|
||||
if (!status.isLaunchRodCleared()) {
|
||||
dt[0] /= 5.0;
|
||||
dt[6] = status.getSimulationConditions().getLaunchRodLength() / k1.v.length() / 10;
|
||||
@ -273,8 +275,8 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
|
||||
// Call post-listeners
|
||||
store.accelerationData = SimulationListenerHelper.firePostAccelerationCalculation(status, store.accelerationData);
|
||||
|
||||
params.a = dataStore.linearAcceleration;
|
||||
params.ra = dataStore.angularAcceleration;
|
||||
params.a = dataStore.accelerationData.getLinearAccelerationWC();
|
||||
params.ra = dataStore.accelerationData.getRotationalAccelerationWC();
|
||||
params.v = status.getRocketVelocity();
|
||||
params.rv = status.getRocketRotationVelocity();
|
||||
|
||||
@ -298,6 +300,8 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
|
||||
* @throws SimulationException
|
||||
*/
|
||||
private AccelerationData calculateAcceleration(SimulationStatus status, DataStore store) throws SimulationException {
|
||||
Coordinate linearAcceleration;
|
||||
Coordinate angularAcceleration;
|
||||
|
||||
// Compute the forces affecting the rocket
|
||||
calculateForces(status, store);
|
||||
@ -325,35 +329,33 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
|
||||
double fN = store.forces.getCN() * dynP * refArea;
|
||||
double fSide = store.forces.getCside() * dynP * refArea;
|
||||
|
||||
store.thrustForce = calculateThrust(status, store.longitudinalAcceleration, store.flightConditions.getAtmosphericConditions(), false);
|
||||
store.thrustForce = calculateThrust(status, store, false);
|
||||
double forceZ = store.thrustForce - store.dragForce;
|
||||
|
||||
store.linearAcceleration = new Coordinate(-fN / store.rocketMass.getMass(),
|
||||
linearAcceleration = new Coordinate(-fN / store.rocketMass.getMass(),
|
||||
-fSide / store.rocketMass.getMass(),
|
||||
forceZ / store.rocketMass.getMass());
|
||||
|
||||
store.linearAcceleration = store.thetaRotation.rotateZ(store.linearAcceleration);
|
||||
linearAcceleration = store.thetaRotation.rotateZ(linearAcceleration);
|
||||
|
||||
// Convert into rocket world coordinates
|
||||
store.linearAcceleration = status.getRocketOrientationQuaternion().rotate(store.linearAcceleration);
|
||||
linearAcceleration = status.getRocketOrientationQuaternion().rotate(linearAcceleration);
|
||||
|
||||
// add effect of gravity
|
||||
store.gravity = modelGravity(status);
|
||||
store.linearAcceleration = store.linearAcceleration.sub(0, 0, store.gravity);
|
||||
linearAcceleration = 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);
|
||||
linearAcceleration = 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()) {
|
||||
|
||||
store.linearAcceleration = store.launchRodDirection.multiply(store.linearAcceleration.dot(store.launchRodDirection));
|
||||
store.angularAcceleration = Coordinate.NUL;
|
||||
store.rollAcceleration = 0;
|
||||
store.lateralPitchAcceleration = 0;
|
||||
linearAcceleration = store.launchRodDirection.multiply(linearAcceleration.dot(store.launchRodDirection));
|
||||
angularAcceleration = Coordinate.NUL;
|
||||
|
||||
} else {
|
||||
|
||||
@ -366,24 +368,18 @@ public class RK4SimulationStepper extends AbstractSimulationStepper {
|
||||
double momY = Cm * dynP * refArea * refLength;
|
||||
double momZ = store.forces.getCroll() * dynP * refArea * refLength;
|
||||
|
||||
// Compute acceleration in rocket coordinates
|
||||
store.angularAcceleration = new Coordinate(momX / store.rocketMass.getLongitudinalInertia(),
|
||||
// Compute angular acceleration in rocket coordinates
|
||||
angularAcceleration = new Coordinate(momX / store.rocketMass.getLongitudinalInertia(),
|
||||
momY / store.rocketMass.getLongitudinalInertia(),
|
||||
momZ / store.rocketMass.getRotationalInertia());
|
||||
|
||||
store.rollAcceleration = store.angularAcceleration.z;
|
||||
// TODO: LOW: This should be hypot, but does it matter?
|
||||
store.lateralPitchAcceleration = MathUtil.max(Math.abs(store.angularAcceleration.x),
|
||||
Math.abs(store.angularAcceleration.y));
|
||||
|
||||
store.angularAcceleration = store.thetaRotation.rotateZ(store.angularAcceleration);
|
||||
angularAcceleration = store.thetaRotation.rotateZ(angularAcceleration);
|
||||
|
||||
// Convert to world coordinates
|
||||
store.angularAcceleration = status.getRocketOrientationQuaternion().rotate(store.angularAcceleration);
|
||||
|
||||
angularAcceleration = status.getRocketOrientationQuaternion().rotate(angularAcceleration);
|
||||
}
|
||||
|
||||
return new AccelerationData(null, null, store.linearAcceleration, store.angularAcceleration, status.getRocketOrientationQuaternion());
|
||||
return new AccelerationData(null, null, linearAcceleration, angularAcceleration, status.getRocketOrientationQuaternion());
|
||||
}
|
||||
|
||||
|
||||
|
@ -80,9 +80,9 @@ dependencies {
|
||||
implementation group: 'io.github.g00fy2', name: 'versioncompare', version: '1.5.0'
|
||||
implementation 'com.github.Dansoftowner:jSystemThemeDetector:3.8'
|
||||
|
||||
implementation group: 'com.formdev', name: 'flatlaf', version: '3.4.1'
|
||||
implementation group: 'com.formdev', name: 'flatlaf-extras', version: '3.4.1'
|
||||
implementation group: 'com.formdev', name: 'flatlaf-intellij-themes', version: '3.4.1'
|
||||
implementation group: 'com.formdev', name: 'flatlaf', version: '3.5'
|
||||
implementation group: 'com.formdev', name: 'flatlaf-extras', version: '3.5'
|
||||
implementation group: 'com.formdev', name: 'flatlaf-intellij-themes', version: '3.5'
|
||||
|
||||
implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.5.0'
|
||||
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.5.0'
|
||||
|
@ -217,12 +217,27 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
scrollPane = new ScaleScrollPane(figure) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
final CustomClickCountListener clickCountListener = new CustomClickCountListener();
|
||||
private Point mousePressedLoc = null;
|
||||
private double originalFigureRotation = 0;
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent event) {
|
||||
clickCountListener.click();
|
||||
handleMouseClick(event, clickCountListener.getClickCount());
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (is3d) {
|
||||
return;
|
||||
}
|
||||
mousePressedLoc = e.getPoint();
|
||||
originalFigureRotation = figure.getRotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
handleMouseDragged(e, mousePressedLoc, originalFigureRotation);
|
||||
}
|
||||
};
|
||||
scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
|
||||
scrollPane.setFitting(true);
|
||||
@ -413,6 +428,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
|
||||
// Create slider and scroll pane
|
||||
rotationModel = new DoubleModel(figure, "Rotation", UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI);
|
||||
figure.addChangeListener(rotationModel);
|
||||
UnitSelector us = new UnitSelector(rotationModel, true);
|
||||
us.setHorizontalAlignment(JLabel.CENTER);
|
||||
add(us, "alignx 50%, growx");
|
||||
@ -422,12 +438,13 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
add(figureHolder, "grow, spany 2, wmin 300lp, hmin 100lp, wrap");
|
||||
|
||||
// Add rotation slider
|
||||
// Dummy label to find the minimum size to fit "360deg"
|
||||
JLabel l = new JLabel("360" + Chars.DEGREE);
|
||||
// Dummy label to find the minimum size to fit "360.0deg"
|
||||
JLabel l = new JLabel("360.0" + Chars.DEGREE);
|
||||
Dimension d = l.getPreferredSize();
|
||||
|
||||
add(rotationSlider = new BasicSlider(rotationModel.getSliderModel(0, 2 * Math.PI), JSlider.VERTICAL, true),
|
||||
"ax 50%, wrap, width " + (d.width + 6) + "px:null:null, growy");
|
||||
us.setMinimumSize(new Dimension(d.width, us.getPreferredSize().height));
|
||||
rotationSlider = new BasicSlider(rotationModel.getSliderModel(0, 2 * Math.PI), JSlider.VERTICAL, true);
|
||||
add(rotationSlider, "ax 50%, wrap, width " + (d.width + 6) + "px:null:null, growy");
|
||||
rotationSlider.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
@ -737,6 +754,20 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMouseDragged(MouseEvent event, Point originalDragLocation, double originalRotation) {
|
||||
if (originalDragLocation == null || is3d) {
|
||||
return;
|
||||
}
|
||||
|
||||
int dy = event.getY() - originalDragLocation.y;
|
||||
|
||||
double rotationOffset = ((double) dy / scrollPane.getHeight()) * Math.PI;
|
||||
double newRotation = originalRotation - rotationOffset;
|
||||
// Ensure the rotation is within the range [0, 2*PI]
|
||||
newRotation = (newRotation + 2 * Math.PI) % (2 * Math.PI);
|
||||
figure.setRotation(newRotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the extra data included in the figure. Currently this includes
|
||||
* the CP and CG carets. Also start the background simulator.
|
||||
|
Loading…
x
Reference in New Issue
Block a user