Merge branch 'unstable' into issue-1889
This commit is contained in:
commit
f252ae0fd1
@ -69,3 +69,4 @@ OpenRocket needs help to become even better. Implementing features, writing docu
|
||||
- Vladimir Beran
|
||||
- Polish Rocketry Society / Łukasz & Alex Kazanski
|
||||
- Sibo Van Gool
|
||||
- Mohamed Amin Elkebsi
|
||||
|
@ -1849,7 +1849,7 @@ PlotConfiguration.Groundtrack = Ground track
|
||||
Warning.LargeAOA.str1 = Large angle of attack encountered.
|
||||
Warning.LargeAOA.str2 = Large angle of attack encountered (
|
||||
Warning.DISCONTINUITY = Discontinuity in rocket body diameter
|
||||
Warning.THICK_FIN = Thick fins may not be modeled accurately.
|
||||
Warning.THICK_FIN = Thick fins may not simulate accurately.
|
||||
Warning.JAGGED_EDGED_FIN = Jagged-edged fin predictions may be inaccurate.
|
||||
Warning.LISTENERS_AFFECTED = Listeners modified the flight simulation
|
||||
Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING = Recovery device opened while motor still burning.
|
||||
@ -1861,10 +1861,9 @@ Warning.RECOVERY_HIGH_SPEED = Recovery device deployment at high speed
|
||||
Warning.NO_RECOVERY_DEVICE = No recovery device defined in the simulation.
|
||||
Warning.TUMBLE_UNDER_THRUST = Stage began to tumble under thrust.
|
||||
Warning.EVENT_AFTER_LANDING = Flight Event occurred after landing:
|
||||
Warning.ZERO_LENGTH_BODY = Zero length bodies may not result in accurate simulations.
|
||||
Warning.ZERO_RADIUS_BODY = Zero length bodies may not result in accurate simulations.
|
||||
Warning.TUBE_SEPARATION = Space between tube fins may not result in accurate simulations.
|
||||
Warning.TUBE_OVERLAP = Overlapping tube fins may not result in accurate simulations.
|
||||
Warning.ZERO_VOLUME_BODY = Zero-volume bodies may not simulate accurately.
|
||||
Warning.TUBE_SEPARATION = Space between tube fins may not simulate accurately.
|
||||
Warning.TUBE_OVERLAP = Overlapping tube fins may not simulate accurately.
|
||||
Warning.EMPTY_BRANCH = Simulation branch contains no data
|
||||
Warning.SEPARATION_ORDER = Stages separated in an unreasonable order
|
||||
|
||||
|
2285
core/resources/l10n/messages_ar.properties
Normal file
2285
core/resources/l10n/messages_ar.properties
Normal file
File diff suppressed because it is too large
Load Diff
@ -1700,8 +1700,7 @@ Warning.RECOVERY_LAUNCH_ROD = Terugvorderingsapparaat ingezet tijdens de lanceri
|
||||
Warning.RECOVERY_HIGH_SPEED = Terugvorderingsapparaat ingezet bij hoge snelheid
|
||||
Warning.TUMBLE_UNDER_THRUST = Trap begon te tuimelen onder stuwkracht.
|
||||
Warning.EVENT_AFTER_LANDING = Vlucht evenement opgetreden tijdens landing:
|
||||
Warning.ZERO_LENGTH_BODY = Lichamen met een lengte van nul resulteren mogelijk niet in nauwkeurige simulaties.
|
||||
Warning.ZERO_RADIUS_BODY = Lichamen met een lengte van nul resulteren mogelijk niet in nauwkeurige simulaties.
|
||||
Warning.ZERO_VOLUME_BODY = Lichamen met een lengte van nul resulteren mogelijk niet in nauwkeurige simulaties.
|
||||
|
||||
! Scale dialog
|
||||
ScaleDialog.lbl.scaleRocket = Volledige raket
|
||||
|
@ -1823,8 +1823,7 @@ Warning.RECOVERY_HIGH_SPEED = \u0421\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u
|
||||
Warning.NO_RECOVERY_DEVICE = \u0412 \u0440\u0430\u0441\u0447\u0435\u0442\u0435 \u043D\u0435\u0442 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432 \u0441\u043F\u0430\u0441\u0435\u043D\u0438\u044F
|
||||
Warning.TUMBLE_UNDER_THRUST = \u0421\u0442\u0443\u043F\u0435\u043D\u044C \u043D\u0430\u0447\u0430\u043B\u0430 \u043A\u0443\u0432\u044B\u0440\u044C\u043A\u0430\u0442\u044C\u0441\u044F \u043F\u043E\u0434 \u0442\u044F\u0433\u043E\u0439.
|
||||
Warning.EVENT_AFTER_LANDING = \u041B\u0435\u0442\u043D\u043E\u0435 \u0441\u043E\u0431\u044B\u0442\u0438\u0435 \u0432\u043E\u0437\u043D\u0438\u043A\u043B\u043E \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u0438\u0437\u0435\u043C\u043B\u0435\u043D\u0438\u044F:
|
||||
Warning.ZERO_LENGTH_BODY = \u0422\u0435\u043B\u0430 \u043D\u0443\u043B\u0435\u0432\u043E\u0439 \u0434\u043B\u0438\u043D\u044B \u043C\u043E\u0433\u0443\u0442 \u043F\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043A \u043D\u0435\u0442\u043E\u0447\u043D\u043E\u0441\u0442\u044F\u043C \u0432 \u0440\u0430\u0441\u0447\u0435\u0442\u0435.
|
||||
Warning.ZERO_RADIUS_BODY = \u0422\u0435\u043B\u0430 \u043D\u0443\u043B\u0435\u0432\u043E\u0433\u043E \u0440\u0430\u0434\u0438\u0443\u0441\u0430 \u043C\u043E\u0433\u0443\u0442 \u043F\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043A \u043D\u0435\u0442\u043E\u0447\u043D\u043E\u0441\u0442\u044F\u043C \u0432 \u0440\u0430\u0441\u0447\u0435\u0442\u0435.
|
||||
Warning.ZERO_VOLUME_BODY = \u0422\u0435\u043B\u0430 \u043D\u0443\u043B\u0435\u0432\u043E\u0439 \u0434\u043B\u0438\u043D\u044B \u043C\u043E\u0433\u0443\u0442 \u043F\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043A \u043D\u0435\u0442\u043E\u0447\u043D\u043E\u0441\u0442\u044F\u043C \u0432 \u0440\u0430\u0441\u0447\u0435\u0442\u0435.
|
||||
Warning.TUBE_SEPARATION = \u041F\u0440\u043E\u0441\u0442\u0440\u0430\u043D\u0441\u0442\u0432\u043E \u043C\u0435\u0436\u0434\u0443 \u0442\u0440\u0443\u0431\u0447\u0430\u0442\u044B\u043C\u0438 \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u0430\u043C\u0438 \u043C\u043E\u0436\u0435\u0442 \u043F\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043A \u043D\u0435\u0442\u043E\u0447\u043D\u043E\u0441\u0442\u044F\u043C \u0432 \u0440\u0430\u0441\u0447\u0435\u0442\u0435.
|
||||
Warning.TUBE_OVERLAP = \u041F\u0435\u0440\u0435\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0438\u0435\u0441\u044F \u0442\u0440\u0443\u0431\u0447\u0430\u0442\u044B\u0435 \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u044B \u043C\u043E\u0433\u0443\u0442 \u043F\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043A \u043D\u0435\u0442\u043E\u0447\u043D\u043E\u0441\u0442\u044F\u043C \u0432 \u0440\u0430\u0441\u0447\u0435\u0442\u0435.
|
||||
|
||||
|
@ -1536,7 +1536,7 @@ PlotConfiguration.Simulationtime = Simulation time step and computation time
|
||||
Warning.LargeAOA.str1 = Large angle of attack encountered.
|
||||
Warning.LargeAOA.str2 = Large angle of attack encountered (
|
||||
Warning.DISCONTINUITY = Discontinuity in rocket body diameter
|
||||
Warning.THICK_FIN = Thick fins may not be modeled accurately.
|
||||
Warning.THICK_FIN = Thick fins may not simulate accurately.
|
||||
Warning.JAGGED_EDGED_FIN = Jagged-edged fin predictions may be inaccurate.
|
||||
Warning.LISTENERS_AFFECTED = Listeners modified the flight simulation
|
||||
Warning.RECOVERY_DEPLOYMENT_WHILE_BURNING = Recovery device opened while motor still burning.
|
||||
|
@ -390,8 +390,7 @@ public abstract class Warning {
|
||||
|
||||
public static final Warning EVENT_AFTER_LANDING = new Other(trans.get("Warning.EVENT_AFTER_LANDING"));
|
||||
|
||||
public static final Warning ZERO_LENGTH_BODY = new Other(trans.get("Warning.ZERO_LENGTH_BODY"));
|
||||
public static final Warning ZERO_RADIUS_BODY = new Other(trans.get("Warning.ZERO_RADIUS_BODY"));
|
||||
public static final Warning ZERO_VOLUME_BODY = new Other(trans.get("Warning.ZERO_VOLUME_BODY"));
|
||||
|
||||
public static final Warning TUBE_SEPARATION = new Other(trans.get("Warning.TUBE_SEPARATION"));
|
||||
public static final Warning TUBE_OVERLAP = new Other(trans.get("Warning.TUBE_OVERLAP"));
|
||||
|
@ -101,12 +101,9 @@ public class FinSetCalc extends RocketComponentCalc {
|
||||
return;
|
||||
}
|
||||
|
||||
if((EPSILON > bodyLength)) {
|
||||
if ((bodyLength < EPSILON) || (bodyRadius < EPSILON)) {
|
||||
// Add warnings: Phantom Body
|
||||
warnings.add(Warning.ZERO_LENGTH_BODY);
|
||||
}else if((EPSILON > bodyRadius)){
|
||||
// Add warnings: Phantom Body
|
||||
warnings.add(Warning.ZERO_RADIUS_BODY);
|
||||
warnings.add(Warning.ZERO_VOLUME_BODY);
|
||||
}else if( (0 < bodyRadius) && (thickness > bodyRadius / 2)){
|
||||
// Add warnings (radius/2 == diameter/4)
|
||||
warnings.add(Warning.THICK_FIN);
|
||||
|
@ -136,6 +136,21 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
updateMotors();
|
||||
updateActiveInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy only the stage activeness from another configuration.
|
||||
* @param other the configuration to copy the stage active flags from.
|
||||
*/
|
||||
public void copyStageActiveness(FlightConfiguration other) {
|
||||
for (StageFlags flags : this.stages.values()) {
|
||||
StageFlags otherFlags = other.stages.get(flags.stageNumber);
|
||||
if (otherFlags != null) {
|
||||
flags.active = otherFlags.active;
|
||||
}
|
||||
}
|
||||
updateMotors();
|
||||
updateActiveInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method flags a stage inactive. Other stages are unaffected.
|
||||
@ -831,6 +846,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
// Note the stages are updated in the constructor call.
|
||||
FlightConfiguration clone = new FlightConfiguration( this.rocket, this.fcid );
|
||||
clone.setName(configurationName);
|
||||
clone.copyStageActiveness(this);
|
||||
clone.preloadStageActiveness = this.preloadStageActiveness == null ? null : new HashMap<>(this.preloadStageActiveness);
|
||||
|
||||
clone.cachedBoundsAerodynamic = this.cachedBoundsAerodynamic.clone();
|
||||
@ -851,7 +867,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
@Override
|
||||
public FlightConfiguration copy( final FlightConfigurationId newId ) {
|
||||
// Note the stages are updated in the constructor call.
|
||||
FlightConfiguration copy= new FlightConfiguration( this.rocket, newId );
|
||||
FlightConfiguration copy = new FlightConfiguration( this.rocket, newId );
|
||||
final FlightConfigurationId copyId = copy.getId();
|
||||
|
||||
// copy motor instances.
|
||||
@ -861,6 +877,7 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo
|
||||
cloneMotor.getMount().setMotorConfig(cloneMotor, copyId);
|
||||
}
|
||||
|
||||
copy.copyStages(this);
|
||||
copy.preloadStageActiveness = this.preloadStageActiveness == null ? null : new HashMap<>(this.preloadStageActiveness);
|
||||
copy.cachedBoundsAerodynamic = this.cachedBoundsAerodynamic.clone();
|
||||
copy.cachedBounds = this.cachedBounds.clone();
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.l10n.L10N;
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
@ -28,6 +28,11 @@ public class AirStart extends AbstractSimulationExtension {
|
||||
name = L10N.replace(name, "{vel}", UnitGroup.UNITS_VELOCITY.toStringUnit(getLaunchVelocity()));
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Start simulation with a configurable altitude and velocity";
|
||||
}
|
||||
|
||||
public double getLaunchAltitude() {
|
||||
return config.getDouble("launchAltitude", 0.0);
|
@ -1,4 +1,4 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;
|
@ -1,4 +1,4 @@
|
||||
package net.sf.openrocket.simulation.listeners.example;
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@ -9,12 +9,14 @@ import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
|
||||
|
||||
public class CSVSaveListener extends AbstractSimulationListener {
|
||||
public class CSVSave extends AbstractSimulationExtension {
|
||||
|
||||
private static enum Types {
|
||||
TIME {
|
||||
@ -218,78 +220,89 @@ public class CSVSaveListener extends AbstractSimulationListener {
|
||||
|
||||
private File file;
|
||||
private PrintStream output = null;
|
||||
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "dump a CSV file with a predetermined set of flight variables to a CSV file while running";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException {
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
conditions.getSimulationListenerList().add(new CSVSaveListener());
|
||||
}
|
||||
|
||||
private class CSVSaveListener extends AbstractSimulationListener {
|
||||
|
||||
if (event.getType() == FlightEvent.Type.LAUNCH) {
|
||||
int n = 1;
|
||||
@Override
|
||||
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException {
|
||||
|
||||
if (output != null) {
|
||||
System.err.println("WARNING: Ending simulation logging to CSV file " +
|
||||
"(SIMULATION_END not encountered).");
|
||||
if (event.getType() == FlightEvent.Type.LAUNCH) {
|
||||
int n = 1;
|
||||
|
||||
if (output != null) {
|
||||
System.err.println("WARNING: Ending simulation logging to CSV file " +
|
||||
"(SIMULATION_END not encountered).");
|
||||
output.close();
|
||||
output = null;
|
||||
}
|
||||
|
||||
do {
|
||||
file = new File(String.format(FILENAME_FORMAT, n));
|
||||
n++;
|
||||
} while (file.exists());
|
||||
|
||||
System.err.println("Opening file " + file + " for CSV output.");
|
||||
try {
|
||||
output = new PrintStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println("ERROR OPENING FILE: " + e);
|
||||
}
|
||||
|
||||
final Types[] types = Types.values();
|
||||
StringBuilder s = new StringBuilder("# " + types[0].toString());
|
||||
for (int i = 1; i < types.length; i++) {
|
||||
s.append("," + types[i].toString());
|
||||
}
|
||||
output.println(s);
|
||||
|
||||
} else if (event.getType() == FlightEvent.Type.SIMULATION_END && output != null) {
|
||||
|
||||
System.err.println("Ending simulation logging to CSV file: " + file);
|
||||
output.close();
|
||||
output = null;
|
||||
|
||||
} else if (event.getType() != FlightEvent.Type.ALTITUDE) {
|
||||
|
||||
if (output != null) {
|
||||
output.println("# Event " + event);
|
||||
} else {
|
||||
System.err.println("WARNING: Event " + event + " encountered without open file");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
do {
|
||||
file = new File(String.format(FILENAME_FORMAT, n));
|
||||
n++;
|
||||
} while (file.exists());
|
||||
|
||||
System.err.println("Opening file " + file + " for CSV output.");
|
||||
try {
|
||||
output = new PrintStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println("ERROR OPENING FILE: " + e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
|
||||
final Types[] types = Types.values();
|
||||
StringBuilder s = new StringBuilder("# " + types[0].toString());
|
||||
for (int i = 1; i < types.length; i++) {
|
||||
s.append("," + types[i].toString());
|
||||
}
|
||||
output.println(s);
|
||||
|
||||
} else if (event.getType() == FlightEvent.Type.SIMULATION_END && output != null) {
|
||||
|
||||
System.err.println("Ending simulation logging to CSV file: " + file);
|
||||
output.close();
|
||||
output = null;
|
||||
|
||||
} else if (event.getType() != FlightEvent.Type.ALTITUDE) {
|
||||
StringBuilder s;
|
||||
|
||||
if (output != null) {
|
||||
output.println("# Event " + event);
|
||||
|
||||
s = new StringBuilder("" + types[0].getValue(status));
|
||||
for (int i = 1; i < types.length; i++) {
|
||||
s.append("," + types[i].getValue(status));
|
||||
}
|
||||
output.println(s);
|
||||
|
||||
} else {
|
||||
System.err.println("WARNING: Event " + event + " encountered without open file");
|
||||
|
||||
System.err.println("WARNING: stepTaken called with no open file " +
|
||||
"(t=" + status.getSimulationTime() + ")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
|
||||
final Types[] types = Types.values();
|
||||
StringBuilder s;
|
||||
|
||||
if (output != null) {
|
||||
|
||||
s = new StringBuilder("" + types[0].getValue(status));
|
||||
for (int i = 1; i < types.length; i++) {
|
||||
s.append("," + types[i].getValue(status));
|
||||
}
|
||||
output.println(s);
|
||||
|
||||
} else {
|
||||
|
||||
System.err.println("WARNING: stepTaken called with no open file " +
|
||||
"(t=" + status.getSimulationTime() + ")");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;
|
||||
|
||||
@Plugin
|
||||
public class CSVSaveProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public CSVSaveProvider() {
|
||||
super(CSVSave.class, "Reports", "CSV Save");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.motor.MotorConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
public class DampingMoment extends AbstractSimulationExtension {
|
||||
private static final Logger log = LoggerFactory.getLogger(DampingMoment.class);
|
||||
|
||||
// Save it as a FlightDataType
|
||||
private static final FlightDataType cdm = FlightDataType.getType("Damping moment coefficient", "Cdm", UnitGroup.UNITS_COEFFICIENT);
|
||||
private static final ArrayList<FlightDataType> types = new ArrayList<FlightDataType>();
|
||||
|
||||
DampingMoment() {
|
||||
types.add(cdm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FlightDataType> getFlightDataTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
log.debug("initializing...");
|
||||
conditions.getSimulationListenerList().add(new DampingMomentListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Damping Moment Coeficient(Cdm)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Calculate damping moment coefficient after every simulation step and publish to FlightData as Cdm";
|
||||
}
|
||||
|
||||
private class DampingMomentListener extends AbstractSimulationListener {
|
||||
|
||||
@Override
|
||||
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException {
|
||||
|
||||
//status.getFlightData().setValue(cdm, aerodynamicPart + propulsivePart);
|
||||
status.getFlightData().setValue(cdm, calculate(status, flightConditions));
|
||||
|
||||
return flightConditions;
|
||||
}
|
||||
|
||||
private double calculate(SimulationStatus status, FlightConditions flightConditions) {
|
||||
|
||||
// Work out the propulsive/jet damping part of the moment.
|
||||
|
||||
// dm/dt = (thrust - ma)/v
|
||||
FlightDataBranch data = status.getFlightData();
|
||||
|
||||
List<Double> mpAll = data.get(FlightDataType.TYPE_MOTOR_MASS);
|
||||
List<Double> time = data.get(FlightDataType.TYPE_TIME);
|
||||
if (mpAll == null || time == null) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
int len = mpAll.size();
|
||||
|
||||
// This isn't as accurate as I would like
|
||||
double mdot = Double.NaN;
|
||||
if (len > 2) {
|
||||
// Using polynomial interpolator for derivative. Doesn't help much
|
||||
//double[] x = { time.get(len-5), time.get(len-4), time.get(len-3), time.get(len-2), time.get(len-1) };
|
||||
//double[] y = { mpAll.get(len-5), mpAll.get(len-4), mpAll.get(len-3), mpAll.get(len-2), mpAll.get(len-1) };
|
||||
//PolyInterpolator interp = new PolyInterpolator(x);
|
||||
//double[] coeff = interp.interpolator(y);
|
||||
//double dt = .01;
|
||||
//mdot = (interp.eval(x[4], coeff) - interp.eval(x[4]-dt, coeff))/dt;
|
||||
|
||||
mdot = (mpAll.get(len - 1) - mpAll.get(len - 2)) / (time.get(len - 1) - time.get(len - 2));
|
||||
}
|
||||
|
||||
double cg = data.getLast(FlightDataType.TYPE_CG_LOCATION);
|
||||
|
||||
// find the maximum distance from nose to nozzle.
|
||||
double nozzleDistance = 0;
|
||||
FlightConfiguration config = status.getConfiguration();
|
||||
for (MotorConfiguration inst : config.getActiveMotors()) {
|
||||
double x_position= inst.getX();
|
||||
double x = x_position + inst.getMotor().getLaunchCGx();
|
||||
if (x > nozzleDistance) {
|
||||
nozzleDistance = x;
|
||||
}
|
||||
}
|
||||
|
||||
// now can get the propulsive part
|
||||
double propulsivePart = mdot * Math.pow(nozzleDistance - cg, 2);
|
||||
|
||||
// Work out the aerodynamic part of the moment.
|
||||
double aerodynamicPart = 0;
|
||||
|
||||
AerodynamicCalculator aerocalc = status.getSimulationConditions().getAerodynamicCalculator();
|
||||
|
||||
// Must go through each component ...
|
||||
Map<RocketComponent, AerodynamicForces> forces = aerocalc.getForceAnalysis(status.getConfiguration(), flightConditions, status.getWarnings());
|
||||
for (Map.Entry<RocketComponent, AerodynamicForces> entry : forces.entrySet()) {
|
||||
|
||||
RocketComponent comp = entry.getKey();
|
||||
|
||||
if (!comp.isAerodynamic())
|
||||
continue;
|
||||
|
||||
//System.out.println(comp.toString());
|
||||
|
||||
double CNa = entry.getValue().getCNa(); //?
|
||||
double Cp = entry.getValue().getCP().length();
|
||||
double z = comp.getAxialOffset();
|
||||
|
||||
aerodynamicPart += CNa * Math.pow(z - Cp, 2);
|
||||
}
|
||||
|
||||
double v = flightConditions.getVelocity();
|
||||
double rho = flightConditions.getAtmosphericConditions().getDensity();
|
||||
double ar = flightConditions.getRefArea();
|
||||
|
||||
aerodynamicPart = aerodynamicPart * .5 * rho * v * ar;
|
||||
|
||||
return aerodynamicPart + propulsivePart;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;
|
||||
|
||||
@Plugin
|
||||
public class DampingMomentProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public DampingMomentProvider() {
|
||||
super(DampingMoment.class, "Post-step flight conditions", "Damping Moment Coefficient (Cdm)");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
|
||||
public class PrintSimulation extends AbstractSimulationExtension {
|
||||
|
||||
@Override
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
conditions.getSimulationListenerList().add(new PrintSimulationListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Print Simulation Values";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Print summary of simulation progress to standard output after every step";
|
||||
}
|
||||
|
||||
private class PrintSimulationListener extends AbstractSimulationListener {
|
||||
|
||||
@Override
|
||||
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException {
|
||||
System.out.println("*** handleEvent *** " + event.toString() +
|
||||
" position=" + status.getRocketPosition() + " velocity=" + status.getRocketVelocity());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
FlightDataBranch data = status.getFlightData();
|
||||
System.out.printf("*** stepTaken *** time=%.3f position=" + status.getRocketPosition() +
|
||||
" velocity=" + status.getRocketVelocity() + "=%.3f\n", status.getSimulationTime(), status.getRocketVelocity().length());
|
||||
System.out.printf(" thrust=%.3fN drag==%.3fN mass=%.3fkg " +
|
||||
"accZ=%.3fm/s2 acc=%.3fm/s2\n",
|
||||
data.getLast(FlightDataType.TYPE_THRUST_FORCE),
|
||||
data.getLast(FlightDataType.TYPE_DRAG_FORCE),
|
||||
data.getLast(FlightDataType.TYPE_MASS),
|
||||
data.getLast(FlightDataType.TYPE_ACCELERATION_Z),
|
||||
data.getLast(FlightDataType.TYPE_ACCELERATION_TOTAL));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;
|
||||
|
||||
@Plugin
|
||||
public class PrintSimulationProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public PrintSimulationProvider() {
|
||||
super(PrintSimulation.class, "Reports", "Print Simulation");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
|
||||
/**
|
||||
* An example listener that applies a PI-controller to adjust the cant of fins
|
||||
* to control the rocket's roll rate
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*
|
||||
* One note: making aerodynamic changes during the simulation results in the simulation
|
||||
* running *extremely* slowly
|
||||
*/
|
||||
public class RollControl extends AbstractSimulationExtension {
|
||||
|
||||
// save fin cant angle as a FlightDataType
|
||||
private static final ArrayList<FlightDataType> types = new ArrayList<FlightDataType>();
|
||||
private static final FlightDataType FIN_CANT_TYPE = FlightDataType.getType("Control fin cant", "\u03B1fc", UnitGroup.UNITS_ANGLE);
|
||||
|
||||
@Override
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
conditions.getSimulationListenerList().add(new RollControlListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Roll Control";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Use a PID control to control a rocket's roll. The current cant angle of the control finset is published to flight data as \u03B1fc. "
|
||||
+ "Since this extension modifies design parameters during the simulation, it causes the simulation to run <b>much</b> more slowly.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FlightDataType> getFlightDataTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
RollControl() {
|
||||
types.add(FIN_CANT_TYPE);
|
||||
}
|
||||
|
||||
public String getControlFinName() {
|
||||
return config.getString("controlFinName", "CONTROL");
|
||||
}
|
||||
|
||||
public void setControlFinName(String name) {
|
||||
config.put("controlFinName", name);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
public double getStartTime() {
|
||||
return config.getDouble("startTime", 0.5);
|
||||
}
|
||||
|
||||
public void setStartTime(double startTime) {
|
||||
config.put("startTime", startTime);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
// Desired roll rate (rad/sec)
|
||||
public double getSetPoint() {
|
||||
return config.getDouble("setPoint", 0.0);
|
||||
}
|
||||
|
||||
public void setSetPoint(double rollRate) {
|
||||
config.put("setPoint", rollRate);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
// Maximum control fin turn rate (rad/sec)
|
||||
public double getFinRate() {
|
||||
return config.getDouble("finRate", 10 * Math.PI/180);
|
||||
}
|
||||
|
||||
public void setFinRate(double finRate) {
|
||||
config.put("finRate", finRate);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
// Maximum control fin angle (rad)
|
||||
public double getMaxFinAngle() {
|
||||
return config.getDouble("maxFinAngle", 15 * Math.PI / 180);
|
||||
}
|
||||
|
||||
public void setMaxFinAngle(double maxFin) {
|
||||
config.put("maxFinAngle", maxFin);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
public double getKP() {
|
||||
return config.getDouble("KP", 0.007);
|
||||
}
|
||||
|
||||
public void setKP(double KP) {
|
||||
config.put("KP", KP);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
public double getKI() {
|
||||
return config.getDouble("KI", 0.2);
|
||||
}
|
||||
|
||||
public void setKI(double KI) {
|
||||
config.put("KI", KI);
|
||||
fireChangeEvent();
|
||||
}
|
||||
|
||||
private class RollControlListener extends AbstractSimulationListener {
|
||||
private FinSet finset;
|
||||
|
||||
private double rollRate;
|
||||
|
||||
private double prevTime = 0;
|
||||
private double intState = 0;
|
||||
|
||||
private double initialFinPosition;
|
||||
private double finPosition = 0;
|
||||
|
||||
@Override
|
||||
public void startSimulation(SimulationStatus status) throws SimulationException {
|
||||
|
||||
// Find the fin set
|
||||
finset = null;
|
||||
for (RocketComponent c : status.getConfiguration().getActiveComponents()) {
|
||||
if ((c instanceof FinSet) && c.getName().equals(getControlFinName())) {
|
||||
finset = (FinSet) c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (finset == null) {
|
||||
throw new SimulationException("A fin set with name '" + getControlFinName() + "' was not found");
|
||||
}
|
||||
|
||||
// remember the initial fin position so we can set it back after running the simulation
|
||||
initialFinPosition = finset.getCantAngle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) {
|
||||
// Store the current roll rate for later use
|
||||
rollRate = flightConditions.getRollRate();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
// Activate PID controller only after a specific time
|
||||
if (status.getSimulationTime() < getStartTime()) {
|
||||
prevTime = status.getSimulationTime();
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine time step
|
||||
double deltaT = status.getSimulationTime() - prevTime;
|
||||
prevTime = status.getSimulationTime();
|
||||
|
||||
// PID controller
|
||||
double error = getSetPoint() - rollRate;
|
||||
|
||||
double p = getKP() * error;
|
||||
intState += error * deltaT;
|
||||
double i = getKI() * intState;
|
||||
|
||||
double value = p + i;
|
||||
|
||||
// Limit the fin turn rate
|
||||
if (finPosition < value) {
|
||||
finPosition = Math.min(finPosition + getFinRate() * deltaT, value);
|
||||
} else {
|
||||
finPosition = Math.max(finPosition - getFinRate() * deltaT, value);
|
||||
}
|
||||
|
||||
// Clamp the fin angle between bounds
|
||||
if (Math.abs(value) > getMaxFinAngle()) {
|
||||
System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n",
|
||||
value * 180 / Math.PI, status.getSimulationTime());
|
||||
value = MathUtil.clamp(value, -getMaxFinAngle(), getMaxFinAngle());
|
||||
}
|
||||
|
||||
// Set the control fin cant and store the data
|
||||
finset.setCantAngle(finPosition);
|
||||
status.getFlightData().setValue(FIN_CANT_TYPE, finPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endSimulation(SimulationStatus status, SimulationException exception) {
|
||||
finset.setCantAngle(initialFinPosition);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;
|
||||
|
||||
@Plugin
|
||||
public class RollControlProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public RollControlProvider() {
|
||||
super(RollControl.class, "Control Enhancements", "Roll Control");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.SimulationConditions;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtension;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
|
||||
/**
|
||||
* A simulation listener that stops the simulation after a specified number of steps or
|
||||
* after a specified amount of simulation time.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class StopSimulation extends AbstractSimulationExtension {
|
||||
|
||||
@Override
|
||||
public void initialize(SimulationConditions conditions) throws SimulationException {
|
||||
conditions.getSimulationListenerList().add(new StopSimulationListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Stop Simulation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Stop simulafter at a configurable simulation time or step count";
|
||||
}
|
||||
|
||||
public int getReportRate() {
|
||||
return config.getInt("reportRate", 500);
|
||||
}
|
||||
|
||||
public void setReportRate(int reportRate) {
|
||||
config.put("reportRate", reportRate);
|
||||
}
|
||||
|
||||
public int getStopStep() {
|
||||
return config.getInt("stopStep", 5000);
|
||||
}
|
||||
|
||||
public void setStopStep(int stopStep) {
|
||||
config.put("stopStep", stopStep);
|
||||
}
|
||||
|
||||
public int getStopTime() {
|
||||
return config.getInt("stopTime", 10);
|
||||
}
|
||||
|
||||
public void setStopTime(int stopTime) {
|
||||
config.put("stopTime", stopTime);
|
||||
}
|
||||
|
||||
private class StopSimulationListener extends AbstractSimulationListener {
|
||||
|
||||
private int step = 0;
|
||||
|
||||
private long startTime = -1;
|
||||
private long time = -1;
|
||||
|
||||
@Override
|
||||
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) {
|
||||
|
||||
if (event.getType() == FlightEvent.Type.LAUNCH) {
|
||||
System.out.println("Simulation starting.");
|
||||
time = System.nanoTime();
|
||||
startTime = System.nanoTime();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
step++;
|
||||
if ((step % getReportRate()) == 0) {
|
||||
long t = System.nanoTime();
|
||||
|
||||
System.out.printf("Step %4d, time=%.3f, took %d us/step (avg. %d us/step)\n",
|
||||
step, status.getSimulationTime(), (t - time) / 1000 / getReportRate(), (t - startTime) / 1000 / step);
|
||||
time = t;
|
||||
}
|
||||
if (status.getSimulationTime() >= getStopTime() || step >= getStopStep()) {
|
||||
System.out.printf("Stopping simulation, step=%d time=%.3f\n", step, status.getSimulationTime());
|
||||
status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END,
|
||||
status.getSimulationTime(), null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvider;
|
||||
|
||||
@Plugin
|
||||
public class StopSimulationProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public StopSimulationProvider() {
|
||||
super(StopSimulation.class, "Simulation Conditions", "Stop Simulation");
|
||||
}
|
||||
|
||||
}
|
@ -7,7 +7,7 @@ import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvide
|
||||
public class JavaCodeProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public JavaCodeProvider() {
|
||||
super(JavaCode.class, "User code", "Java code");
|
||||
super(JavaCode.class, "Scripts", "Java listeners");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import net.sf.openrocket.simulation.extension.AbstractSimulationExtensionProvide
|
||||
public class ScriptingProvider extends AbstractSimulationExtensionProvider {
|
||||
|
||||
public ScriptingProvider() {
|
||||
super(ScriptingExtension.class, "User code", "Scripts");
|
||||
super(ScriptingExtension.class, "Scripts", "JavaScript");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,116 +0,0 @@
|
||||
package net.sf.openrocket.simulation.listeners.example;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
|
||||
import net.sf.openrocket.aerodynamics.AerodynamicForces;
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.aerodynamics.WarningSet;
|
||||
import net.sf.openrocket.motor.MotorConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
public class DampingMoment extends AbstractSimulationListener {
|
||||
|
||||
private static final FlightDataType type = FlightDataType.getType("Damping moment coefficient", "Cdm", UnitGroup.UNITS_COEFFICIENT);
|
||||
|
||||
// unused
|
||||
//private static final FlightDataType[] typeList = { type };
|
||||
|
||||
@Override
|
||||
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) throws SimulationException {
|
||||
|
||||
// Save it as a flightdatatype
|
||||
|
||||
//status.getFlightData().setValue(type, aerodynamicPart + propulsivePart);
|
||||
status.getFlightData().setValue(type, calculate(status, flightConditions));
|
||||
|
||||
return flightConditions;
|
||||
}
|
||||
|
||||
private double calculate(SimulationStatus status, FlightConditions flightConditions) {
|
||||
|
||||
// Work out the propulsive/jet damping part of the moment.
|
||||
|
||||
// dm/dt = (thrust - ma)/v
|
||||
FlightDataBranch data = status.getFlightData();
|
||||
|
||||
List<Double> mpAll = data.get(FlightDataType.TYPE_MOTOR_MASS);
|
||||
List<Double> time = data.get(FlightDataType.TYPE_TIME);
|
||||
if (mpAll == null || time == null) {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
int len = mpAll.size();
|
||||
|
||||
// This isn't as accurate as I would like
|
||||
double mdot = Double.NaN;
|
||||
if (len > 2) {
|
||||
// Using polynomial interpolator for derivative. Doesn't help much
|
||||
//double[] x = { time.get(len-5), time.get(len-4), time.get(len-3), time.get(len-2), time.get(len-1) };
|
||||
//double[] y = { mpAll.get(len-5), mpAll.get(len-4), mpAll.get(len-3), mpAll.get(len-2), mpAll.get(len-1) };
|
||||
//PolyInterpolator interp = new PolyInterpolator(x);
|
||||
//double[] coeff = interp.interpolator(y);
|
||||
//double dt = .01;
|
||||
//mdot = (interp.eval(x[4], coeff) - interp.eval(x[4]-dt, coeff))/dt;
|
||||
|
||||
mdot = (mpAll.get(len - 1) - mpAll.get(len - 2)) / (time.get(len - 1) - time.get(len - 2));
|
||||
}
|
||||
|
||||
double cg = data.getLast(FlightDataType.TYPE_CG_LOCATION);
|
||||
|
||||
// find the maximum distance from nose to nozzle.
|
||||
double nozzleDistance = 0;
|
||||
FlightConfiguration config = status.getConfiguration();
|
||||
for (MotorConfiguration inst : config.getActiveMotors()) {
|
||||
double x_position= inst.getX();
|
||||
double x = x_position + inst.getMotor().getLaunchCGx();
|
||||
if (x > nozzleDistance) {
|
||||
nozzleDistance = x;
|
||||
}
|
||||
}
|
||||
|
||||
// now can get the propulsive part
|
||||
double propulsivePart = mdot * Math.pow(nozzleDistance - cg, 2);
|
||||
|
||||
// Work out the aerodynamic part of the moment.
|
||||
double aerodynamicPart = 0;
|
||||
|
||||
AerodynamicCalculator aerocalc = status.getSimulationConditions().getAerodynamicCalculator();
|
||||
|
||||
// Must go through each component ...
|
||||
Map<RocketComponent, AerodynamicForces> forces = aerocalc.getForceAnalysis(status.getConfiguration(), flightConditions, status.getWarnings());
|
||||
for (Map.Entry<RocketComponent, AerodynamicForces> entry : forces.entrySet()) {
|
||||
|
||||
RocketComponent comp = entry.getKey();
|
||||
|
||||
if (!comp.isAerodynamic())
|
||||
continue;
|
||||
|
||||
//System.out.println(comp.toString());
|
||||
|
||||
double CNa = entry.getValue().getCNa(); //?
|
||||
double Cp = entry.getValue().getCP().length();
|
||||
double z = comp.getAxialOffset();
|
||||
|
||||
aerodynamicPart += CNa * Math.pow(z - Cp, 2);
|
||||
}
|
||||
|
||||
double v = flightConditions.getVelocity();
|
||||
double rho = flightConditions.getAtmosphericConditions().getDensity();
|
||||
double ar = flightConditions.getRefArea();
|
||||
|
||||
aerodynamicPart = aerodynamicPart * .5 * rho * v * ar;
|
||||
|
||||
return aerodynamicPart + propulsivePart;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package net.sf.openrocket.simulation.listeners.example;
|
||||
|
||||
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
|
||||
|
||||
public class PrintSimulationListener extends AbstractSimulationListener {
|
||||
|
||||
@Override
|
||||
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) throws SimulationException {
|
||||
System.out.println("*** handleEvent *** " + event.toString() +
|
||||
" position=" + status.getRocketPosition() + " velocity=" + status.getRocketVelocity());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
FlightDataBranch data = status.getFlightData();
|
||||
System.out.printf("*** stepTaken *** time=%.3f position=" + status.getRocketPosition() +
|
||||
" velocity=" + status.getRocketVelocity() + "=%.3f\n", status.getSimulationTime(), status.getRocketVelocity().length());
|
||||
System.out.printf(" thrust=%.3fN drag==%.3fN mass=%.3fkg " +
|
||||
"accZ=%.3fm/s2 acc=%.3fm/s2\n",
|
||||
data.getLast(FlightDataType.TYPE_THRUST_FORCE),
|
||||
data.getLast(FlightDataType.TYPE_DRAG_FORCE),
|
||||
data.getLast(FlightDataType.TYPE_MASS),
|
||||
data.getLast(FlightDataType.TYPE_ACCELERATION_Z),
|
||||
data.getLast(FlightDataType.TYPE_ACCELERATION_TOTAL));
|
||||
}
|
||||
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
package net.sf.openrocket.simulation.listeners.example;
|
||||
|
||||
import net.sf.openrocket.aerodynamics.FlightConditions;
|
||||
import net.sf.openrocket.rocketcomponent.FinSet;
|
||||
import net.sf.openrocket.rocketcomponent.RocketComponent;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
import net.sf.openrocket.util.MathUtil;
|
||||
|
||||
/**
|
||||
* An example listener that applies a PI-controller to adjust the cant of fins
|
||||
* named "CONTROL" to stop the rocket from rolling.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class RollControlListener extends AbstractSimulationListener {
|
||||
|
||||
// Name of control fin set
|
||||
private static final String CONTROL_FIN_NAME = "CONTROL";
|
||||
|
||||
// Define custom flight data type
|
||||
private static final FlightDataType FIN_CANT_TYPE = FlightDataType.getType("Control fin cant", "\u03B1fc", UnitGroup.UNITS_ANGLE);
|
||||
|
||||
// Simulation time at which PID controller is activated
|
||||
private static final double START_TIME = 0.5;
|
||||
|
||||
// Desired roll rate (rad/sec)
|
||||
private static final double SETPOINT = 0.0;
|
||||
|
||||
// Maximum control fin turn rate (rad/sec)
|
||||
private static final double TURNRATE = 10 * Math.PI / 180;
|
||||
|
||||
// Maximum control fin angle (rad)
|
||||
private static final double MAX_ANGLE = 15 * Math.PI / 180;
|
||||
|
||||
/*
|
||||
* PID parameters
|
||||
*
|
||||
* At M=0.3 KP oscillation threshold between 0.35 and 0.4. Good KI=3
|
||||
* At M=0.6 KP oscillation threshold between 0.07 and 0.08 Good KI=2
|
||||
* At M=0.9 KP oscillation threshold between 0.013 and 0.014 Good KI=0.5
|
||||
*/
|
||||
private static final double KP = 0.007;
|
||||
private static final double KI = 0.2;
|
||||
|
||||
private double rollRate;
|
||||
|
||||
private double prevTime = 0;
|
||||
private double intState = 0;
|
||||
|
||||
private double finPosition = 0;
|
||||
|
||||
@Override
|
||||
public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) {
|
||||
// Store the current roll rate for later use
|
||||
rollRate = flightConditions.getRollRate();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
// Activate PID controller only after a specific time
|
||||
if (status.getSimulationTime() < START_TIME) {
|
||||
prevTime = status.getSimulationTime();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the fin set named CONTROL
|
||||
FinSet finset = null;
|
||||
for (RocketComponent c : status.getConfiguration().getActiveComponents()) {
|
||||
if ((c instanceof FinSet) && (c.getName().equals(CONTROL_FIN_NAME))) {
|
||||
finset = (FinSet) c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (finset == null) {
|
||||
throw new SimulationException("A fin set with name '" + CONTROL_FIN_NAME + "' was not found");
|
||||
}
|
||||
|
||||
// Determine time step
|
||||
double deltaT = status.getSimulationTime() - prevTime;
|
||||
prevTime = status.getSimulationTime();
|
||||
|
||||
// PID controller
|
||||
double error = SETPOINT - rollRate;
|
||||
|
||||
double p = KP * error;
|
||||
intState += error * deltaT;
|
||||
double i = KI * intState;
|
||||
|
||||
double value = p + i;
|
||||
|
||||
// Clamp the fin angle between -MAX_ANGLE and MAX_ANGLE
|
||||
if (Math.abs(value) > MAX_ANGLE) {
|
||||
System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n",
|
||||
value * 180 / Math.PI, status.getSimulationTime());
|
||||
value = MathUtil.clamp(value, -MAX_ANGLE, MAX_ANGLE);
|
||||
}
|
||||
|
||||
// Limit the fin turn rate
|
||||
if (finPosition < value) {
|
||||
finPosition = Math.min(finPosition + TURNRATE * deltaT, value);
|
||||
} else {
|
||||
finPosition = Math.max(finPosition - TURNRATE * deltaT, value);
|
||||
}
|
||||
|
||||
// Set the control fin cant and store the data
|
||||
finset.setCantAngle(finPosition);
|
||||
status.getFlightData().setValue(FIN_CANT_TYPE, finPosition);
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package net.sf.openrocket.simulation.listeners.example;
|
||||
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.simulation.SimulationStatus;
|
||||
import net.sf.openrocket.simulation.exception.SimulationException;
|
||||
import net.sf.openrocket.simulation.listeners.AbstractSimulationListener;
|
||||
|
||||
/**
|
||||
* A simulation listener that stops the simulation after a specified number of steps or
|
||||
* after a specified amount of simulation time.
|
||||
*
|
||||
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
|
||||
*/
|
||||
public class StopSimulationListener extends AbstractSimulationListener {
|
||||
|
||||
private final int REPORT = 500;
|
||||
|
||||
private final double stopTime;
|
||||
private final int stopStep;
|
||||
|
||||
private int step = 0;
|
||||
|
||||
private long startTime = -1;
|
||||
private long time = -1;
|
||||
|
||||
public StopSimulationListener() {
|
||||
this(0, 0);
|
||||
}
|
||||
|
||||
public StopSimulationListener(double t, int n) {
|
||||
stopTime = t;
|
||||
stopStep = n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleFlightEvent(SimulationStatus status, FlightEvent event) {
|
||||
|
||||
if (event.getType() == FlightEvent.Type.LAUNCH) {
|
||||
System.out.println("Simulation starting.");
|
||||
time = System.nanoTime();
|
||||
startTime = System.nanoTime();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void postStep(SimulationStatus status) throws SimulationException {
|
||||
step++;
|
||||
if ((step % REPORT) == 0) {
|
||||
long t = System.nanoTime();
|
||||
|
||||
System.out.printf("Step %4d, time=%.3f, took %d us/step (avg. %d us/step)\n",
|
||||
step, status.getSimulationTime(), (t - time) / 1000 / REPORT, (t - startTime) / 1000 / step);
|
||||
time = t;
|
||||
}
|
||||
if (status.getSimulationTime() >= stopTime || step >= stopStep) {
|
||||
System.out.printf("Stopping simulation, step=%d time=%.3f\n", step, status.getSimulationTime());
|
||||
status.getEventQueue().add(new FlightEvent(FlightEvent.Type.SIMULATION_END,
|
||||
status.getSimulationTime(), null));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -165,4 +165,11 @@ public class BoundingBox {
|
||||
min.x, min.y, min.z,
|
||||
max.x, max.y, max.z );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof BoundingBox &&
|
||||
((BoundingBox) other).min.equals(this.min) &&
|
||||
((BoundingBox) other).max.equals(this.max);
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,19 @@ package net.sf.openrocket.rocketcomponent;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.openrocket.util.BoundingBox;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sf.openrocket.util.Coordinate;
|
||||
@ -572,6 +579,134 @@ public class FlightConfigurationTest extends BaseTestCase {
|
||||
assertThat(components.get(1).getName(), equalTo("Core Stage Body"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopy() throws NoSuchFieldException, IllegalAccessException {
|
||||
Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
FlightConfiguration original = rocket.getSelectedConfiguration();
|
||||
original.setOnlyStage(0);
|
||||
|
||||
// vvvv Test Target vvvv
|
||||
FlightConfiguration copy = original.copy(null);
|
||||
// ^^^^ Test Target ^^^^
|
||||
|
||||
assertNotEquals(original, copy);
|
||||
assertNotSame(original, copy);
|
||||
assertEquals(original.getName(), copy.getName());
|
||||
assertNotEquals(original.getFlightConfigurationID(), copy.getFlightConfigurationID());
|
||||
|
||||
// Test preloadStageActiveness copy
|
||||
Field preloadStageActivenessField = FlightConfiguration.class.getDeclaredField("preloadStageActiveness");
|
||||
preloadStageActivenessField.setAccessible(true);
|
||||
Map<Integer, Boolean> preloadStageActivenessOriginal = (Map<Integer, Boolean>) preloadStageActivenessField.get(original);
|
||||
Map<Integer, Boolean> preloadStageActivenessCopy = (Map<Integer, Boolean>) preloadStageActivenessField.get(copy);
|
||||
assertEquals(preloadStageActivenessOriginal, preloadStageActivenessCopy);
|
||||
if (preloadStageActivenessOriginal == null) {
|
||||
assertNull(preloadStageActivenessCopy);
|
||||
} else {
|
||||
assertNotSame(preloadStageActivenessOriginal, preloadStageActivenessCopy);
|
||||
}
|
||||
|
||||
// Test cachedBoundsAerodynamic copy
|
||||
Field cachedBoundsAerodynamicField = FlightConfiguration.class.getDeclaredField("cachedBoundsAerodynamic");
|
||||
cachedBoundsAerodynamicField.setAccessible(true);
|
||||
BoundingBox cachedBoundsAerodynamicOriginal = (BoundingBox) cachedBoundsAerodynamicField.get(original);
|
||||
BoundingBox cachedBoundsAerodynamicCopy = (BoundingBox) cachedBoundsAerodynamicField.get(copy);
|
||||
assertEquals(cachedBoundsAerodynamicOriginal, cachedBoundsAerodynamicCopy);
|
||||
assertNotSame(cachedBoundsAerodynamicOriginal, cachedBoundsAerodynamicCopy);
|
||||
|
||||
// Test cachedBounds copy
|
||||
Field cachedBoundsField = FlightConfiguration.class.getDeclaredField("cachedBounds");
|
||||
cachedBoundsField.setAccessible(true);
|
||||
BoundingBox cachedBoundsOriginal = (BoundingBox) cachedBoundsField.get(original);
|
||||
BoundingBox cachedBoundsCopy = (BoundingBox) cachedBoundsField.get(copy);
|
||||
assertEquals(cachedBoundsOriginal, cachedBoundsCopy);
|
||||
assertNotSame(cachedBoundsOriginal, cachedBoundsCopy);
|
||||
|
||||
// Test modID copy
|
||||
assertEquals(original.getModID(), copy.getModID());
|
||||
|
||||
// Test boundModID
|
||||
Field boundsModIDField = FlightConfiguration.class.getDeclaredField("boundsModID");
|
||||
boundsModIDField.setAccessible(true);
|
||||
int boundsModIDCopy = (int) boundsModIDField.get(copy);
|
||||
assertEquals(-1, boundsModIDCopy);
|
||||
|
||||
// Test refLengthModID
|
||||
Field refLengthModIDField = FlightConfiguration.class.getDeclaredField("refLengthModID");
|
||||
refLengthModIDField.setAccessible(true);
|
||||
int refLengthModIDCopy = (int) refLengthModIDField.get(copy);
|
||||
assertEquals(-1, refLengthModIDCopy);
|
||||
|
||||
// Test stageActiveness copy
|
||||
for (int i = 0; i < original.getStageCount(); i++) {
|
||||
assertEquals(original.isStageActive(i), copy.isStageActive(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClone() throws NoSuchFieldException, IllegalAccessException {
|
||||
Rocket rocket = TestRockets.makeFalcon9Heavy();
|
||||
FlightConfiguration original = rocket.getSelectedConfiguration();
|
||||
original.setOnlyStage(0);
|
||||
|
||||
// vvvv Test Target vvvv
|
||||
FlightConfiguration clone = original.clone();
|
||||
// ^^^^ Test Target ^^^^
|
||||
|
||||
assertEquals(original, clone);
|
||||
assertNotSame(original, clone);
|
||||
assertEquals(original.getName(), clone.getName());
|
||||
assertEquals(original.getFlightConfigurationID(), clone.getFlightConfigurationID());
|
||||
|
||||
// Test preloadStageActiveness clone
|
||||
Field preloadStageActivenessField = FlightConfiguration.class.getDeclaredField("preloadStageActiveness");
|
||||
preloadStageActivenessField.setAccessible(true);
|
||||
Map<Integer, Boolean> preloadStageActivenessOriginal = (Map<Integer, Boolean>) preloadStageActivenessField.get(original);
|
||||
Map<Integer, Boolean> preloadStageActivenessClone = (Map<Integer, Boolean>) preloadStageActivenessField.get(clone);
|
||||
assertEquals(preloadStageActivenessOriginal, preloadStageActivenessClone);
|
||||
if (preloadStageActivenessOriginal == null) {
|
||||
assertNull(preloadStageActivenessClone);
|
||||
} else {
|
||||
assertNotSame(preloadStageActivenessOriginal, preloadStageActivenessClone);
|
||||
}
|
||||
|
||||
// Test cachedBoundsAerodynamic clone
|
||||
Field cachedBoundsAerodynamicField = FlightConfiguration.class.getDeclaredField("cachedBoundsAerodynamic");
|
||||
cachedBoundsAerodynamicField.setAccessible(true);
|
||||
BoundingBox cachedBoundsAerodynamicOriginal = (BoundingBox) cachedBoundsAerodynamicField.get(original);
|
||||
BoundingBox cachedBoundsAerodynamicClone = (BoundingBox) cachedBoundsAerodynamicField.get(clone);
|
||||
assertEquals(cachedBoundsAerodynamicOriginal, cachedBoundsAerodynamicClone);
|
||||
assertNotSame(cachedBoundsAerodynamicOriginal, cachedBoundsAerodynamicClone);
|
||||
|
||||
// Test cachedBounds clone
|
||||
Field cachedBoundsField = FlightConfiguration.class.getDeclaredField("cachedBounds");
|
||||
cachedBoundsField.setAccessible(true);
|
||||
BoundingBox cachedBoundsOriginal = (BoundingBox) cachedBoundsField.get(original);
|
||||
BoundingBox cachedBoundsClone = (BoundingBox) cachedBoundsField.get(clone);
|
||||
assertEquals(cachedBoundsOriginal, cachedBoundsClone);
|
||||
assertNotSame(cachedBoundsOriginal, cachedBoundsClone);
|
||||
|
||||
// Test modID clone
|
||||
assertEquals(original.getModID(), clone.getModID());
|
||||
|
||||
// Test boundModID
|
||||
Field boundsModIDField = FlightConfiguration.class.getDeclaredField("boundsModID");
|
||||
boundsModIDField.setAccessible(true);
|
||||
int boundsModIDClone = (int) boundsModIDField.get(clone);
|
||||
assertEquals(-1, boundsModIDClone);
|
||||
|
||||
// Test refLengthModID
|
||||
Field refLengthModIDField = FlightConfiguration.class.getDeclaredField("refLengthModID");
|
||||
refLengthModIDField.setAccessible(true);
|
||||
int refLengthModIDClone = (int) refLengthModIDField.get(clone);
|
||||
assertEquals(-1, refLengthModIDClone);
|
||||
|
||||
// Test stageActiveness copy
|
||||
for (int i = 0; i < original.getStageCount(); i++) {
|
||||
assertEquals(original.isStageActive(i), clone.isStageActive(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -146,7 +146,25 @@
|
||||
<Mass Unit="g">1.355</Mass>
|
||||
<ScrewMass Unit="g">2.720</ScrewMass>
|
||||
<NutMass Unit="g"></NutMass>
|
||||
</RailButton>
|
||||
</RailButton>
|
||||
|
||||
<RailButton>
|
||||
<Manufacturer>Wildman Rocketry</Manufacturer>
|
||||
<PartNumber>2052-LG</PartNumber>
|
||||
<Description>1 Piece 1515 Rail Button, Countersunk 10-32 Screw, and T-Nut</Description>
|
||||
<Material Type="BULK">Delrin</Material>
|
||||
<Finish>Polished</Finish>
|
||||
<OuterDiameter Unit="in">0.621</OuterDiameter>
|
||||
<InnerDiameter Unit="in">0.308</InnerDiameter>
|
||||
<Height Unit="in">0.6845</Height>
|
||||
<BaseHeight Unit="in">0.338</BaseHeight>
|
||||
<FlangeHeight Unit="in">0.1675</FlangeHeight>
|
||||
<ScrewHeight Unit="in">0.0000</ScrewHeight>
|
||||
<DragCoefficient></DragCoefficient>
|
||||
<Mass Unit="g">3.215</Mass>
|
||||
<ScrewMass Unit="g">3.795</ScrewMass>
|
||||
<NutMass Unit="g">2.195</NutMass>
|
||||
</RailButton>
|
||||
|
||||
</Components>
|
||||
|
||||
|
Binary file not shown.
BIN
swing/resources/datafiles/examples/Simulation Extension.ork
Normal file
BIN
swing/resources/datafiles/examples/Simulation Extension.ork
Normal file
Binary file not shown.
@ -56,6 +56,7 @@ public class AboutDialog extends JDialog {
|
||||
"Vladimir Beran (Czech)<br>" +
|
||||
"Polish Rocketry Society / \u0141ukasz & Alex Kazanski (Polish)<br>" +
|
||||
"Sibo Van Gool (Dutch)<br>" +
|
||||
"Mohamed Amin Elkebsi (Arabic)<br>" +
|
||||
"<br>" +
|
||||
"See all contributors at <br>" + href("https://github.com/openrocket/openrocket/graphs/contributors", false, false) + "<br>" +
|
||||
"<br>" +
|
||||
|
@ -496,17 +496,14 @@ public class RocketFigure extends AbstractScaleFigure {
|
||||
protected void updateSubjectDimensions() {
|
||||
// calculate bounds, and store in class variables
|
||||
|
||||
final FlightConfiguration config = rocket.getSelectedConfiguration().clone();
|
||||
// Explicitly zoom & draw at a scale to fit the entire rocket, but only show the selected stages.
|
||||
config.setAllStages();
|
||||
final BoundingBox newBounds = config.getBoundingBox();
|
||||
final BoundingBox bounds = rocket.getSelectedConfiguration().getBoundingBox();
|
||||
|
||||
final double maxR = Math.max( Math.hypot(newBounds.min.y, newBounds.min.z),
|
||||
Math.hypot(newBounds.max.y, newBounds.max.z));
|
||||
final double maxR = Math.max( Math.hypot(bounds.min.y, bounds.min.z),
|
||||
Math.hypot(bounds.max.y, bounds.max.z));
|
||||
|
||||
switch (currentViewType) {
|
||||
case SideView:
|
||||
subjectBounds_m = new Rectangle2D.Double(newBounds.min.x, -maxR, newBounds.span().x, 2 * maxR);
|
||||
subjectBounds_m = new Rectangle2D.Double(bounds.min.x, -maxR, bounds.span().x, 2 * maxR);
|
||||
break;
|
||||
case BackView:
|
||||
subjectBounds_m = new Rectangle2D.Double(-maxR, -maxR, 2 * maxR, 2 * maxR);
|
||||
|
@ -197,12 +197,12 @@ class SimulationOptionsPanel extends JPanel {
|
||||
|
||||
|
||||
final JButton addExtension = new SelectColorButton(trans.get("simedtdlg.SimExt.add"));
|
||||
this.extensionMenu = getExtensionMenu();
|
||||
extensionMenu = getExtensionMenu();
|
||||
addExtension.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
extensionMenu.show(addExtension, 5, addExtension.getBounds().height);
|
||||
}
|
||||
});
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
extensionMenu.show(addExtension, 5, addExtension.getBounds().height);
|
||||
}
|
||||
});
|
||||
sub.add(addExtension, "growx, wrap 0");
|
||||
|
||||
currentExtensions = new JPanel(new MigLayout("fillx, gap 0 0, ins 0"));
|
||||
|
@ -43,7 +43,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
||||
private static final List<Locale> SUPPORTED_LOCALES;
|
||||
static {
|
||||
List<Locale> list = new ArrayList<Locale>();
|
||||
for (String lang : new String[] { "en", "de", "es", "fr", "it", "nl", "ru", "cs", "pl", "ja", "pt", "tr" }) {
|
||||
for (String lang : new String[] { "en", "ar", "de", "es", "fr", "it", "nl", "ru", "cs", "pl", "ja", "pt", "tr" }) {
|
||||
list.add(new Locale(lang));
|
||||
}
|
||||
list.add(new Locale("zh", "CN"));
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.sf.openrocket.simulation.extension.impl;
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
@ -0,0 +1,83 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.gui.SpinnerEditor;
|
||||
import net.sf.openrocket.gui.adaptors.DoubleModel;
|
||||
import net.sf.openrocket.gui.components.BasicSlider;
|
||||
import net.sf.openrocket.gui.components.UnitSelector;
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
@Plugin
|
||||
public class RollControlConfigurator extends AbstractSwingSimulationExtensionConfigurator<RollControl> {
|
||||
private JPanel panel;
|
||||
private RollControl extension;
|
||||
|
||||
public RollControlConfigurator() {
|
||||
super(RollControl.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent getConfigurationComponent(RollControl extension, Simulation simulation, JPanel panel) {
|
||||
this.panel = panel;
|
||||
this.extension = extension;
|
||||
panel.add(new JLabel("Control FinSet Name:"));
|
||||
|
||||
JTextField finSetName = new JTextField();
|
||||
finSetName.setText(extension.getControlFinName());
|
||||
|
||||
finSetName.addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
extension.setControlFinName(finSetName.getText());
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(finSetName, "growx, span3, wrap");
|
||||
|
||||
addRow("Start Time", "StartTime", UnitGroup.UNITS_SHORT_TIME, 0.0, 1200.0);
|
||||
addRow("Desired Roll Rate", "SetPoint", UnitGroup.UNITS_ROLL, -10.0, 10.0);
|
||||
addRow("Max Fin Turn Rate", "FinRate", UnitGroup.UNITS_ROLL, 0, 10);
|
||||
addRow("Max Fin Angle", "MaxFinAngle", UnitGroup.UNITS_ANGLE, 0, 0.25);
|
||||
|
||||
panel.add(new JLabel("PID parameters:"), "newline 0.25in, span4, wrap");
|
||||
panel.add(new JLabel("At M=0.3 KP oscillation threshold between 0.35 and 0.4. Good KI=3" ), "gapbefore indent, span4, wrap");
|
||||
panel.add(new JLabel("At M=0.6 KP oscillation threshold between 0.07 and 0.08 Good KI=2" ), "gapbefore indent, span4, wrap");
|
||||
panel.add(new JLabel("At M=0.9 KP oscillation threshold between 0.013 and 0.014 Good KI=0.5"), "gapbefore indent, span4, wrap");
|
||||
|
||||
addRow("KP (Proportional)", "KP", UnitGroup.UNITS_COEFFICIENT, 0.0, 0.02);
|
||||
addRow("KI (Integrated)", "KI", UnitGroup.UNITS_COEFFICIENT, 0.0, 1.0);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void addRow(String prompt, String fieldName, UnitGroup units, double min, double max) {
|
||||
panel.add(new JLabel(prompt + ":"));
|
||||
|
||||
DoubleModel m = new DoubleModel(extension, fieldName, units, min, max);
|
||||
|
||||
JSpinner spin = new JSpinner(m.getSpinnerModel());
|
||||
spin.setEditor(new SpinnerEditor(spin));
|
||||
panel.add(spin, "w 65lp!");
|
||||
|
||||
UnitSelector unit = new UnitSelector(m);
|
||||
panel.add(unit, "w 25");
|
||||
|
||||
BasicSlider slider = new BasicSlider(m.getSliderModel(0, 1000));
|
||||
panel.add(slider, "w 75lp, wrap");
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package net.sf.openrocket.simulation.extension.example;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
|
||||
import net.sf.openrocket.document.Simulation;
|
||||
import net.sf.openrocket.gui.SpinnerEditor;
|
||||
import net.sf.openrocket.gui.adaptors.IntegerModel;
|
||||
import net.sf.openrocket.gui.components.BasicSlider;
|
||||
import net.sf.openrocket.plugin.Plugin;
|
||||
import net.sf.openrocket.simulation.extension.AbstractSwingSimulationExtensionConfigurator;
|
||||
import net.sf.openrocket.unit.UnitGroup;
|
||||
|
||||
@Plugin
|
||||
public class StopSimulationConfigurator extends AbstractSwingSimulationExtensionConfigurator<StopSimulation> {
|
||||
private JPanel panel;
|
||||
private StopSimulation extension;
|
||||
|
||||
public StopSimulationConfigurator() {
|
||||
super(StopSimulation.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent getConfigurationComponent(StopSimulation extension, Simulation simulation, JPanel panel) {
|
||||
this.panel = panel;
|
||||
this.extension = extension;
|
||||
|
||||
addRow("Report Rate", "ReportRate", 0, 1000);
|
||||
addRow("Stop Step", "StopStep", 0, 50000);
|
||||
addRow("Stop Time", "StopTime", 0, 1000);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void addRow(String prompt, String fieldName, int min, int max) {
|
||||
panel.add(new JLabel(prompt + ":"));
|
||||
|
||||
IntegerModel m = new IntegerModel(extension, fieldName, min, max);
|
||||
|
||||
JSpinner spin = new JSpinner(m.getSpinnerModel());
|
||||
spin.setEditor(new SpinnerEditor(spin));
|
||||
panel.add(spin, "w 65lp!");
|
||||
|
||||
BasicSlider slider = new BasicSlider(m.getSliderModel());
|
||||
panel.add(slider, "w 75lp, wrap");
|
||||
}
|
||||
}
|
@ -123,6 +123,7 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi
|
||||
@Override
|
||||
protected void close() {
|
||||
util.setTrustedScript(extension.getLanguage(), extension.getScript(), trusted.isSelected());
|
||||
super.close();
|
||||
}
|
||||
|
||||
private void setLanguage(String language) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user