Merge branch 'openrocket:unstable' into issue-2485

This commit is contained in:
Ahanu Dewhirst 2024-07-19 13:02:48 +10:00 committed by GitHub
commit c4f93b1a6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 80 additions and 62 deletions

View File

@ -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();

View File

@ -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,16 +238,11 @@ 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;
@ -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) {

View File

@ -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,7 +300,9 @@ 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());
}

View File

@ -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'

View File

@ -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.