Merge branch 'openrocket:unstable' into fix-tubefins

This commit is contained in:
Joe Pfeiffer 2023-06-28 15:18:53 -06:00 committed by GitHub
commit 6908bd8e7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 1116 additions and 687 deletions

View File

@ -17,10 +17,10 @@ jobs:
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup JDK 11
- name: Setup JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
- name: Ant build
run: ant -noinput -buildfile build.xml clean check jar unittest

4
.idea/ant.xml generated
View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AntConfiguration">
<buildFile url="file://$PROJECT_DIR$/build.xml" />
<buildFile url="file://$PROJECT_DIR$/build.xml">
<customJdkName value="17" />
</buildFile>
</component>
</project>

2
.idea/misc.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -1,25 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All tests" type="JUnit" factoryName="JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$MODULE_DIR$" />
<option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="singleModule" />
</option>
<envs />
<patterns />
<method>
<option name="Make" enabled="false" />
</method>
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
<method v="2" />
</configuration>
</component>

View File

@ -2,7 +2,7 @@
<configuration default="false" name="Openrocket UI Jar" type="JarApplication">
<option name="JAR_PATH" value="$PROJECT_DIR$/build/jar/OpenRocket.jar" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="ALTERNATIVE_JRE_PATH" value="11" />
<option name="ALTERNATIVE_JRE_PATH" value="17" />
<method v="2">
<option name="BuildArtifacts" enabled="true">
<artifact name="openrocket:jar" />

90
.idea/workspace.xml generated Normal file
View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AnalysisUIOptions">
<option name="ANALYZE_INJECTED_CODE" value="false" />
</component>
<component name="ArtifactsWorkspaceSettings">
<artifacts-to-build>
<artifact name="openrocket:jar" />
</artifacts-to-build>
</component>
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Interface" />
<option value="Class" />
</list>
</option>
</component>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$PROJECT_DIR$/core/resources/l10n/messages.properties" root0="SKIP_INSPECTION" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="CurrentFile" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="RunManager" selected="Application.SwingStartup">
<configuration name="jar" type="AntRunConfiguration">
<antsettings antfile="file://$PROJECT_DIR$/build.xml" target="jar">
<property name="source" value="17" />
<property name="target" value="17" />
</antsettings>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="SwingStartup" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
<option name="ALTERNATIVE_JRE_PATH" value="17" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="MAIN_CLASS_NAME" value="net.sf.openrocket.startup.SwingStartup" />
<module name="OpenRocket Swing" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="net.sf.openrocket.startup.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<list>
<item itemvalue="Ant Target.jar" />
<item itemvalue="Application.SwingStartup" />
<item itemvalue="JAR Application.Openrocket UI Jar" />
<item itemvalue="JUnit.All tests" />
</list>
<recent_temporary>
<list>
<item itemvalue="Application.SwingStartup" />
</list>
</recent_temporary>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="2ecf826f-1227-4870-b9d4-17215cf24a1b" name="Changes" comment="Fix transition config layout" />
<created>1625095153807</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1625095153807</updated>
</component>
</project>

View File

@ -13,6 +13,15 @@ Release Notes
</div>
<div id="23.xx">
OpenRocket 23.xx
------------------------
Here, the release notes for the next release will be documented.
</div>
<div id="22.02">
OpenRocket 22.02 (2023-02-08)

View File

@ -59,7 +59,7 @@
<echo level="info">Java/JVM detail version: ${java.version}</echo>
<mkdir dir="${classes.dir}"/>
<echo level="info">Compiling main classes</echo>
<javac debug="true" srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" includeantruntime="false" source="1.8" target="1.8"/>
<javac debug="true" srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" includeantruntime="false" source="17" target="17"/>
</target>
<!-- Executable Eclipse-Jar-In-Jar style JAR -->

View File

@ -4,6 +4,7 @@ OpenRocket text:
Create logo using Gimp's "Blended" logo script.
Apply suitable gradient to text layer
(softened "Horizon 1" -> "Horizon 1 soft").
Font: Kanit (Italic): https://fonts.google.com/specimen/Kanit?query=kanit
Background starfield:

View File

@ -1,6 +1,6 @@
# The OpenRocket build version
build.version=22.02
build.version=23.xx
# The copyright year for the build. Displayed in the about dialog.
# Will show as Copyright 2013-${build.copyright}

View File

@ -297,6 +297,7 @@ pref.dlg.tab.Launch = Launch
pref.dlg.tab.Miscellaneousoptions = Miscellaneous options
pref.dlg.lbl.RASAeroWarning = Show warning when saving in RASAero format
pref.dlg.lbl.RockSimWarning = Show warning when saving in RockSim format
pref.dlg.checkbox.ShowDiscardPreferencesConfirmation = Show confirmation dialog when discarding preferences
pref.dlg.but.resetAllPreferences = Reset all preferences
pref.dlg.but.resetAllPreferences.ttip = Reset all the preferences, including cached preferences (UI settings, recent files, etc.)
pref.dlg.but.exportPreferences = Export preferences
@ -366,6 +367,10 @@ pref.dlg.lbl.Temperature = Temperature:
pref.dlg.lbl.Momentofinertia = Moment of inertia:
pref.dlg.lbl.Pressure = Pressure:
pref.dlg.lbl.Stability = Stability:
pref.dlg.lbl.SecondaryStability = Secondary Stability:
pref.dlg.lbl.SecondaryStability.ttip = Select the stability unit that will be displayed alongside the main stability unit in the design view.
pref.dlg.checkbox.DisplaySecondaryStability = Display secondary stability unit
pref.dlg.checkbox.DisplaySecondaryStability.ttip = If checked, a secondary stability unit will be displayed in the rocket design view.
pref.dlg.lbl.FlightTime = Flight time:
pref.dlg.lbl.effect1 = The effects will take place the next time you open a window.
pref.dlg.lbl.Checkingupdates = Checking for updates\u2026
@ -380,6 +385,8 @@ pref.dlg.DescriptionArea.Adddirectories = Add directories, RASP motor files (*.e
PreferencesDialog.lbl.language = Interface language:
PreferencesDialog.languages.default = System default
PreferencesDialog.lbl.languageEffect = The language will change the next time you start OpenRocket.
PreferencesDialog.CancelOperation.title = Discard Preference Changes
PreferencesDialog.CancelOperation.msg.discardChanges = <html>Are you sure you want to <b>discard the preference changes</b>?</html>
generalprefs.lbl.language = Interface language
generalprefs.languages.default = System default
@ -619,7 +626,7 @@ SimuRunDlg.msg.branchErrorOccurred = An error occurred during simulation branch
BasicEventSimulationEngine.error.noMotorsDefined = No motors defined in the simulation.
BasicEventSimulationEngine.error.activeLengthZero = Active airframe has length 0
BasicEventSimulationEngine.error.cantCalculateStability = Can't calculate stability
BasicEventSimulationEngine.error.earlyMotorBurnout = Motor burnout without liftoff.
BasicEventSimulationEngine.error.earlyMotorBurnout = <html>Motor burnout without liftoff.<br>Use more (powerful) motors, or decrease the rocket mass.</html>
BasicEventSimulationEngine.error.noConfiguredIgnition = No motors configured to ignite at liftoff
BasicEventSimulationEngine.error.noIgnition = No motors ignited.
BasicEventSimulationEngine.error.NaNResult = Simulation resulted in not-a-number (NaN) value, please report a bug.
@ -1504,6 +1511,7 @@ TCMotorSelPan.btn.close = Close
! PlotDialog
PlotDialog.CheckBox.Showdatapoints = Show data points
PlotDialog.lbl.Chart = left click drag to zoom area. mouse wheel to zoom. ctrl-mouse wheel to zoom x axis only. ctrl-left click drag to pan. right click drag to zoom dynamically.
PlotDialog.lbl.timeSeriesWarning = The data is plotted in time order even though the X axis type is not time.
PlotDialog.btn.exportImage = Export Image
ComponentTree.ttip.massoverride = mass overriden

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 326 B

View File

@ -172,8 +172,8 @@ public class BodyTube extends SymmetricComponent implements BoxBounded, MotorMou
@Override
protected void loadFromPreset(ComponentPreset preset) {
this.autoRadius = false;
if (preset.has(ComponentPreset.OUTER_DIAMETER)) {
this.autoRadius = false;
double outerDiameter = preset.get(ComponentPreset.OUTER_DIAMETER);
this.outerRadius = outerDiameter / 2.0;
if (preset.has(ComponentPreset.INNER_DIAMETER)) {

View File

@ -102,7 +102,7 @@ public abstract class ExternalComponent extends RocketComponent {
*/
@Override
public double getComponentMass() {
return material.getDensity() * getComponentVolume() * getInstanceCount();
return material.getDensity() * getComponentVolume();
}
/**

View File

@ -544,7 +544,7 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
return centerOfMass;
}
private static Coordinate calculateFilletCrossSection(final double filletRadius, final double bodyRadius){
private static Coordinate calculateFilletCrossSection(final double filletRadius, final double bodyRadius) {
final double hypotenuse = filletRadius + bodyRadius;
final double innerArcAngle = Math.asin(filletRadius / hypotenuse);
final double outerArcAngle = Math.acos(filletRadius / hypotenuse);
@ -554,17 +554,17 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
- outerArcAngle * filletRadius * filletRadius / 2
- innerArcAngle * bodyRadius * bodyRadius / 2);
if(Double.isNaN(crossSectionArea)) {
if (Double.isNaN(crossSectionArea)) {
crossSectionArea = 0.;
}else {
} else {
// each fin has a fillet on each side
crossSectionArea *= 2;
}
// heuristic, relTo the body center
double yCentroid = bodyRadius + filletRadius /5;
double yCentroid = bodyRadius + filletRadius / 5;
return new Coordinate(0,yCentroid,0,crossSectionArea);
return new Coordinate(0, yCentroid, 0, crossSectionArea);
}
/*
@ -581,7 +581,8 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
* 5. Return twice that since there is a fillet on each side of the fin.
*/
protected Coordinate calculateFilletVolumeCentroid() {
if((null == this.parent) || (!SymmetricComponent.class.isAssignableFrom(this.parent.getClass()))){
if ((this.filletRadius == 0) || (this.parent == null) ||
(!SymmetricComponent.class.isAssignableFrom(this.parent.getClass()))) {
return Coordinate.ZERO;
}
Coordinate[] mountPoints = this.getRootPoints();
@ -594,7 +595,7 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
final Coordinate finLead = getFinFront();
final double xFinEnd = finLead.x + getLength();
final Coordinate[] rootPoints = getMountPoints( finLead.x, xFinEnd, -finLead.x, -finLead.y);
if (0 == rootPoints.length) {
if (rootPoints.length == 0) {
return Coordinate.ZERO;
}
@ -616,7 +617,7 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
final Coordinate segmentCentroid = segmentCrossSection.setWeight(segmentVolume);
filletVolumeCentroid = filletVolumeCentroid.add(segmentCentroid);
filletVolumeCentroid = filletVolumeCentroid.average(segmentCentroid);
prev = cur;
}
@ -624,7 +625,7 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
if (finCount == 1) {
Transformation rotation = Transformation.rotate_x( getAngleOffset());
return rotation.transform(filletVolumeCentroid);
}else{
} else{
return filletVolumeCentroid.setY(0.);
}
}

View File

@ -215,7 +215,7 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li
@Override
public double getComponentVolume() {
return length * Math.PI * (MathUtil.pow2(radius) - MathUtil.pow2(radius - thickness));
return length * Math.PI * (MathUtil.pow2(radius) - MathUtil.pow2(radius - thickness)) * getInstanceCount();
}
@Override
@ -228,9 +228,12 @@ public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, Li
@Override
public Coordinate getComponentCG() {
final double parentRadius = parent instanceof SymmetricComponent ?
((SymmetricComponent) parent).getRadius(getAxialOffset()) : 0;
final double CMx = length / 2 + (instanceSeparation * (instanceCount-1)) / 2;
final double CMy = Math.cos(this.angleOffsetRad)*getOuterRadius();
final double CMz = Math.sin(this.angleOffsetRad)*getOuterRadius();
final double CMy = Math.cos(this.angleOffsetRad) * (parentRadius + getOuterRadius());
final double CMz = Math.sin(this.angleOffsetRad) * (parentRadius + getOuterRadius());
return new Coordinate(CMx, CMy, CMz, getComponentMass());
}

View File

@ -304,7 +304,8 @@ public class RailButton extends ExternalComponent implements AnglePositionable,
final double volInner = Math.PI*Math.pow( innerDiameter_m/2, 2)*getInnerHeight();
final double volStandoff = Math.PI*Math.pow( outerDiameter_m/2, 2)* baseHeight_m;
final double volScrew = 2f/3 * Math.PI * MathUtil.pow2(outerDiameter_m/2) * screwHeight_m;
return volOuter + volInner + volStandoff + volScrew;
final double volInstance = volOuter + volInner + volStandoff + volScrew;
return volInstance * getInstanceCount();
}
@Override
@ -380,14 +381,16 @@ public class RailButton extends ExternalComponent implements AnglePositionable,
final double flangeCM = this.totalHeight_m - getFlangeHeight()/2;
final double screwCM = this.totalHeight_m + 4 * this.screwHeight_m / (3 * Math.PI);
final double heightCM = (massBase*baseCM + massInner*innerCM + massFlange*flangeCM + massScrew*screwCM)/totalMass;
final double parentRadius = parent instanceof SymmetricComponent ?
((SymmetricComponent) parent).getRadius(getAxialOffset()) : 0;
if (heightCM > this.totalHeight_m + this.screwHeight_m) {
throw new BugException(" bug found while computing the CG of a RailButton: "+this.getName()+"\n height of CG: "+heightCM);
}
final double CMx = (instanceSeparation * (instanceCount-1)) / 2;
final double CMy = Math.cos(this.angleOffsetRad)*heightCM;
final double CMz = Math.sin(this.angleOffsetRad)*heightCM;
final double CMy = Math.cos(this.angleOffsetRad) * (parentRadius + heightCM);
final double CMz = Math.sin(this.angleOffsetRad) * (parentRadius + heightCM);
return new Coordinate( CMx, CMy, CMz, getComponentMass());
}

View File

@ -60,6 +60,14 @@ public class StageSeparationConfiguration implements FlightConfigurableParameter
return (mount == ignition);
}
},
//// Launch
LAUNCH(trans.get("Stage.SeparationEvent.LAUNCH")) {
@Override
public boolean isSeparationEvent(FlightEvent e, AxialStage stage) {
return e.getType() == FlightEvent.Type.LAUNCH;
}
},
//// Never
NEVER(trans.get("Stage.SeparationEvent.NEVER")) {
@Override
public boolean isSeparationEvent(FlightEvent e, AxialStage stage) {

View File

@ -518,6 +518,19 @@ public class TubeFinSet extends Tube implements AxialPositionable, BoxBounded, R
}
@Override
protected void loadFromPreset(ComponentPreset preset) {
super.loadFromPreset(preset);
if (preset.has(ComponentPreset.OUTER_DIAMETER)) {
this.autoRadius = false;
double outerDiameter = preset.get(ComponentPreset.OUTER_DIAMETER);
this.outerRadius = outerDiameter / 2.0;
if (preset.has(ComponentPreset.INNER_DIAMETER)) {
double innerDiameter = preset.get(ComponentPreset.INNER_DIAMETER);
this.thickness = (outerDiameter - innerDiameter) / 2.0;
}
}
}
@Override
public InsideColorComponentHandler getInsideColorComponentHandler() {

View File

@ -0,0 +1,184 @@
package net.sf.openrocket.simulation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.rocketcomponent.InstanceMap;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.GeodeticComputationStrategy;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.WorldCoordinate;
public abstract class AbstractEulerStepper extends AbstractSimulationStepper {
private static final Logger log = LoggerFactory.getLogger(AbstractEulerStepper.class);
private static final double RECOVERY_TIME_STEP = 0.5;
protected double cd;
@Override
public SimulationStatus initialize(SimulationStatus status) {
this.cd = computeCD(status);
return status;
}
private double getCD() {
return cd;
}
protected abstract double computeCD(SimulationStatus status);
@Override
public void step(SimulationStatus status, double maxTimeStep) throws SimulationException {
// Get the atmospheric conditions
AtmosphericConditions atmosphere = modelAtmosphericConditions(status);
//// Local wind speed and direction
Coordinate windSpeed = modelWindVelocity(status);
Coordinate airSpeed = status.getRocketVelocity().add(windSpeed);
// Compute drag force
double mach = airSpeed.length() / atmosphere.getMachSpeed();
double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
double dragForce = getCD() * dynP * status.getConfiguration().getReferenceArea();
// n.b. this is constant, and could be calculated once at the beginning of this simulation branch...
double rocketMass = calculateStructureMass(status).getMass();
double motorMass = calculateMotorMass(status).getMass();
double mass = rocketMass + motorMass;
// Compute drag acceleration
Coordinate linearAcceleration;
if (airSpeed.length() > 0.001) {
linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass);
} else {
linearAcceleration = Coordinate.NUL;
}
// Add effect of gravity
double gravity = modelGravity(status);
linearAcceleration = linearAcceleration.sub(0, 0, gravity);
// Add coriolis acceleration
Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration(
status.getRocketWorldPosition(), status.getRocketVelocity());
linearAcceleration = linearAcceleration.add(coriolisAcceleration);
// Select tentative time step
double timeStep = RECOVERY_TIME_STEP;
// adjust based on change in acceleration (ie jerk)
final double jerk = Math.abs(linearAcceleration.sub(status.getRocketAcceleration()).multiply(1.0/status.getPreviousTimeStep()).length());
if (jerk > MathUtil.EPSILON) {
timeStep = Math.min(timeStep, 1.0/jerk);
}
// but don't let it get *too* small
timeStep = Math.max(timeStep, MIN_TIME_STEP);
log.trace("timeStep is " + timeStep);
// Perform Euler integration
Coordinate newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2));
// If I've hit the ground, recalculate time step and position
if (newPosition.z < 0) {
final double a = linearAcceleration.z;
final double v = status.getRocketVelocity().z;
final double z0 = status.getRocketPosition().z;
// The new timestep is the solution of
// 1/2 at^2 + vt + z0 = 0
timeStep = (-v - Math.sqrt(v*v - 2*a*z0))/a;
log.trace("ground hit changes timeStep to " + timeStep);
newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2));
// avoid rounding error in new altitude
newPosition = newPosition.setZ(0);
}
status.setSimulationTime(status.getSimulationTime() + timeStep);
status.setPreviousTimeStep(timeStep);
status.setRocketPosition(newPosition);
status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
status.setRocketAcceleration(linearAcceleration);
// Update the world coordinate
WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition());
status.setRocketWorldPosition(w);
// Store data
FlightDataBranch data = status.getFlightData();
boolean extra = status.getSimulationConditions().isCalculateExtras();
data.addPoint();
data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime());
data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z);
data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
airSpeed = status.getRocketVelocity().add(windSpeed);
if (extra) {
data.setValue(FlightDataType.TYPE_POSITION_XY,
MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y));
data.setValue(FlightDataType.TYPE_POSITION_DIRECTION,
Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x));
data.setValue(FlightDataType.TYPE_VELOCITY_XY,
MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y));
data.setValue(FlightDataType.TYPE_ACCELERATION_XY,
MathUtil.hypot(linearAcceleration.x, linearAcceleration.y));
data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length());
double Re = airSpeed.length() *
status.getConfiguration().getLengthAerodynamic() /
atmosphere.getKinematicViscosity();
data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re);
}
data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
data.setValue(FlightDataType.TYPE_GRAVITY, gravity);
if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) {
data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length());
}
data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z);
data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z);
data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length());
data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach);
data.setValue(FlightDataType.TYPE_MASS, mass);
data.setValue(FlightDataType.TYPE_MOTOR_MASS, motorMass);
data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0);
data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce);
data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length());
data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature());
data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure());
data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed());
data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep);
data.setValue(FlightDataType.TYPE_COMPUTATION_TIME,
(System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0);
log.trace("time " + data.getLast(FlightDataType.TYPE_TIME) + ", altitude " + data.getLast(FlightDataType.TYPE_ALTITUDE) + ", velocity " + data.getLast(FlightDataType.TYPE_VELOCITY_Z));
}
}

View File

@ -100,8 +100,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
toSimulate.push(currentStatus);
SimulationListenerHelper.fireStartSimulation(currentStatus);
do{
if( null == toSimulate.peek()){
do {
if (toSimulate.peek() == null) {
break;
}
currentStatus = toSimulate.pop();
@ -121,7 +121,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
if (dataBranch.getLength() == 0) {
flightData.getWarningSet().add(Warning.EMPTY_BRANCH, dataBranch.getBranchName());
}
}while( ! toSimulate.isEmpty());
} while (!toSimulate.isEmpty());
SimulationListenerHelper.fireEndSimulation(currentStatus, null);
@ -707,7 +707,7 @@ public class BasicEventSimulationEngine implements SimulationEngine {
}
}
private FlightData computeCoastTime() {
private FlightData computeCoastTime() throws SimulationException {
try {
SimulationConditions conds = currentStatus.getSimulationConditions().clone();
conds.getSimulationListenerList().add(OptimumCoastListener.INSTANCE);
@ -715,6 +715,8 @@ public class BasicEventSimulationEngine implements SimulationEngine {
FlightData d = e.simulate(conds);
return d;
} catch (SimulationException e) {
throw e;
} catch (Exception e) {
log.warn("Exception computing coast time: ", e);
return null;

View File

@ -1,185 +1,18 @@
package net.sf.openrocket.simulation;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.rocketcomponent.InstanceMap;
import net.sf.openrocket.rocketcomponent.RecoveryDevice;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.GeodeticComputationStrategy;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.WorldCoordinate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BasicLandingStepper extends AbstractEulerStepper {
public class BasicLandingStepper extends AbstractSimulationStepper {
private static final Logger log = LoggerFactory.getLogger(BasicLandingStepper.class);
private static final double RECOVERY_TIME_STEP = 0.5;
@Override
public SimulationStatus initialize(SimulationStatus status) {
return status;
}
@Override
public void step(SimulationStatus status, double maxTimeStep) throws SimulationException {
double totalCD = 0;
double refArea = status.getConfiguration().getReferenceArea();
// Get the atmospheric conditions
AtmosphericConditions atmosphere = modelAtmosphericConditions(status);
//// Local wind speed and direction
Coordinate windSpeed = modelWindVelocity(status);
Coordinate airSpeed = status.getRocketVelocity().add(windSpeed);
// Get total CD
double mach = airSpeed.length() / atmosphere.getMachSpeed();
protected double computeCD(SimulationStatus status) {
// Accumulate CD for all recovery devices
cd = 0;
final InstanceMap imap = status.getConfiguration().getActiveInstances();
for (RecoveryDevice c : status.getDeployedRecoveryDevices()) {
totalCD += imap.count(c) * c.getCD(mach) * c.getArea() / refArea;
cd += imap.count(c) * c.getCD() * c.getArea() / status.getConfiguration().getReferenceArea();
}
// Compute drag force
double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
double dragForce = totalCD * dynP * refArea;
// Calculate mass data
double rocketMass = calculateStructureMass(status).getMass();
double motorMass = calculateMotorMass(status).getMass();
double mass = rocketMass + motorMass;
// Compute drag acceleration
Coordinate linearAcceleration;
if (airSpeed.length() > 0.001) {
linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass);
} else {
linearAcceleration = Coordinate.NUL;
}
// Add effect of gravity
double gravity = modelGravity(status);
linearAcceleration = linearAcceleration.sub(0, 0, gravity);
// Add coriolis acceleration
Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration(
status.getRocketWorldPosition(), status.getRocketVelocity());
linearAcceleration = linearAcceleration.add(coriolisAcceleration);
// Select tentative time step
double timeStep = RECOVERY_TIME_STEP;
// adjust based on change in acceleration (ie jerk)
final double jerk = Math.abs(linearAcceleration.sub(status.getRocketAcceleration()).multiply(1.0/status.getPreviousTimeStep()).length());
if (jerk > MathUtil.EPSILON) {
timeStep = Math.min(timeStep, 1.0/jerk);
}
// but don't let it get *too* small
timeStep = Math.max(timeStep, MIN_TIME_STEP);
log.trace("timeStep is " + timeStep);
// Perform Euler integration
Coordinate newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2));
// If I've hit the ground, recalculate time step and position
if (newPosition.z < 0) {
final double a = linearAcceleration.z;
final double v = status.getRocketVelocity().z;
final double z0 = status.getRocketPosition().z;
// The new timestep is the solution of
// 1/2 at^2 + vt + z0 = 0
timeStep = (-v - Math.sqrt(v*v - 2*a*z0))/a;
log.trace("ground hit changes timeStep to " + timeStep);
newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2));
// avoid rounding error in new altitude
newPosition = newPosition.setZ(0);
}
status.setSimulationTime(status.getSimulationTime() + timeStep);
status.setPreviousTimeStep(timeStep);
status.setRocketPosition(newPosition);
status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
status.setRocketAcceleration(linearAcceleration);
// Update the world coordinate
WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition());
status.setRocketWorldPosition(w);
// Store data
FlightDataBranch data = status.getFlightData();
boolean extra = status.getSimulationConditions().isCalculateExtras();
data.addPoint();
data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime());
data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z);
data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
airSpeed = status.getRocketVelocity().add(windSpeed);
if (extra) {
data.setValue(FlightDataType.TYPE_POSITION_XY,
MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y));
data.setValue(FlightDataType.TYPE_POSITION_DIRECTION,
Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x));
data.setValue(FlightDataType.TYPE_VELOCITY_XY,
MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y));
data.setValue(FlightDataType.TYPE_ACCELERATION_XY,
MathUtil.hypot(linearAcceleration.x, linearAcceleration.y));
data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length());
double Re = airSpeed.length() *
status.getConfiguration().getLengthAerodynamic() /
atmosphere.getKinematicViscosity();
data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re);
}
data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
data.setValue(FlightDataType.TYPE_GRAVITY, gravity);
if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) {
data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length());
}
data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z);
data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z);
data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length());
data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach);
data.setValue(FlightDataType.TYPE_MASS, mass);
data.setValue(FlightDataType.TYPE_MOTOR_MASS, motorMass);
data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0);
data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce);
data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length());
data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature());
data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure());
data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed());
data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep);
data.setValue(FlightDataType.TYPE_COMPUTATION_TIME,
(System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0);
log.trace("time " + data.getLast(FlightDataType.TYPE_TIME) + ", altitude " + data.getLast(FlightDataType.TYPE_ALTITUDE) + ", velocity " + data.getLast(FlightDataType.TYPE_VELOCITY_Z));
return cd;
}
}

View File

@ -1,85 +0,0 @@
package net.sf.openrocket.simulation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
import net.sf.openrocket.rocketcomponent.InstanceContext;
import net.sf.openrocket.rocketcomponent.InstanceMap;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
public class BasicTumbleStatus extends SimulationStatus {
// Magic constants from techdoc.pdf
private final static double cDFin = 1.42;
private final static double cDBt = 0.56;
// Fin efficiency. Index is number of fins. The 0th entry is arbitrary and used to
// offset the indexes so finEff[1] is the coefficient for one fin from the table in techdoc.pdf
private final static double[] finEff = { 0.0, 0.5, 1.0, 1.41, 1.81, 1.73, 1.90, 1.85 };
private final double drag;
public BasicTumbleStatus(FlightConfiguration configuration,
SimulationConditions simulationConditions) {
super(configuration, simulationConditions);
this.drag = computeTumbleDrag();
}
public BasicTumbleStatus(SimulationStatus orig) {
super(orig);
if (orig instanceof BasicTumbleStatus) {
this.drag = ((BasicTumbleStatus) orig).drag;
} else {
this.drag = computeTumbleDrag();
}
}
public double getTumbleDrag() {
return drag;
}
private double computeTumbleDrag() {
// Computed based on Sampo's experimentation as documented in the pdf.
// compute the fin and body tube projected areas
double aFins = 0.0;
double aBt = 0.0;
final InstanceMap imap = this.getConfiguration().getActiveInstances();
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) {
final RocketComponent component = entry.getKey();
if (!component.isAerodynamic()) {
continue;
}
// iterate across component instances
final ArrayList<InstanceContext> contextList = entry.getValue();
for(InstanceContext context: contextList ) {
if (component instanceof FinSet) {
final FinSet finComponent = ((FinSet) component);
final double finArea = finComponent.getPlanformArea();
int finCount = finComponent.getFinCount();
// check bounds on finCount.
if (finCount >= finEff.length) {
finCount = finEff.length - 1;
}
aFins += finArea * finEff[finCount] / finComponent.getFinCount();
} else if (component instanceof SymmetricComponent) {
aBt += ((SymmetricComponent) component).getComponentPlanformArea();
}
}
}
return (cDFin * aFins + cDBt * aBt);
}
}

View File

@ -1,165 +1,62 @@
package net.sf.openrocket.simulation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import net.sf.openrocket.models.atmosphere.AtmosphericConditions;
import net.sf.openrocket.simulation.exception.SimulationException;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.GeodeticComputationStrategy;
import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.WorldCoordinate;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.InstanceContext;
import net.sf.openrocket.rocketcomponent.InstanceMap;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
public class BasicTumbleStepper extends AbstractSimulationStepper {
public class BasicTumbleStepper extends AbstractEulerStepper {
private static final double RECOVERY_TIME_STEP = 0.5;
public double computeCD(SimulationStatus status) {
// Computed based on Sampo's experimentation as documented in techdoc.pdf.
@Override
public SimulationStatus initialize(SimulationStatus original) {
BasicTumbleStatus status = new BasicTumbleStatus(original);
status.setWarnings(original.getWarnings());
return status;
}
@Override
public void step(SimulationStatus status, double maxTimeStep) throws SimulationException {
// Magic constants from techdoc.pdf
final double cDFin = 1.42;
final double cDBt = 0.56;
// Fin efficiency. Index is number of fins. The 0th entry is arbitrary and used to
// offset the indexes so finEff[1] is the coefficient for one fin from the table in techdoc.pdf
final double[] finEff = { 0.0, 0.5, 1.0, 1.41, 1.81, 1.73, 1.90, 1.85 };
// Get the atmospheric conditions
AtmosphericConditions atmosphere = modelAtmosphericConditions(status);
//// Local wind speed and direction
Coordinate windSpeed = modelWindVelocity(status);
Coordinate airSpeed = status.getRocketVelocity().add(windSpeed);
// Get total CD
double mach = airSpeed.length() / atmosphere.getMachSpeed();
double tumbleDrag = ((BasicTumbleStatus)status).getTumbleDrag();
// compute the fin and body tube projected areas
double aFins = 0.0;
double aBt = 0.0;
final InstanceMap imap = status.getConfiguration().getActiveInstances();
for(Map.Entry<RocketComponent, ArrayList<InstanceContext>> entry: imap.entrySet() ) {
final RocketComponent component = entry.getKey();
if (!component.isAerodynamic()) {
continue;
}
// iterate across component instances
final ArrayList<InstanceContext> contextList = entry.getValue();
for(InstanceContext context: contextList ) {
// Compute drag force
double dynP = (0.5 * atmosphere.getDensity() * airSpeed.length2());
double dragForce = tumbleDrag * dynP;
// n.b. this is constant, and could be calculated once at the beginning of this simulation branch...
double rocketMass = calculateStructureMass(status).getMass();
double motorMass = calculateMotorMass(status).getMass();
double mass = rocketMass + motorMass;
// Compute drag acceleration
Coordinate linearAcceleration;
if (airSpeed.length() > 0.001) {
linearAcceleration = airSpeed.normalize().multiply(-dragForce / mass);
} else {
linearAcceleration = Coordinate.NUL;
if (component instanceof FinSet) {
final FinSet finComponent = ((FinSet) component);
final double finArea = finComponent.getPlanformArea();
int finCount = finComponent.getFinCount();
// check bounds on finCount.
if (finCount >= finEff.length) {
finCount = finEff.length - 1;
}
aFins += finArea * finEff[finCount] / finComponent.getFinCount();
} else if (component instanceof SymmetricComponent) {
aBt += ((SymmetricComponent) component).getComponentPlanformArea();
}
}
}
// Add effect of gravity
double gravity = modelGravity(status);
linearAcceleration = linearAcceleration.sub(0, 0, gravity);
// Add coriolis acceleration
Coordinate coriolisAcceleration = status.getSimulationConditions().getGeodeticComputation().getCoriolisAcceleration(
status.getRocketWorldPosition(), status.getRocketVelocity());
linearAcceleration = linearAcceleration.add(coriolisAcceleration);
// Select time step
double timeStep = MathUtil.min(0.5 / linearAcceleration.length(), RECOVERY_TIME_STEP);
// Perform Euler integration
Coordinate newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2));
// If I've hit the ground, recalculate time step and position
if (newPosition.z < 0) {
final double a = linearAcceleration.z;
final double v = status.getRocketVelocity().z;
final double z0 = status.getRocketPosition().z;
// The new timestep is the solution of
// 1/2 at^2 + vt + z0 = 0
timeStep = (-v - Math.sqrt(v*v - 2*a*z0))/a;
newPosition = status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2));
// avoid rounding error in new altitude
newPosition = newPosition.setZ(0);
}
status.setRocketPosition(status.getRocketPosition().add(status.getRocketVelocity().multiply(timeStep)).
add(linearAcceleration.multiply(MathUtil.pow2(timeStep) / 2)));
status.setRocketVelocity(status.getRocketVelocity().add(linearAcceleration.multiply(timeStep)));
status.setSimulationTime(status.getSimulationTime() + timeStep);
// Update the world coordinate
WorldCoordinate w = status.getSimulationConditions().getLaunchSite();
w = status.getSimulationConditions().getGeodeticComputation().addCoordinate(w, status.getRocketPosition());
status.setRocketWorldPosition(w);
// Store data
FlightDataBranch data = status.getFlightData();
boolean extra = status.getSimulationConditions().isCalculateExtras();
data.addPoint();
data.setValue(FlightDataType.TYPE_TIME, status.getSimulationTime());
data.setValue(FlightDataType.TYPE_ALTITUDE, status.getRocketPosition().z);
data.setValue(FlightDataType.TYPE_POSITION_X, status.getRocketPosition().x);
data.setValue(FlightDataType.TYPE_POSITION_Y, status.getRocketPosition().y);
if (extra) {
data.setValue(FlightDataType.TYPE_POSITION_XY,
MathUtil.hypot(status.getRocketPosition().x, status.getRocketPosition().y));
data.setValue(FlightDataType.TYPE_POSITION_DIRECTION,
Math.atan2(status.getRocketPosition().y, status.getRocketPosition().x));
data.setValue(FlightDataType.TYPE_VELOCITY_XY,
MathUtil.hypot(status.getRocketVelocity().x, status.getRocketVelocity().y));
data.setValue(FlightDataType.TYPE_ACCELERATION_XY,
MathUtil.hypot(linearAcceleration.x, linearAcceleration.y));
data.setValue(FlightDataType.TYPE_ACCELERATION_TOTAL, linearAcceleration.length());
double Re = airSpeed.length() *
status.getConfiguration().getLengthAerodynamic() /
atmosphere.getKinematicViscosity();
data.setValue(FlightDataType.TYPE_REYNOLDS_NUMBER, Re);
}
data.setValue(FlightDataType.TYPE_LATITUDE, status.getRocketWorldPosition().getLatitudeRad());
data.setValue(FlightDataType.TYPE_LONGITUDE, status.getRocketWorldPosition().getLongitudeRad());
data.setValue(FlightDataType.TYPE_GRAVITY, gravity);
if (status.getSimulationConditions().getGeodeticComputation() != GeodeticComputationStrategy.FLAT) {
data.setValue(FlightDataType.TYPE_CORIOLIS_ACCELERATION, coriolisAcceleration.length());
}
data.setValue(FlightDataType.TYPE_VELOCITY_Z, status.getRocketVelocity().z);
data.setValue(FlightDataType.TYPE_ACCELERATION_Z, linearAcceleration.z);
data.setValue(FlightDataType.TYPE_VELOCITY_TOTAL, airSpeed.length());
data.setValue(FlightDataType.TYPE_MACH_NUMBER, mach);
data.setValue(FlightDataType.TYPE_MASS, mass);
data.setValue(FlightDataType.TYPE_MOTOR_MASS, motorMass);
data.setValue(FlightDataType.TYPE_THRUST_FORCE, 0);
data.setValue(FlightDataType.TYPE_DRAG_FORCE, dragForce);
data.setValue(FlightDataType.TYPE_WIND_VELOCITY, windSpeed.length());
data.setValue(FlightDataType.TYPE_AIR_TEMPERATURE, atmosphere.getTemperature());
data.setValue(FlightDataType.TYPE_AIR_PRESSURE, atmosphere.getPressure());
data.setValue(FlightDataType.TYPE_SPEED_OF_SOUND, atmosphere.getMachSpeed());
data.setValue(FlightDataType.TYPE_TIME_STEP, timeStep);
data.setValue(FlightDataType.TYPE_COMPUTATION_TIME,
(System.nanoTime() - status.getSimulationStartWallTime()) / 1000000000.0);
return (cDFin * aFins + cDBt * aBt)/status.getConfiguration().getReferenceArea();
}
}

View File

@ -44,6 +44,10 @@ public abstract class Preferences implements ChangeSource {
public static final String USER_THRUST_CURVES_KEY = "UserThrustCurves";
public static final String DEFAULT_MACH_NUMBER = "DefaultMachNumber";
// Preferences related to units
public static final String DISPLAY_SECONDARY_STABILITY = "DisplaySecondaryStability";
// Preferences related to data export
public static final String EXPORT_FIELD_SEPARATOR = "ExportFieldSeparator";
public static final String EXPORT_DECIMAL_PLACES = "ExportDecimalPlaces";
@ -79,10 +83,11 @@ public abstract class Preferences implements ChangeSource {
private static final String OPEN_LEFTMOST_DESIGN_TAB = "OpenLeftmostDesignTab";
private static final String SHOW_DISCARD_CONFIRMATION = "IgnoreDiscardEditingWarning";
private static final String SHOW_DISCARD_SIMULATION_CONFIRMATION = "IgnoreDiscardSimulationEditingWarning";
public static final String MARKER_STYLE_ICON = "MARKER_STYLE_ICON";
private static final String SHOW_MARKERS = "SHOW_MARKERS";
private static final String SHOW_RASAERO_FORMAT_WARNING = "SHOW_RASAERO_FORMAT_WARNING";
private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING";
private static final String SHOW_DISCARD_PREFERENCES_CONFIRMATION = "IgnoreDiscardPreferencesWarning";
public static final String MARKER_STYLE_ICON = "MarkerStyleIcon";
private static final String SHOW_MARKERS = "ShowMarkers";
private static final String SHOW_RASAERO_FORMAT_WARNING = "ShowRASAeroFormatWarning";
private static final String SHOW_ROCKSIM_FORMAT_WARNING = "ShowRockSimFormatWarning";
private static final String EXPORT_USER_DIRECTORIES = "ExportUserDirectories";
private static final String EXPORT_WINDOW_INFORMATION = "ExportWindowInformation";
@ -201,6 +206,27 @@ public abstract class Preferences implements ChangeSource {
}
/*
* *********************** Unit Preferences *******************************************
*/
/**
* Return whether to display a secondary stability unit in the rocket design view.
* @return true if the secondary unit should be displayed, false if not.
*/
public final boolean isDisplaySecondaryStability() {
return this.getBoolean(DISPLAY_SECONDARY_STABILITY, true);
}
/**
* Set whether to display a secondary stability unit in the rocket design view.
* @param check if true, display the secondary unit, if false not.
*/
public final void setDisplaySecondaryStability(boolean check) {
this.putBoolean(DISPLAY_SECONDARY_STABILITY, check);
}
/*
* ******************************************************************************************
*/
@ -582,6 +608,22 @@ public abstract class Preferences implements ChangeSource {
this.putBoolean(SHOW_DISCARD_SIMULATION_CONFIRMATION, enabled);
}
/**
* Answer if a confirmation dialog should be shown when canceling preferences changes.
*
* @return true if the confirmation dialog should be shown.
*/
public final boolean isShowDiscardPreferencesConfirmation() {
return this.getBoolean(SHOW_DISCARD_PREFERENCES_CONFIRMATION, true);
}
/**
* Enable/Disable showing a confirmation warning when canceling preferences changes.
*/
public final void setShowDiscardPreferencesConfirmation(boolean enabled) {
this.putBoolean(SHOW_DISCARD_PREFERENCES_CONFIRMATION, enabled);
}
/**
* Answer if the always open leftmost tab is enabled.
*

View File

@ -39,6 +39,8 @@ public class UnitGroup {
public static final UnitGroup UNITS_AREA;
public static final UnitGroup UNITS_STABILITY;
public static final UnitGroup UNITS_SECONDARY_STABILITY;
/**
* This unit group contains only the caliber unit that never scales the originating "SI" value.
* It can be used in cases where the originating value is already in calibers to obtains the correct unit.
@ -162,12 +164,10 @@ public class UnitGroup {
UNITS_STABILITY = new UnitGroup();
UNITS_STABILITY.addUnit(new GeneralUnit(0.001, "mm"));
UNITS_STABILITY.addUnit(new GeneralUnit(0.01, "cm"));
UNITS_STABILITY.addUnit(new GeneralUnit(1, "m"));
UNITS_STABILITY.addUnit(new GeneralUnit(0.0254, "in"));
UNITS_STABILITY.addUnit(new CaliberUnit((Rocket) null));
UNITS_STABILITY.addUnit(new PercentageOfLengthUnit((Rocket) null));
UNITS_SECONDARY_STABILITY = new UnitGroup();
addStabilityUnits(UNITS_STABILITY);
addStabilityUnits(UNITS_SECONDARY_STABILITY);
UNITS_STABILITY_CALIBERS = new UnitGroup();
UNITS_STABILITY_CALIBERS.addUnit(new GeneralUnit(1, "cal"));
@ -310,6 +310,7 @@ public class UnitGroup {
map.put("ACCELERATION", UNITS_ACCELERATION);
map.put("AREA", UNITS_AREA);
map.put("STABILITY", UNITS_STABILITY);
map.put("SECONDARY_STABILITY", UNITS_SECONDARY_STABILITY);
map.put("MASS", UNITS_MASS);
map.put("INERTIA", UNITS_INERTIA);
map.put("ANGLE", UNITS_ANGLE);
@ -366,6 +367,7 @@ public class UnitGroup {
UNITS_DISTANCE.setDefaultUnit("m");
UNITS_AREA.setDefaultUnit("cm" + SQUARED);
UNITS_STABILITY.setDefaultUnit("cal");
UNITS_SECONDARY_STABILITY.setDefaultUnit("%");
UNITS_VELOCITY.setDefaultUnit("m/s");
UNITS_ACCELERATION.setDefaultUnit("m/s" + SQUARED);
UNITS_MASS.setDefaultUnit("g");
@ -392,6 +394,7 @@ public class UnitGroup {
UNITS_DISTANCE.setDefaultUnit("ft");
UNITS_AREA.setDefaultUnit("in" + SQUARED);
UNITS_STABILITY.setDefaultUnit("cal");
UNITS_SECONDARY_STABILITY.setDefaultUnit("%");
UNITS_VELOCITY.setDefaultUnit("ft/s");
UNITS_ACCELERATION.setDefaultUnit("ft/s" + SQUARED);
UNITS_MASS.setDefaultUnit("oz");
@ -425,6 +428,7 @@ public class UnitGroup {
UNITS_ALL_LENGTHS.setDefaultUnit(2);
UNITS_AREA.setDefaultUnit(1);
UNITS_STABILITY.setDefaultUnit(4);
UNITS_SECONDARY_STABILITY.setDefaultUnit(5);
UNITS_STABILITY_CALIBERS.setDefaultUnit(0);
UNITS_VELOCITY.setDefaultUnit(0);
UNITS_WINDSPEED.setDefaultUnit(0);
@ -448,6 +452,15 @@ public class UnitGroup {
UNITS_COEFFICIENT.setDefaultUnit(0);
UNITS_FREQUENCY.setDefaultUnit(1);
}
private static void addStabilityUnits(UnitGroup stabilityUnit) {
stabilityUnit.addUnit(new GeneralUnit(0.001, "mm"));
stabilityUnit.addUnit(new GeneralUnit(0.01, "cm"));
stabilityUnit.addUnit(new GeneralUnit(1, "m"));
stabilityUnit.addUnit(new GeneralUnit(0.0254, "in"));
stabilityUnit.addUnit(new CaliberUnit((Rocket) null));
stabilityUnit.addUnit(new PercentageOfLengthUnit((Rocket) null));
}
/**
@ -456,8 +469,18 @@ public class UnitGroup {
* @param rocket the rocket from which to calculate the caliber
* @return the unit group
*/
public static UnitGroup stabilityUnits(Rocket rocket) {
return new StabilityUnitGroup(rocket);
public static StabilityUnitGroup stabilityUnits(Rocket rocket) {
return new StabilityUnitGroup(UnitGroup.UNITS_STABILITY, rocket);
}
/**
* Return a UnitGroup for secondary stability units based on the rocket.
*
* @param rocket the rocket from which to calculate the caliber
* @return the unit group
*/
public static StabilityUnitGroup secondaryStabilityUnits(Rocket rocket) {
return new StabilityUnitGroup(UnitGroup.UNITS_SECONDARY_STABILITY, rocket);
}
@ -467,8 +490,18 @@ public class UnitGroup {
* @param config the rocket configuration from which to calculate the caliber
* @return the unit group
*/
public static UnitGroup stabilityUnits(FlightConfiguration config) {
return new StabilityUnitGroup(config);
public static StabilityUnitGroup stabilityUnits(FlightConfiguration config) {
return new StabilityUnitGroup(UnitGroup.UNITS_STABILITY, config);
}
/**
* Return a UnitGroup for stability units based on the rocket configuration.
*
* @param config the rocket configuration from which to calculate the caliber
* @return the unit group
*/
public static StabilityUnitGroup secondaryStabilityUnits(FlightConfiguration config) {
return new StabilityUnitGroup(UnitGroup.UNITS_SECONDARY_STABILITY, config);
}
@ -479,7 +512,17 @@ public class UnitGroup {
* @return the unit group
*/
public static UnitGroup stabilityUnits(double reference) {
return new StabilityUnitGroup(reference);
return new StabilityUnitGroup(UnitGroup.UNITS_STABILITY, reference);
}
/**
* Return a UnitGroup for secondary stability units based on a constant caliber.
*
* @param reference the constant reference length
* @return the unit group
*/
public static UnitGroup secondaryStabilityUnits(double reference) {
return new StabilityUnitGroup(UnitGroup.UNITS_SECONDARY_STABILITY, reference);
}
@ -717,19 +760,27 @@ public class UnitGroup {
* A private class that switches the CaliberUnit to a rocket-specific CaliberUnit.
* All other methods are passed through to UNITS_STABILITY.
*/
private static class StabilityUnitGroup extends UnitGroup {
public static class StabilityUnitGroup extends UnitGroup {
private final PercentageOfLengthUnit percentageOfLengthUnit;
private final UnitGroup stabilityUnit;
public StabilityUnitGroup(double ref) { this(new CaliberUnit(ref), new PercentageOfLengthUnit(ref)); }
public StabilityUnitGroup(Rocket rocket) {
this(new CaliberUnit(rocket), new PercentageOfLengthUnit(rocket));
public StabilityUnitGroup(UnitGroup stabilityUnit, double ref) {
this(stabilityUnit, new CaliberUnit(ref), new PercentageOfLengthUnit(ref));
}
public StabilityUnitGroup(FlightConfiguration config) { this(new CaliberUnit(config), new PercentageOfLengthUnit(config)); }
public StabilityUnitGroup(UnitGroup stabilityUnit, Rocket rocket) {
this(stabilityUnit, new CaliberUnit(rocket), new PercentageOfLengthUnit(rocket));
}
private StabilityUnitGroup(CaliberUnit caliberUnit, PercentageOfLengthUnit percentageOfLengthUnit) {
this.units.addAll(UnitGroup.UNITS_STABILITY.units);
this.defaultUnit = UnitGroup.UNITS_STABILITY.defaultUnit;
public StabilityUnitGroup(UnitGroup stabilityUnit, FlightConfiguration config) {
this(stabilityUnit, new CaliberUnit(config), new PercentageOfLengthUnit(config));
}
private StabilityUnitGroup(UnitGroup stabilityUnit, CaliberUnit caliberUnit, PercentageOfLengthUnit percentageOfLengthUnit) {
this.percentageOfLengthUnit = percentageOfLengthUnit;
this.stabilityUnit = stabilityUnit;
this.units.addAll(stabilityUnit.units);
this.defaultUnit = stabilityUnit.defaultUnit;
for (int i = 0; i < units.size(); i++) {
if (units.get(i) instanceof CaliberUnit) {
units.set(i, caliberUnit);
@ -744,7 +795,15 @@ public class UnitGroup {
@Override
public void setDefaultUnit(int n) {
super.setDefaultUnit(n);
UNITS_STABILITY.setDefaultUnit(n);
this.stabilityUnit.setDefaultUnit(n);
}
/**
* Returns the percentage of length unit. (Stability in %)
* @return the percentage of length unit.
*/
public Unit getPercentageOfLengthUnit() {
return this.percentageOfLengthUnit;
}
}
}

View File

@ -65,7 +65,7 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals(" Alpha III Empty Mass is incorrect: ", expRocketDryMass, actualRocketDryMass, EPSILON);
double expCMx = 0.1917685523;
double expCMy = -0.00006340812673; // Slight offset due to launch lug
double expCMy = -0.000317040634; // Slight offset due to launch lug
Coordinate expCM = new Coordinate(expCMx, expCMy, 0, expRocketDryMass);
assertEquals("Simple Rocket CM.x is incorrect: ", expCM.x, actualRocketDryCM.x, EPSILON);
assertEquals("Simple Rocket CM.y is incorrect: ", expCM.y, actualRocketDryCM.y, EPSILON);
@ -73,7 +73,7 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals("Simple Rocket CM is incorrect: ", expCM, actualRocketDryCM);
double expMOIrot = 1.8763734635622462E-5;
double expMOIrot = 1.888136072268211E-5;
double expMOIlong = 1.7808603404853048E-4;
double actualMOIrot = actualStructure.getRotationalInertia();
@ -118,7 +118,7 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals(" Alpha III Total Mass (with motor: " + desig + ") is incorrect: ", expRocketLaunchMass, actualRocketLaunchMass, EPSILON);
double expCMx = 0.20996455968266833;
double expCMy = -0.00003845163503; // Slight offset due to launch lug
double expCMy = -0.00019225817513303; // Slight offset due to launch lug
Coordinate expCM = new Coordinate(expCMx, expCMy, 0, expRocketLaunchMass);
assertEquals("Simple Rocket CM.x is incorrect: ", expCM.x, actualRocketLaunchCM.x, EPSILON);
assertEquals("Simple Rocket CM.y is incorrect: ", expCM.y, actualRocketLaunchCM.y, EPSILON);
@ -1269,5 +1269,37 @@ public class MassCalculatorTest extends BaseTestCase {
assertEquals(0.02, bodyTube.getMass(), EPSILON);
assertEquals(0.02, bodyTube.getSectionMass(), EPSILON);
}
@Test
public void testTubeFinMass() {
Rocket rocket = OpenRocketDocumentFactory.createNewRocket().getRocket();
AxialStage stage = rocket.getStage(0);
BodyTube bodyTube = new BodyTube();
stage.addChild(bodyTube);
TubeFinSet tubeFinSet = new TubeFinSet();
tubeFinSet.setOuterRadius(0.04);
tubeFinSet.setThickness(0.002);
tubeFinSet.setLength(0.1);
tubeFinSet.setInstanceCount(3);
bodyTube.addChild(tubeFinSet);
assertEquals(0.0001470265, tubeFinSet.getComponentVolume(), EPSILON);
assertEquals(0.0999780446, tubeFinSet.getComponentMass(), EPSILON);
assertEquals(0.0999780446, tubeFinSet.getMass(), EPSILON);
tubeFinSet.setInstanceCount(4);
assertEquals(0.000196035, tubeFinSet.getComponentVolume(), EPSILON);
assertEquals(0.133304059, tubeFinSet.getComponentMass(), EPSILON);
assertEquals(0.133304059, tubeFinSet.getMass(), EPSILON);
tubeFinSet.setMassOverridden(true);
tubeFinSet.setOverrideMass(0.02);
assertEquals(0.133304059, tubeFinSet.getComponentMass(), EPSILON);
assertEquals(0.02, tubeFinSet.getMass(), EPSILON);
}
}

View File

@ -39,7 +39,7 @@ public class LaunchLugTest extends BaseTestCase {
public void testLaunchLugLocationAtAngles() {
Rocket rocket = TestRockets.makeEstesAlphaIII();
BodyTube body= (BodyTube)rocket.getChild(0).getChild(1);
BodyTube body = (BodyTube)rocket.getChild(0).getChild(1);
LaunchLug lug = (LaunchLug)rocket.getChild(0).getChild(1).getChild(1);
double startAngle = Math.PI/2;
lug.setAngleOffset( startAngle );
@ -67,6 +67,7 @@ public class LaunchLugTest extends BaseTestCase {
@Test
public void testCMSingleInstance() {
BodyTube bodyTube = new BodyTube();
bodyTube.setOuterRadius(0.025);
LaunchLug lug = new LaunchLug();
lug.setLength(0.1);
lug.setOuterRadius(0.02);
@ -75,7 +76,7 @@ public class LaunchLugTest extends BaseTestCase {
// Test normal CG
Coordinate CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.05, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", -0.02, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", -0.045, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON);
@ -84,14 +85,14 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.05, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.02, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.045, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON);
lug.setAngleOffset(-Math.PI / 3);
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.05, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.01, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.0173205, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.0225, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.03897114, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON);
@ -102,7 +103,7 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.025, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.015, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.04, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.00309761, CG.weight, EPSILON);
@ -111,20 +112,21 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.025, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.015, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.04, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.00309761, CG.weight, EPSILON);
lug.setAngleOffset(-Math.PI / 3);
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.025, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.0075, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.01299038, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.02, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.034641016, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.00309761, CG.weight, EPSILON);
}
@Test
public void testCMSingleInstanceOverride() {
BodyTube bodyTube = new BodyTube();
bodyTube.setOuterRadius(0.025);
LaunchLug lug = new LaunchLug();
lug.setLength(0.1);
lug.setOuterRadius(0.02);
@ -135,7 +137,7 @@ public class LaunchLugTest extends BaseTestCase {
// Test normal CG
Coordinate CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", -0.02, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", -0.045, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON);
@ -144,14 +146,14 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.02, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.045, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON);
lug.setAngleOffset(-Math.PI / 3);
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.01, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.0173205, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.0225, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.03897114, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.008331504, CG.weight, EPSILON);
@ -165,7 +167,7 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.015, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.04, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.1, CG.weight, EPSILON);
@ -174,20 +176,21 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.015, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.04, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.1, CG.weight, EPSILON);
lug.setAngleOffset(-Math.PI / 3);
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.0075, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.01299038, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.02, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.034641016, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.1, CG.weight, EPSILON);
}
@Test
public void testCMMultipleInstances() {
BodyTube bodyTube = new BodyTube();
bodyTube.setOuterRadius(0.025);
LaunchLug lug = new LaunchLug();
lug.setLength(0.1);
lug.setOuterRadius(0.02);
@ -198,7 +201,7 @@ public class LaunchLugTest extends BaseTestCase {
// Test normal CG
Coordinate CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.25, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", -0.02, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", -0.045, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON);
@ -207,14 +210,14 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.25, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.02, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.045, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON);
lug.setAngleOffset(-Math.PI / 3);
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.25, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.01, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.0173205, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.0225, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.03897114, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON);
@ -227,7 +230,7 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.1, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.015, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.04, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.00619522, CG.weight, EPSILON);
@ -236,20 +239,21 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.1, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.015, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.04, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.00619522, CG.weight, EPSILON);
lug.setAngleOffset(-Math.PI / 3);
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.1, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.0075, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.01299038, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.02, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.034641016, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.00619522, CG.weight, EPSILON);
}
@Test
public void testCMMultipleInstancesOverride() {
BodyTube bodyTube = new BodyTube();
bodyTube.setOuterRadius(0.025);
LaunchLug lug = new LaunchLug();
lug.setLength(0.1);
lug.setOuterRadius(0.02);
@ -262,7 +266,7 @@ public class LaunchLugTest extends BaseTestCase {
// Test normal CG
Coordinate CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", -0.02, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", -0.045, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON);
@ -271,14 +275,14 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.02, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.045, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON);
lug.setAngleOffset(-Math.PI / 3);
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.01, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.0173205, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.0225, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.03897114, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.024994512, CG.weight, EPSILON);
@ -294,7 +298,7 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.015, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.04, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.2, CG.weight, EPSILON);
@ -303,14 +307,14 @@ public class LaunchLugTest extends BaseTestCase {
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.015, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", 0.04, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.2, CG.weight, EPSILON);
lug.setAngleOffset(-Math.PI / 3);
CG = lug.getCG();
assertEquals(" LaunchLug CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.0075, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.01299038, CG.z, EPSILON);
assertEquals(" LaunchLug CG has the wrong y value: ", 0.02, CG.y, EPSILON);
assertEquals(" LaunchLug CG has the wrong z value: ", -0.034641016, CG.z, EPSILON);
assertEquals(" LaunchLug CM has the wrong value: ", 0.2, CG.weight, EPSILON);
}

View File

@ -13,6 +13,7 @@ public class RailButtonTest extends BaseTestCase {
@Test
public void testCMSingleInstance() {
BodyTube bodyTube = new BodyTube();
bodyTube.setOuterRadius(0.025);
RailButton button = new RailButton();
button.setOuterDiameter(0.05);
button.setTotalHeight(0.05);
@ -21,7 +22,7 @@ public class RailButtonTest extends BaseTestCase {
// Test normal CG
Coordinate CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", -0.025, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", -0.05, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON);
@ -30,14 +31,14 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.025, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.05, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON);
button.setAngleOffset(-Math.PI / 3);
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.0125, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.02165064, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.025, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.04330127, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON);
@ -48,7 +49,7 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.01, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.035, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.003930195, CG.weight, EPSILON);
@ -57,20 +58,21 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.01, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.035, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.003930195, CG.weight, EPSILON);
button.setAngleOffset(-Math.PI / 3);
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.005, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.00866025, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.0175, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.03031089, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.003930195, CG.weight, EPSILON);
}
@Test
public void testCMSingleInstanceOverride() {
BodyTube bodyTube = new BodyTube();
bodyTube.setOuterRadius(0.025);
RailButton button = new RailButton();
button.setOuterDiameter(0.05);
button.setTotalHeight(0.05);
@ -81,7 +83,7 @@ public class RailButtonTest extends BaseTestCase {
// Test normal CG
Coordinate CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", -0.025, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", -0.05, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON);
@ -90,14 +92,14 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.025, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.05, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON);
button.setAngleOffset(-Math.PI / 3);
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.0125, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.02165064, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.025, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.04330127, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.014435995, CG.weight, EPSILON);
@ -111,7 +113,7 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.01, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.035, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.1, CG.weight, EPSILON);
@ -120,20 +122,21 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.01, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.035, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.1, CG.weight, EPSILON);
button.setAngleOffset(-Math.PI / 3);
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.005, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.00866025, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.0175, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.03031089, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.1, CG.weight, EPSILON);
}
@Test
public void testCMMultipleInstances() {
BodyTube bodyTube = new BodyTube();
bodyTube.setOuterRadius(0.025);
RailButton button = new RailButton();
button.setOuterDiameter(0.05);
button.setTotalHeight(0.05);
@ -144,7 +147,7 @@ public class RailButtonTest extends BaseTestCase {
// Test normal CG
Coordinate CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.2, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", -0.025, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", -0.05, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON);
@ -153,14 +156,14 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.2, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.025, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.05, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON);
button.setAngleOffset(-Math.PI / 3);
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.2, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.0125, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.02165064, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.025, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.04330127, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON);
@ -173,7 +176,7 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.075, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.01, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.035, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.00786039, CG.weight, EPSILON);
@ -182,20 +185,21 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.075, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.01, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.035, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.00786039, CG.weight, EPSILON);
button.setAngleOffset(-Math.PI / 3);
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.075, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.005, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.00866025, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.0175, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.03031089, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.00786039, CG.weight, EPSILON);
}
@Test
public void testCMMultipleInstancesOverride() {
BodyTube bodyTube = new BodyTube();
bodyTube.setOuterRadius(0.025);
RailButton button = new RailButton();
button.setOuterDiameter(0.05);
button.setTotalHeight(0.05);
@ -208,7 +212,7 @@ public class RailButtonTest extends BaseTestCase {
// Test normal CG
Coordinate CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", -0.025, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", -0.05, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON);
@ -217,14 +221,14 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.025, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.05, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON);
button.setAngleOffset(-Math.PI / 3);
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0123, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.0125, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.02165064, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.025, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.04330127, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.043307985, CG.weight, EPSILON);
@ -240,7 +244,7 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.01, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.035, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.2, CG.weight, EPSILON);
@ -249,14 +253,14 @@ public class RailButtonTest extends BaseTestCase {
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.01, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", 0.035, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.2, CG.weight, EPSILON);
button.setAngleOffset(-Math.PI / 3);
CG = button.getCG();
assertEquals(" RailButton CG has the wrong x value: ", 0.0321, CG.x, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.005, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.00866025, CG.z, EPSILON);
assertEquals(" RailButton CG has the wrong y value: ", 0.0175, CG.y, EPSILON);
assertEquals(" RailButton CG has the wrong z value: ", -0.03031089, CG.z, EPSILON);
assertEquals(" RailButton CM has the wrong value: ", 0.2, CG.weight, EPSILON);
}

View File

@ -52,6 +52,32 @@ public class TrapezoidFinSetTest extends BaseTestCase {
return rkt;
}
private Rocket createFreeformFinOnTransition() {
final Rocket rkt = new Rocket();
final AxialStage stg = new AxialStage();
rkt.addChild(stg);
Transition transition = new Transition();
transition.setLength(0.2);
transition.setForeRadius(0.1);
transition.setAftRadius(0.3);
transition.setShapeType(Transition.Shape.OGIVE);
stg.addChild(transition);
FreeformFinSet fins = new FreeformFinSet();
fins.setFinCount(1);
fins.setAxialOffset(AxialMethod.MIDDLE, 0.0);
fins.setMaterial(Material.newMaterial(Material.Type.BULK, "Fin-Test-Material", 1.0, true));
fins.setThickness(0.005); // == 5 mm
transition.addChild(fins);
fins.setTabLength(0.00);
fins.setFilletRadius(0.0);
rkt.enableEvents();
return rkt;
}
@Test
public void testMultiplicity() {
final TrapezoidFinSet trapFins = new TrapezoidFinSet();
@ -187,6 +213,33 @@ public class TrapezoidFinSetTest extends BaseTestCase {
}
}
@Test
public void testFilletCalculationsOnTransition() {
final Rocket rkt = createFreeformFinOnTransition();
Transition transition = (Transition) rkt.getChild(0).getChild(0);
FinSet fins = (FinSet) rkt.getChild(0).getChild(0).getChild(0);
fins.setFilletRadius(0.005);
fins.setFilletMaterial(Material.newMaterial(Material.Type.BULK, "Fillet-Test-Material", 1.0, true));
// used for fillet and edge calculations:
//
// [1] +--+ [2]
// / \
// / \
// [0] +--------+ [3]
//
assertEquals(0.05, fins.getLength(), EPSILON);
assertEquals("Transition fore radius doesn't match: ", 0.1, transition.getForeRadius(), EPSILON);
assertEquals("Transition aft radius doesn't match: ", 0.3, transition.getAftRadius(), EPSILON);
final Coordinate actVolume = fins.calculateFilletVolumeCentroid();
assertEquals("Fin volume doesn't match: ", 5.973e-07, actVolume.weight, EPSILON);
assertEquals("Fin mass center.x doesn't match: ", 0.024393025, actVolume.x, EPSILON);
assertEquals("Fin mass center.y doesn't match: ", 0.190479957, actVolume.y, EPSILON);
}
@Test
public void testTrapezoidCGComputation() {
{

View File

@ -20,4 +20,3 @@ media/
.DS_Store
code_signing/
openrocket-22.xx.install4j~

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<install4j version="10.0.4" transformSequenceNumber="10">
<directoryPresets config="../../core/resources/pix/icon" />
<application name="OpenRocket" applicationId="8434-9327-1469-6373" mediaDir="media" mediaFilePattern="${compiler:sys.shortName}_${compiler:sys.version}_${compiler:sys.platform}" shortName="OpenRocket" publisher="OpenRocket" publisherWeb="http://openrocket.info" version="22.02" allPathsRelative="true" convertDotsToUnderscores="false" macVolumeId="5f58a2be20d8e22f" javaMinVersion="11" javaMaxVersion="11" jdkMode="jdk" jdkName="JDK 11.0">
<application name="OpenRocket" applicationId="8434-9327-1469-6373" mediaDir="media" mediaFilePattern="${compiler:sys.shortName}_${compiler:sys.version}_${compiler:sys.platform}" shortName="OpenRocket" publisher="OpenRocket" publisherWeb="http://openrocket.info" version="23.xx" allPathsRelative="true" convertDotsToUnderscores="false" macVolumeId="5f58a2be20d8e22f" javaMinVersion="17" javaMaxVersion="17" jdkMode="jdk" jdkName="JDK 11.0">
<codeSigning macEnabled="true" macPkcs12File="./code_signing/OpenRocket_macOS.p12" windowsEnabled="true" windowsPkcs12File="./code_signing/OpenRocket_Windows.pfx" macNotarize="true" appleId="sibo.vangool@hotmail.com" />
<jreBundles jdkProviderId="Liberica" release="11/11.0.17+7">
<jreBundles jdkProviderId="Liberica" release="17/17.0.7+7">
<modules>
<defaultModules set="jre" />
<module name="java.scripting" />
@ -19,7 +19,7 @@
</entries>
</files>
<launchers>
<launcher name="OpenRocket 22.02" id="59" icnsFile="../../core/resources/pix/icon/icon-macos.icns">
<launcher name="OpenRocket 23.xx" id="59" icnsFile="../../core/resources/pix/icon/icon-macos.icns">
<executable name="OpenRocket" iconSet="true" iconFile="../../core/resources/pix/icon/icon-windows.ico" executableDir="." executableMode="gui">
<versionInfo include="true" fileDescription="A model rocket flight-trajectory simulator." legalCopyright="Copyright 2007-2023 Sampo Niskanen and Others" internalName="${compiler:sys.shortName} ${compiler:sys.version}" />
</executable>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -18,6 +18,9 @@ import java.io.FileOutputStream;
import javax.swing.JTextPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
@ -188,6 +191,7 @@ public class DescriptionArea extends JScrollPane {
}
});
setForeground(editorPane.getForeground());
editorPane.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
@ -202,5 +206,23 @@ public class DescriptionArea extends JScrollPane {
editorPane.setFont(font);
}
}
public void setBackground(Color color) {
if (editorPane == null) return;
editorPane.setBackground(color);
StyledDocument styledDocument = (StyledDocument) editorPane.getDocument();
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setForeground(attributeSet, color);
styledDocument.setCharacterAttributes(0, styledDocument.getLength(), attributeSet, false);
}
public void setForeground(Color color) {
if (editorPane == null) return;
editorPane.setForeground(color);
StyledDocument styledDocument = (StyledDocument) editorPane.getDocument();
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setForeground(attributeSet, color);
styledDocument.setCharacterAttributes(0, styledDocument.getLength(), attributeSet, false);
}
}

View File

@ -3,6 +3,8 @@ package net.sf.openrocket.gui.dialogs.preferences;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@ -40,7 +42,6 @@ import net.sf.openrocket.gui.util.PreferencesExporter;
import net.sf.openrocket.gui.util.PreferencesImporter;
import net.sf.openrocket.l10n.L10N;
import net.sf.openrocket.logging.Markers;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.util.BuildProperties;
import net.sf.openrocket.util.Named;
@ -255,6 +256,17 @@ public class GeneralPreferencesPanel extends PreferencesPanel {
});
this.add(rocksimWarningDialogBox,"spanx, wrap");
//// Show confirmation dialog when discarding preferences
final JCheckBox prefsDiscardBox = new JCheckBox(trans.get("pref.dlg.checkbox.ShowDiscardPreferencesConfirmation"));
prefsDiscardBox.setSelected(preferences.isShowDiscardPreferencesConfirmation());
prefsDiscardBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
preferences.setShowDiscardPreferencesConfirmation(e.getStateChange() == ItemEvent.SELECTED);
}
});
this.add(prefsDiscardBox,"spanx, wrap");
// Preference buttons
JPanel buttonPanel = new JPanel(new MigLayout("fillx, ins 0"));
@ -315,7 +327,7 @@ public class GeneralPreferencesPanel extends PreferencesPanel {
}
}
});
buttonPanel.add(resetAllPreferences, "pushx, right, wrap");
buttonPanel.add(resetAllPreferences, "pushx, right, gaptop 20lp, wrap");
this.add(buttonPanel, "spanx, growx, pushy, bottom, wrap");
}

View File

@ -1,21 +1,31 @@
package net.sf.openrocket.gui.dialogs.preferences;
import java.awt.Dialog;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.prefs.BackingStoreException;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.main.BasicFrame;
import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.PreferencesExporter;
import net.sf.openrocket.gui.util.PreferencesImporter;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.startup.Application;
@ -35,6 +45,9 @@ public class PreferencesDialog extends JDialog {
private BasicFrame parentFrame;
private boolean storePreferences = true;
private File initPrefsFile = null;
private PreferencesDialog(BasicFrame parent) {
// // Preferences
super(parent, trans.get("pref.dlg.title.Preferences"),
@ -42,6 +55,9 @@ public class PreferencesDialog extends JDialog {
this.parentFrame = parent;
// First store the initial preferences
initPrefsFile = storeInitPreferences();
JPanel panel = new JPanel(new MigLayout("fill, gap unrel", "[grow]",
"[grow][]"));
@ -78,16 +94,42 @@ public class PreferencesDialog extends JDialog {
// tabbedPane.addTab(trans.get("pref.dlg.tab.Colors"),
// new DisplayPreferencesPanel());
// Close button
JButton close = new SelectColorButton(trans.get("dlg.but.close"));
close.addActionListener(new ActionListener() {
//// Cancel button
JButton cancelButton = new SelectColorButton(trans.get("dlg.but.cancel"));
cancelButton.setToolTipText(trans.get("SimulationEditDialog.btn.Cancel.ttip"));
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
PreferencesDialog.this.setVisible(false);
PreferencesDialog.this.dispose();
public void actionPerformed(ActionEvent e) {
// Apply the cancel operation if set to auto discard in preferences
if (!preferences.isShowDiscardPreferencesConfirmation()) {
closeDialog(false);
return;
}
// Yes/No dialog: Are you sure you want to discard your changes?
JPanel msg = createCancelOperationContent();
int resultYesNo = JOptionPane.showConfirmDialog(PreferencesDialog.this, msg,
trans.get("PreferencesDialog.CancelOperation.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (resultYesNo == JOptionPane.YES_OPTION) {
closeDialog(false);
}
}
});
panel.add(close, "span, right, tag close");
panel.add(cancelButton, "span, split 2, right, tag cancel");
//// Ok button
JButton okButton = new SelectColorButton(trans.get("dlg.but.ok"));
okButton.setToolTipText(trans.get("SimulationEditDialog.btn.OK.ttip"));
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
closeDialog(true);
}
});
panel.add(okButton, "tag ok");
this.setContentPane(panel);
pack();
@ -96,7 +138,22 @@ public class PreferencesDialog extends JDialog {
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
preferences.storeDefaultUnits();
// We don't want to lose the preference for the confirmation dialog
boolean isShowDiscardConfirmation = preferences.isShowDiscardPreferencesConfirmation();
// Reload initial preferences
if (!storePreferences) {
loadInitPreferences();
}
// Store the preference for showing the confirmation dialog
preferences.setShowDiscardPreferencesConfirmation(isShowDiscardConfirmation);
// Delete the init prefs
if (initPrefsFile != null) {
initPrefsFile.delete();
}
// Make sure unit change applies to the rocket figure
if (parent != null) {
parent.getRocketPanel().updateExtras();
@ -106,13 +163,72 @@ public class PreferencesDialog extends JDialog {
}
});
GUIUtil.setDisposableDialogOptions(this, close);
GUIUtil.setDisposableDialogOptions(this, okButton);
}
public BasicFrame getParentFrame() {
return parentFrame;
}
private void closeDialog(boolean storeChanges) {
storePreferences = storeChanges;
PreferencesDialog.this.setVisible(false);
PreferencesDialog.this.dispose();
}
/**
* Store the intial preferences in a temporary file, and return that file.
* @return the file containing the initial preferences, or null if something went wrong
*/
private File storeInitPreferences() {
try {
File outputFile = Files.createTempFile("ORInitPrefs_" + System.currentTimeMillis(), ".xml").toFile();
try (FileOutputStream outputFos = new FileOutputStream(outputFile)) {
PreferencesExporter.exportPreferencesToFile(preferences.getPreferences(), outputFos, false);
log.debug("Initial preferences stored in temporary file: " + outputFile.getAbsolutePath());
} catch (BackingStoreException e) {
log.error("Could not store initial preferences", e);
return null;
}
return outputFile;
} catch (IOException e) {
log.error("Could not create temporary preferences file", e);
return null;
}
}
/**
* Loads the initial stored preferences back (restores preferences).
*/
private void loadInitPreferences() {
if (initPrefsFile == null) {
return;
}
PreferencesImporter.importPreferences(initPrefsFile);
}
private JPanel createCancelOperationContent() {
JPanel panel = new JPanel(new MigLayout());
String msg = trans.get("PreferencesDialog.CancelOperation.msg.discardChanges");
JLabel msgLabel = new JLabel(msg);
JCheckBox dontAskAgain = new JCheckBox(trans.get("SimulationEditDialog.CancelOperation.checkbox.dontAskAgain"));
dontAskAgain.setSelected(false);
dontAskAgain.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
preferences.setShowDiscardPreferencesConfirmation(false);
}
// Unselected state should be not be possible and thus not be handled
}
});
panel.add(msgLabel, "left, wrap");
panel.add(dontAskAgain, "left, gaptop para");
return panel;
}
// ////// Singleton implementation ////////
private static PreferencesDialog dialog = null;

View File

@ -3,11 +3,15 @@ package net.sf.openrocket.gui.dialogs.preferences;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.components.StyledLabel;
@ -18,122 +22,146 @@ import net.sf.openrocket.gui.widgets.SelectColorButton;
public class UnitsPreferencesPanel extends PreferencesPanel {
public UnitsPreferencesPanel(JDialog parent) {
super(parent, new MigLayout("", "[][]40lp[][]"));
super(parent, new MigLayout("", "[]40lp[]"));
JComboBox<?> combo;
JPanel leftPanel = new JPanel(new MigLayout("ins 0"));
JPanel rightPanel = new JPanel(new MigLayout("ins 0"));
//// Select your preferred units:
this.add(new JLabel(trans.get("pref.dlg.lbl.Selectprefunits")), "span, wrap paragraph");
// -------------- LEFT PANEL
//// Rocket dimensions:
this.add(new JLabel(trans.get("pref.dlg.lbl.Rocketdimensions")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Rocketdimensions")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_LENGTH));
this.add(combo, "sizegroup boxes");
//// Line density:
this.add(new JLabel(trans.get("pref.dlg.lbl.Linedensity")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Motor dimensions:
this.add(new JLabel(trans.get("pref.dlg.lbl.Motordimensions")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Motordimensions")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_MOTOR_DIMENSIONS));
this.add(combo, "sizegroup boxes");
//// Surface density:
this.add(new JLabel(trans.get("pref.dlg.lbl.Surfacedensity")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Distance:
this.add(new JLabel(trans.get("pref.dlg.lbl.Distance")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Distance")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_DISTANCE));
this.add(combo, "sizegroup boxes");
//// Bulk density::
this.add(new JLabel(trans.get("pref.dlg.lbl.Bulkdensity")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Velocity:
this.add(new JLabel(trans.get("pref.dlg.lbl.Velocity")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Velocity")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_VELOCITY));
this.add(combo, "sizegroup boxes");
//// Surface roughness:
this.add(new JLabel(trans.get("pref.dlg.lbl.Surfaceroughness")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Acceleration:
this.add(new JLabel(trans.get("pref.dlg.lbl.Acceleration")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Acceleration")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_ACCELERATION));
this.add(combo, "sizegroup boxes");
//// Area:
this.add(new JLabel(trans.get("pref.dlg.lbl.Area")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_AREA));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Mass:
this.add(new JLabel(trans.get("pref.dlg.lbl.Mass")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Mass")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_MASS));
this.add(combo, "sizegroup boxes");
//// Angle:
this.add(new JLabel(trans.get("pref.dlg.lbl.Angle")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Force:
this.add(new JLabel(trans.get("pref.dlg.lbl.Force")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Force")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_FORCE));
this.add(combo, "sizegroup boxes");
//// Roll rate:
this.add(new JLabel(trans.get("pref.dlg.lbl.Rollrate")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_ROLL));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Total impulse:
this.add(new JLabel(trans.get("pref.dlg.lbl.Totalimpulse")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Totalimpulse")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_IMPULSE));
this.add(combo, "sizegroup boxes");
//// Temperature:
this.add(new JLabel(trans.get("pref.dlg.lbl.Temperature")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Moment of inertia:
this.add(new JLabel(trans.get("pref.dlg.lbl.Momentofinertia")));
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Momentofinertia")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_INERTIA));
this.add(combo, "sizegroup boxes");
//// Pressure:
this.add(new JLabel(trans.get("pref.dlg.lbl.Pressure")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
this.add(combo, "sizegroup boxes, wrap");
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Stability:
this.add(new JLabel(trans.get("pref.dlg.lbl.Stability")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
this.add(combo, "sizegroup boxes");
leftPanel.add(new JLabel(trans.get("pref.dlg.lbl.Stability")));
combo = new JComboBox<>(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
leftPanel.add(combo, "sizegroup boxes, wrap");
//// Secondary stability:
final JLabel labelSecStab = new JLabel(trans.get("pref.dlg.lbl.SecondaryStability"));
labelSecStab.setToolTipText(trans.get("pref.dlg.lbl.SecondaryStability.ttip"));
labelSecStab.setEnabled(preferences.isDisplaySecondaryStability());
leftPanel.add(labelSecStab);
final JComboBox<?> comboSecStab = new JComboBox<>(new DefaultUnitSelector(UnitGroup.UNITS_SECONDARY_STABILITY));
comboSecStab.setToolTipText(trans.get("pref.dlg.lbl.SecondaryStability.ttip"));
comboSecStab.setEnabled(preferences.isDisplaySecondaryStability());
leftPanel.add(comboSecStab, "sizegroup boxes, wrap");
//// Display secondary stability unit:
JCheckBox displaySecondary = new JCheckBox(trans.get("pref.dlg.checkbox.DisplaySecondaryStability"));
displaySecondary.setToolTipText(trans.get("pref.dlg.checkbox.DisplaySecondaryStability.ttip"));
displaySecondary.setSelected(preferences.isDisplaySecondaryStability());
displaySecondary.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
preferences.setDisplaySecondaryStability(e.getStateChange() == ItemEvent.SELECTED);
labelSecStab.setEnabled(e.getStateChange() == ItemEvent.SELECTED);
comboSecStab.setEnabled(e.getStateChange() == ItemEvent.SELECTED);
}
});
leftPanel.add(displaySecondary, "spanx, wrap");
// -------------- RIGHT PANEL
//// Line density:
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Linedensity")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Surface density:
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Surfacedensity")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Bulk density::
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Bulkdensity")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Surface roughness:
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Surfaceroughness")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Area:
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Area")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_AREA));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Angle:
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Angle")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Roll rate:
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Rollrate")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_ROLL));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Temperature:
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Temperature")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Pressure:
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Pressure")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
rightPanel.add(combo, "sizegroup boxes, wrap");
//// Windspeed:
this.add(new JLabel(trans.get("pref.dlg.lbl.Windspeed")));
rightPanel.add(new JLabel(trans.get("pref.dlg.lbl.Windspeed")));
combo = new JComboBox<Object>(new DefaultUnitSelector(UnitGroup.UNITS_WINDSPEED));
this.add(combo, "sizegroup boxes, wrap para");
rightPanel.add(combo, "sizegroup boxes, wrap");
this.add(leftPanel, "top");
this.add(rightPanel, "top, wrap para");
//// Default metric button
@ -165,7 +193,7 @@ public class UnitsPreferencesPanel extends PreferencesPanel {
trans.get("pref.dlg.lbl.effect1"), -2, Style.ITALIC),
"spanx, wrap");
}
}
public UnitsPreferencesPanel(LayoutManager layout) {
super(layout);

View File

@ -11,6 +11,7 @@ import java.awt.Rectangle;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.logging.Warning;
import net.sf.openrocket.logging.WarningSet;
import net.sf.openrocket.l10n.Translator;
@ -31,6 +32,7 @@ import net.sf.openrocket.util.MathUtil;
public class RocketInfo implements FigureElement {
private static final Translator trans = Application.getTranslator();
private static final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
// Margin around the figure edges, pixels
private static final int MARGIN = 8;
@ -41,7 +43,8 @@ public class RocketInfo implements FigureElement {
private final Caret cpCaret = new CPCaret(0,0);
private final Caret cgCaret = new CGCaret(0,0);
private UnitGroup stabilityUnits;
private UnitGroup.StabilityUnitGroup stabilityUnits;
private UnitGroup.StabilityUnitGroup secondaryStabilityUnits;
private FlightConfiguration configuration;
private double cg = 0, cp = 0;
@ -66,6 +69,7 @@ public class RocketInfo implements FigureElement {
public RocketInfo(FlightConfiguration configuration) {
this.configuration = configuration;
this.stabilityUnits = UnitGroup.stabilityUnits(configuration);
this.secondaryStabilityUnits = UnitGroup.secondaryStabilityUnits(configuration);
}
@ -189,19 +193,9 @@ public class RocketInfo implements FigureElement {
private void drawStabilityInfo() {
String at;
//// at M=
at = trans.get("RocketInfo.at")+UnitGroup.UNITS_COEFFICIENT.getDefaultUnit().toStringUnit(this.mach);
if (!Double.isNaN(aoa)) {
at += " "+ALPHA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(aoa);
}
if (!Double.isNaN(theta)) {
at += " "+THETA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta);
}
GlyphVector cgValue = createText(getCg());
GlyphVector cpValue = createText(getCp());
GlyphVector stabValue = createText(getStability());
GlyphVector stabValue = createText(getStabilityCombined());
//// CG:
GlyphVector cgText = createText(trans.get("RocketInfo.cgText"));
@ -209,6 +203,15 @@ public class RocketInfo implements FigureElement {
GlyphVector cpText = createText(trans.get("RocketInfo.cpText"));
//// Stability:
GlyphVector stabText = createText(trans.get("RocketInfo.stabText"));
//// at M=...
String at = trans.get("RocketInfo.at")+UnitGroup.UNITS_COEFFICIENT.getDefaultUnit().toStringUnit(this.mach);
if (!Double.isNaN(aoa)) {
at += " "+ALPHA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(aoa);
}
if (!Double.isNaN(theta)) {
at += " "+THETA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta);
}
GlyphVector atText = createSmallText(at);
// GlyphVector visual bounds drops the spaces, so we'll add them
@ -276,16 +279,52 @@ public class RocketInfo implements FigureElement {
public String getMassWithMotors(Unit u) {
return u.toStringUnit(massWithMotors);
}
/**
* Get the stability, in calibers.
* Get the stability in both the selected stability unit and in percentage, e.g. "2.4 cal (14.1 %)".
* If the current unit is already the percentage length unit, only use that.
*
* @return the current stability margin
* @return the current stability margin in the currently selected stability unit and in percentage
*/
public String getStability () {
return stabilityUnits.getDefaultUnit().toStringUnit(cp-cg);
public String getStabilityCombined() {
Unit stabilityUnit = stabilityUnits.getDefaultUnit();
Unit secondaryStabilityUnit = secondaryStabilityUnits.getDefaultUnit();
String stabilityStr = getStability();
// Don't display secondary units if the stability is NaN, or if the secondary unit is the same as the primary unit,
// or if it is disabled in the preferences
if (Double.isNaN(getStabilityValue()) || secondaryStabilityUnit.equals(stabilityUnit) ||
!preferences.isDisplaySecondaryStability()) {
return stabilityStr;
}
String secondaryStabilityStr = getSecondaryStability();
return stabilityStr + " / " + secondaryStabilityStr;
}
/**
* Get the stability in the currently selected unit.
* @return the current stability margin in the currently selected stability unit
*/
private String getStability() {
return stabilityUnits.getDefaultUnit().toStringUnit(getStabilityValue());
}
/**
* Get the stability in the secondary stability unit.
* @return the current stability margin in the secondary stability unit
*/
private String getSecondaryStability() {
return secondaryStabilityUnits.getDefaultUnit().toStringUnit(getStabilityValue());
}
private double getStabilityValue() {
return cp - cg;
}
/**
* Get the center of pressure in default length units.
*
@ -478,5 +517,6 @@ public class RocketInfo implements FigureElement {
public void setCurrentConfig(FlightConfiguration newConfig) {
this.configuration = newConfig;
this.stabilityUnits = UnitGroup.stabilityUnits(newConfig);
this.secondaryStabilityUnits = UnitGroup.secondaryStabilityUnits(newConfig);
}
}

View File

@ -1259,7 +1259,11 @@ public class BasicFrame extends JFrame {
*/
public static BasicFrame open(File file, Window parent) {
OpenFileWorker worker = new OpenFileWorker(file);
return open(worker, file.getName(), parent, false);
BasicFrame frame = open(worker, file.getName(), parent, false);
if (frame != null) {
MRUDesignFile.getInstance().addFile(file.getAbsolutePath());
}
return frame;
}

View File

@ -28,8 +28,10 @@ import net.sf.openrocket.gui.util.Icons;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.gui.widgets.SaveFileChooser;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.simulation.FlightDataType;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.util.Color;
import net.sf.openrocket.gui.widgets.SelectColorButton;
import org.jfree.chart.ChartPanel;
@ -71,6 +73,13 @@ public class SimulationPlotDialog extends JDialog {
//// Description text
JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2);
panel.add(label, "wrap");
// Add warning if X axis type is not time
if (config.getDomainAxisType() != FlightDataType.TYPE_TIME) {
JLabel msg = new StyledLabel(trans.get("PlotDialog.lbl.timeSeriesWarning"), -2);
msg.setForeground(Color.DARK_RED.toAWTColor());
panel.add(msg, "wrap");
}
//// Show data points
final JCheckBox check = new JCheckBox(trans.get("PlotDialog.CheckBox.Showdatapoints"));

View File

@ -256,7 +256,7 @@ public class DesignReport {
canvas.showText(text.getMassWithMotors(UnitGroup.UNITS_MASS.getDefaultUnit()));
canvas.newlineShowText(STABILITY);
canvas.showText(text.getStability());
canvas.showText(text.getStabilityCombined());
canvas.newlineShowText(CG);
canvas.showText(text.getCg());

View File

@ -40,6 +40,7 @@ import net.sf.openrocket.simulation.FlightEvent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.util.Color;
import net.sf.openrocket.util.Utils;
import net.sf.openrocket.gui.widgets.SelectColorButton;
@ -105,7 +106,8 @@ public class SimulationPlotPanel extends JPanel {
private int modifying = 0;
private DescriptionArea simPlotPanelDesc;
public SimulationPlotPanel(final Simulation simulation) {
super(new MigLayout("fill"));
@ -170,6 +172,14 @@ public class SimulationPlotPanel extends JPanel {
if (modifying > 0)
return;
FlightDataType type = (FlightDataType) domainTypeSelector.getSelectedItem();
if (type == FlightDataType.TYPE_TIME) {
simPlotPanelDesc.setVisible(false);
simPlotPanelDesc.setText("");
}
else {
simPlotPanelDesc.setVisible(true);
simPlotPanelDesc.setText(trans.get("simplotpanel.Desc"));
}
configuration.setDomainAxisType(type);
domainUnitSelector.setUnitGroup(type.getUnitGroup());
domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
@ -193,9 +203,12 @@ public class SimulationPlotPanel extends JPanel {
this.add(domainUnitSelector, "width 40lp, gapright para");
//// The data will be plotted in time order even if the X axis type is not time.
DescriptionArea desc = new DescriptionArea(trans.get("simplotpanel.Desc"), 2, -2f);
desc.setViewportBorder(BorderFactory.createEmptyBorder());
this.add(desc, "width 1px, growx 1, wrap unrel");
simPlotPanelDesc = new DescriptionArea("", 2, -2f, false);
simPlotPanelDesc.setVisible(false);
simPlotPanelDesc.setForeground(Color.DARK_RED.toAWTColor());
simPlotPanelDesc.setViewportBorder(BorderFactory.createEmptyBorder());
this.add(simPlotPanelDesc, "width 1px, growx 1, wrap unrel");

View File

@ -121,25 +121,32 @@ public abstract class PreferencesExporter {
keysToIgnore.add(SwingPreferences.UPDATE_PLATFORM); // Don't export platform-specific settings
}
private static void exportFilteredPreferences(Preferences preferences, FileOutputStream fos) throws BackingStoreException, IOException {
public static void exportPreferencesToFile(Preferences preferences, FileOutputStream fos, boolean filterPreferences)
throws BackingStoreException, IOException {
// If no filtering is required, just export the preferences
if (!filterPreferences) {
preferences.exportSubtree(fos);
return;
}
// Filter out user directories
Preferences root = Preferences.userRoot();
String originalNodeName = ((SwingPreferences) prefs).getNodename();
String nodeName = originalNodeName + "-temp";
if (root.nodeExists(nodeName)) {
root.node(nodeName).removeNode();
String filteredPrefsNodeName = originalNodeName + "-filtered";
if (root.nodeExists(filteredPrefsNodeName)) {
root.node(filteredPrefsNodeName).removeNode();
}
Preferences tempPrefs = root.node(nodeName);
Preferences filteredPrefs = root.node(filteredPrefsNodeName);
// Fill in all parameters to the temporary preferences, except for user directories
copyFilteredPreferences(preferences, tempPrefs, nodesToIgnore, keysToIgnore, prefixKeysToIgnore);
copyFilteredPreferences(preferences, filteredPrefs, nodesToIgnore, keysToIgnore, prefixKeysToIgnore);
// Export the filtered preferences
try {
// Export the filtered preferences to a temporary file
Path tempFile = Files.createTempFile("ORprefs_" + System.currentTimeMillis(), ".xml");
Path tempFile = Files.createTempFile("ORPrefs_" + System.currentTimeMillis(), ".xml");
try (FileOutputStream tempFos = new FileOutputStream(tempFile.toFile())) {
tempPrefs.exportSubtree(tempFos);
filteredPrefs.exportSubtree(tempFos);
}
// Read and parse the temporary file
@ -149,11 +156,11 @@ public abstract class PreferencesExporter {
doc = factory.newDocumentBuilder().parse(tempFis);
}
// Find and rename the node
// Find and rename the filtered prefs node
NodeList nodeList = doc.getElementsByTagName("node");
for (int i = 0; i < nodeList.getLength(); i++) {
Element element = (Element) nodeList.item(i);
if (element.getAttribute("name").equals(nodeName)) {
if (element.getAttribute("name").equals(filteredPrefsNodeName)) {
element.setAttribute("name", ((SwingPreferences) prefs).getNodename());
break;
}
@ -176,10 +183,14 @@ public abstract class PreferencesExporter {
} catch (ParserConfigurationException | TransformerException | SAXException e) {
e.printStackTrace();
} finally {
root.node(nodeName).removeNode();
root.node(filteredPrefsNodeName).removeNode();
}
}
private static void exportFilteredPreferences(Preferences preferences, FileOutputStream fos) throws BackingStoreException, IOException {
exportPreferencesToFile(preferences, fos, true);
}
private static void copyFilteredPreferences(Preferences src, Preferences dest,
List<String> nodesToIgnore, List<String> keysToIgnore, List<String> prefixKeysToIgnore) throws BackingStoreException {
for (String key : src.keys()) {

View File

@ -17,6 +17,11 @@ public abstract class PreferencesImporter {
private static final Translator trans = Application.getTranslator();
private static final Logger log = LoggerFactory.getLogger(PreferencesImporter.class);
/**
* Import the preferences by first showing a file chooser dialog to locate the preferences file.
* @param parent The parent window for the file chooser dialog.
* @return true if the preferences were imported successfully, false otherwise.
*/
public static boolean importPreferences(Window parent) {
final JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle(trans.get("PreferencesImporter.chooser.title"));
@ -33,13 +38,26 @@ public abstract class PreferencesImporter {
((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory());
File importFile = chooser.getSelectedFile();
return importPreferences(importFile);
}
/**
* Import preferences from an XML file.
* @param importFile The XML file to import preferences from.
* @return true if the preferences were imported successfully, false otherwise.
*/
public static boolean importPreferences(File importFile) {
try (FileInputStream fis = new FileInputStream(importFile)) {
Preferences.importPreferences(fis);
// Ensure units are updated
((SwingPreferences) Application.getPreferences()).loadDefaultUnits();
log.info("Preferences imported successfully.");
return true;
} catch (IOException | InvalidPreferencesFormatException e) {
log.warn("Error while importing preferences: " + e.getMessage());
}
return true;
return false;
}
}

View File

@ -625,7 +625,10 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
///////// Default unit storage
/**
* Loads the default units from the preferences.
*/
public void loadDefaultUnits() {
Preferences prefs = PREFNODE.node("units");
try {
@ -645,7 +648,10 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
Application.getExceptionHandler().handleErrorCondition(e);
}
}
/**
* Stores the standard default units in the preferences.
*/
public void storeDefaultUnits() {
Preferences prefs = PREFNODE.node("units");

View File

@ -6,8 +6,10 @@ import java.awt.desktop.OpenFilesHandler;
import java.awt.desktop.PreferencesHandler;
import java.awt.desktop.QuitHandler;
import java.awt.desktop.AppReopenedListener;
import java.io.File;
import net.sf.openrocket.communication.UpdateInfoRetriever;
import net.sf.openrocket.gui.main.MRUDesignFile;
import net.sf.openrocket.gui.util.DummyFrameMenuOSX;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,8 +42,9 @@ final class OSXSetup {
* The handler for file associations
*/
public static final OpenFilesHandler OPEN_FILE_HANDLER = (e) -> {
log.info("Opening file from association: " + e.getFiles().get(0));
BasicFrame.open(e.getFiles().get(0), BasicFrame.lastFrameInstance);
File associateFile = e.getFiles().get(0);
log.info("Opening file from association: " + associateFile);
BasicFrame.open(associateFile, BasicFrame.lastFrameInstance);
};
/**